В целом очень хорошая демонстрация, как НЕ нужно реализовывать 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, сценарии использования, психология и т.д. Красивое отображение табличек тут просто один из компонентов.
Для получения нескольких значений можно использовать асинхронные генераторы.
Тут же суть скорее в том, что корутины не обладают тем самым свойством реактивности. Они не способны реагировать на поступающие события, а могут только активно запрашивать данные из источника. Чтобы поверх них реализовать реактивность, нужна отдельная блокирующая очередь сообщений, но даже в таком случае логика обработки данных будет инвертированной.
А для продвинутых продолжение планируется? Потому что как правило различные сложности и тонкости всплывают тогда, когда приложение становится больше, а туториалов, где бы эти моменты освещались просто нет, зато с десяток разных статей про счетчики и «угадай число».
Майерс говорил, что надо писать type-agnostic код, т.е. так, чтобы не зная ничего о типах данных, я мог понять, что он делает, и какой контракт выполняет. Идентификаторы должны точно и однозначно описывать семантику своего значения. Если соблюдать это правило, то никакие иные соглашения будут не нужны.
То, что вы приводите в пример — это, извините, совершенно не читаемая и непонятная криптография. Если мне информация о типах не нужна, то все эти префиксы создают только визуальный шум. А если бы она мне понадобилась, то я бы посмотрел на сигнатуру функции или на объявление переменной, потому что это быстрее, чем расшифровывать очередной vnaRes.
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, сценарии использования, психология и т.д. Красивое отображение табличек тут просто один из компонентов.
Тут же суть скорее в том, что корутины не обладают тем самым свойством реактивности. Они не способны реагировать на поступающие события, а могут только активно запрашивать данные из источника. Чтобы поверх них реализовать реактивность, нужна отдельная блокирующая очередь сообщений, но даже в таком случае логика обработки данных будет инвертированной.
Иммутабельность тут ни при чем. Все передается по ссылке.
То, что вы приводите в пример — это, извините, совершенно не читаемая и непонятная криптография. Если мне информация о типах не нужна, то все эти префиксы создают только визуальный шум. А если бы она мне понадобилась, то я бы посмотрел на сигнатуру функции или на объявление переменной, потому что это быстрее, чем расшифровывать очередной vnaRes.