Джанго условно создать - PullRequest
0 голосов
/ 05 июня 2018

Предоставляет ли Django ORM способ условного создания объекта?

Например, допустим, вы хотите использовать какой-то оптимистичный контроль параллелизма для вставки новых объектов.
В определенный моментВы знаете, что последний объект должен быть вставлен в эту таблицу, и вы хотите создавать новый объект только в том случае, если с тех пор новые объекты не были вставлены.

Если это обновление, вы можете фильтровать на основе ревизии.номер:

updated = Account.objects.filter(
    id=self.id,
    version=self.version,
).update(
    balance=balance + amount,
    version=self.version + 1,
)

Однако я не могу найти никакого документированного способа предоставить условия для вызова create() или save().

Я ищу что-то, что применило бы эти условия на уровне запросов SQL, чтобы избежать проблем «чтения-изменения-записи».

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

РЕДАКТИРОВАТЬ: Это не попытка Optimistic Lock.Это прямой ответ на предоставленный код OP.


Django предлагает способ реализации условных запросов .Он также предлагает ярлык update_or_create(defaults=None, **kwargs), который:

Метод update_or_create пытается извлечь объект из базы данных на основе заданного kwargs.Если совпадение найдено, оно обновляет поля, переданные в словаре по умолчанию.

Значения в значениях по умолчанию могут быть вызваны .

Поэтому мы можем попытатьсясмешайте и сопоставьте эти два, чтобы воссоздать предоставленный запрос:

obj, created = Account.objects.update_or_create(
    id=self.id,
    version=self.version,
    defaults={
        balance: Case(
            When(version=self.version, then=F('balance')+amount),
            default=amount
        ),
        version: Case(
            When(version=self.version, then=F('version')+1),
            default=self.version
        )
    }
)

Разбивка запроса:

update_or_create попытается получитьобъект с id=self.id и version=self.version в базе данных.

  • Найдено: Поля balance и version объекта будут обновлены со значениями внутри Case условные выражения соответственно (см. Следующий раздел ответа).
  • Не найдено: Будет создан объект с id=self.id и version=self.version, а затем он получит свой *Обновлены поля 1049 * и version.

Разбивка условных запросов:

  1. balance Запрос:

    • Если объект существует, условие выражения When будет истинным, поэтому поле balance будетОбновится со значением:

      # Existing balance       # Added amount
         F('balance')      +        amount
      
    • Если объект будет создан, он получит в качестве начального balance значение amount.

  2. version Запрос:

    • Если объект существует, условие выражения When будет истинным,поэтому поле version будет обновлено со значением:

      # Existing version        # Next Version
         F('version')      +           1
      
    • Если объект будет создан, он получит в качестве начального version значение self.version (это также может быть исходная версия по умолчанию, например 1.0.0).


Примечания:

0 голосов
/ 12 июня 2018

За исключением QuerySet.update, возвращающего количество затронутых строк, Django не предоставляет никаких примитивов для работы с оптимистической блокировкой.

Однако есть несколько сторонних приложений, которые предоставляют такую ​​функцию.

  1. django-concurrency, который является наиболее популярной опцией, которая обеспечивает как ограничения уровня базы данных, так и приложения
  2. django-optimistic-lock, который являетсянемного менее популярным, но я пробовал в прошлом проекте, и он работал просто отлично.
  3. django-locking не поддерживается.

Редактировать: Похоже,В конце концов, у OP не было оптимистичных решений по блокировке.

...