Из вроде бы абстракного репозитория торчат какие-то join-ы и load-ы, т.е. детали того, что вы используете РСУБД и конкретно SQLAlchemy ORM (так как варианты в LazyLoadType используются именно оттуда). В FilterType присутствует какой-то VECTOR_SORT, который, подозреваю, тоже существует не во всех СУБД.
Redis не обязательно используется именно как кэш, и я его привел просто в качестве примера другого типа хранилища.
Может быть я не до конца понял задумку, и у вас не было цели полностью абстрагироваться от деталей используемого хранилища, но тогда, как справедливо заметил @whoisking, зачем создавать query builder вокруг уже предоставленного в SQLAlchemy. Вы же приводите пример решения для "сложных" запросов, а таких, как правило, немного, и они не слишком пересекаются в реализации.
В целом очень хорошая демонстрация, как НЕ нужно реализовывать ORM:
ActiveRecord довольно проблемная вещь, но даже закрывая глаза на это:
Первичных ключей нет, как ваш INSERT OR REPLACE поймет, что заменять? Точнее он-то поймет по UNIQUE, но у вас в примерах он нигде не указан. А если будет несколько полей с UNIQUE?
Внешние ключи реализованы через JSON, ломая весь смысл проверки целостности. Ваш же пример с удалением Box ломает данные.
Из-за этого же гарантированный N+1 на любой запрос.
filter сразу же выполняет запрос, последующие фильтры выполняются на стороне ORM. То есть Foo.objects.filter(a=1).filter(b=2) сначала прочитает лишние записи (еще помним про N+1), съест RAM, потому будет тратить такты CPU на фильтрацию.
filter поддерживает только сравнение на =, а как быть с другими операторами?
На каждый запрос делается новое подключение к БД.
... тут можно еще писать и писать ...
Если это учебный материал, то ИМХО стоит в нем показывать как делать правильно, а не писать код ради "использования много сложных инструментов языка python, которые помогли написать короткий и красивый код, решающий довольно сложную задачу", тем более что код не слишком "короткий и красивый" и задачу решает, мягко говоря, так себе.
Если задача была продемонстрировать, как устроены другие ORM, то с уверенностью могу заявить, что ни одна популярная ТАК не устроена.
Предположим, что я хочу достать всех пользователей, и по каждому список профилей, которые он редактировал. Если я сделаю join в лоб, то данные из таблицы пользователей продублируются на каждый профиль.
У меня два варианта — агрегировать профили в запросе в какой-нибудь json (но это уже сложнее "банального джойна"), или тащить результат как есть в приложение и убирать дубликаты там.
join будет оптимальнее, если связь 1 к 1, а если на одну строку приходится 100 связанных, то придется по сети гонять дублированные данные, и тут же их отбросывать в приложении.
Не все в мире CRUD. Почему обязательно из БД? Почему обязательно из реляционной?
Frontend ведь не только и не столько про отображение данных. Это интерфейс между пользователем и машиной на той стороне провода. Через него реализуются механизмы, задуманные дизайнером и UX, сценарии использования, психология и т.д. Красивое отображение табличек тут просто один из компонентов.
Для получения нескольких значений можно использовать асинхронные генераторы.
Тут же суть скорее в том, что корутины не обладают тем самым свойством реактивности. Они не способны реагировать на поступающие события, а могут только активно запрашивать данные из источника. Чтобы поверх них реализовать реактивность, нужна отдельная блокирующая очередь сообщений, но даже в таком случае логика обработки данных будет инвертированной.
А для продвинутых продолжение планируется? Потому что как правило различные сложности и тонкости всплывают тогда, когда приложение становится больше, а туториалов, где бы эти моменты освещались просто нет, зато с десяток разных статей про счетчики и «угадай число».
Из вроде бы абстракного репозитория торчат какие-то join-ы и load-ы, т.е. детали того, что вы используете РСУБД и конкретно SQLAlchemy ORM (так как варианты в
LazyLoadTypeиспользуются именно оттуда). ВFilterTypeприсутствует какой-тоVECTOR_SORT, который, подозреваю, тоже существует не во всех СУБД.Redis не обязательно используется именно как кэш, и я его привел просто в качестве примера другого типа хранилища.
Может быть я не до конца понял задумку, и у вас не было цели полностью абстрагироваться от деталей используемого хранилища, но тогда, как справедливо заметил @whoisking, зачем создавать query builder вокруг уже предоставленного в SQLAlchemy. Вы же приводите пример решения для "сложных" запросов, а таких, как правило, немного, и они не слишком пересекаются в реализации.
В итоге детали реализации протекли за пределы репозитория. Как бы теперь выглядела реализация для Mongo или Redis?
Python и так использует Карацубу для больших чисел.
То что специфику учитывать нужно спору нет, но я процитирую автора:
и вот тут с большими СУБД будут проблемы. Не хочется, чтобы новички переносили такой опыт на серьезные проекты.
В целом очень хорошая демонстрация, как НЕ нужно реализовывать ORM:
ActiveRecord довольно проблемная вещь, но даже закрывая глаза на это:
Первичных ключей нет, как ваш
INSERT OR REPLACEпоймет, что заменять? Точнее он-то поймет поUNIQUE, но у вас в примерах он нигде не указан. А если будет несколько полей сUNIQUE?Внешние ключи реализованы через JSON, ломая весь смысл проверки целостности. Ваш же пример с удалением
Boxломает данные.Из-за этого же гарантированный N+1 на любой запрос.
filterсразу же выполняет запрос, последующие фильтры выполняются на стороне ORM. То естьFoo.objects.filter(a=1).filter(b=2)сначала прочитает лишние записи (еще помним про N+1), съест RAM, потому будет тратить такты CPU на фильтрацию.filterподдерживает только сравнение на=, а как быть с другими операторами?На каждый запрос делается новое подключение к БД.
... тут можно еще писать и писать ...
Если это учебный материал, то ИМХО стоит в нем показывать как делать правильно, а не писать код ради "использования много сложных инструментов языка python, которые помогли написать короткий и красивый код, решающий довольно сложную задачу", тем более что код не слишком "короткий и красивый" и задачу решает, мягко говоря, так себе.
Если задача была продемонстрировать, как устроены другие ORM, то с уверенностью могу заявить, что ни одна популярная ТАК не устроена.
На самом деле не все: при увеличении ширины в 2 раза, площадь прямоугольника увеличится в 2 раза, а квадрата — в 4.
Предположим, что я хочу достать всех пользователей, и по каждому список профилей, которые он редактировал. Если я сделаю join в лоб, то данные из таблицы пользователей продублируются на каждый профиль.
У меня два варианта — агрегировать профили в запросе в какой-нибудь json (но это уже сложнее "банального джойна"), или тащить результат как есть в приложение и убирать дубликаты там.
join будет оптимальнее, если связь 1 к 1, а если на одну строку приходится 100 связанных, то придется по сети гонять дублированные данные, и тут же их отбросывать в приложении.
Есть aiofile, заявляется нативная асинхронная работа с файлами в Linux
Для Python рекомендую обратить внимание на FastAPI.
Frontend ведь не только и не столько про отображение данных. Это интерфейс между пользователем и машиной на той стороне провода. Через него реализуются механизмы, задуманные дизайнером и UX, сценарии использования, психология и т.д. Красивое отображение табличек тут просто один из компонентов.
Тут же суть скорее в том, что корутины не обладают тем самым свойством реактивности. Они не способны реагировать на поступающие события, а могут только активно запрашивать данные из источника. Чтобы поверх них реализовать реактивность, нужна отдельная блокирующая очередь сообщений, но даже в таком случае логика обработки данных будет инвертированной.
Иммутабельность тут ни при чем. Все передается по ссылке.