extra()
Иногда синтаксис запроса Django сам по себе не может легко выразить сложное предложение WHERE. Для этого был создан метод extra().
Employee.objects.extra(where=["first_name=%s"], params=["John"])
Employee.objects.extra(where=["last_name='Doe' OR first_name = 'John'"])
Что будет приравниваться к запросу:
SELECT * FROM hr_employee WHERE last_name='Doe' OR first_name = 'John'
Более подробнее можно почитать в документации - https://docs.djangoproject.com/en/4.2/ref/models/querysets/#extra
defer()
Если в вашей модели Django есть поля, содержащие много данных, и вам не нужны эти поля для определенного запроса, вы можете указать Django не извлекать их с помощью метода defer(). Попробуем вывести первого работника.
Мы видим что у него есть поле 'created'. Попробуем извлечь первого работника используя метод defer чтобы не выводить их дату создания:
Employee.objects.defer('created')
То как мы видим, данное поле не было выведено.
only()
Является противоположностью метода defer() - возвращает только поля, предоставленные в качестве аргументов:
Employee.objects.only('first_name')
using()
Если вы используете несколько баз данных, этот метод используется для управления QuerySet, какая база данных будет оцениваться на. Уникальный параметр этого метода является псевдонимом базы данных, определенным в базах данных.
# запрашивает базу данных 'default'.
>>> Entry.objects.all()
# запрашивает базу данных 'backup'
>>> Entry.objects.using('backup')
В settings.py проекта мы можем задать несколько баз данных и использовать их в зависимости от потребностей через данный метод.
select_for_update()
Допустим, есть учетная запись с балансом в 1000 долларов, доступ к которой имеют 2 разных пользователя. Думайте об этом как о совместном счете. Теперь предположим, что Пользователь 1 зарабатывает, а Пользователь 2 тратит. Пользователь 1 внес 100 долларов на счет, и поэтому сервер вызвал account.deposit(100), но в то же время пользователь 2 снял 100 долларов, вызвав таким образом account.withdraw(100).
Что должно произойти в этом случае? В идеале баланс в конце этих двух транзакций должен оставаться 1000$, верно? Если вы используете один экземпляр вашего сервера, это действительно так, потому что эти две транзакции всегда будут выполняться одна за другой. Но если эти транзакции выполняются разными экземплярами вашего сервера параллельно, есть большая вероятность, что баланс в конце составит 900 долларов. Почему это происходит?
Вот шаги, которые происходят в этих транзакциях
- Пользователь 1 извлекает запись о состоянии счета(Баланс 1000$)
- Пользователь 2 извлекает запись о состоянии счета(Баланс 1000$)
- Пользователь 1 вносит 100 долларов (Баланс 1000 = 1100$)
- Пользователь 2 снимает 100 долларов (Баланс 1000 = 900$)
На шаге 4 баланс, который сервер загрузил в память, устарел, потому что он уже был обновлен до 1100 долларов на шаге 3, о чем другой экземпляр сервера не знал, и поэтому он считает, что текущий баланс все еще составляет 1000 долларов.
Решение довольно простое. Когда выполняется операция с базой данных, объект или набор объектов, которые обновляются, должны быть заблокированы до завершения операции, чтобы ни один другой процесс не мог получить доступ к этому объекту/объектам.
Метод select_for_update, предлагаемый Django ORM, решает проблему параллелизма, возвращая набор запросов, который блокирует все строки, принадлежащие этому набору запросов, до тех пор, пока самая внешняя транзакция, в которой он находится, не будет закончена, что предотвращает повреждение данных.
Employee.objects.select_for_update().get(id=1)