2016-10-31 11 views
1

Как я могу с помощью атомарного сравнения-обменять значение Django Model экземпляр Field? (Использование PostgreSQL в качестве базы данных БД).Атомно-сравнить поле модели в Django

Примером использования является то, что несколько сообщений с похожим контентом (например, отправления одной и той же формы) вступают в силу только один раз, не полагаясь на небезопасное и только иногда работающее на стороне клиента javascript или серверное отслеживание формы UUIDs , который не защищен от вредоносных множественных сообщений.

Например:

def compare_exchange_save(model_object, field_name, comp, exch): 
    # How to implement? 
    .... 


from django.views.generic.edit import FormView  
from django.db import transaction 
from my_app.models import LicenseCode 

class LicenseCodeFormView(FormView): 
    def post(self, request, ...): 

     # Get object matching code entered in form 
     license_code = LicenseCode.objects.get(...) 

     # Safely redeem the code exactly once 
     # No change is made in case of error 
     try: 
      with transaction.atomic() 
       if compare_exchange_save(license_code, 'was_redeemed', False, True): 
        # Deposit a license for the user with a 3rd party service. Raises an exception if it fails. 
        ... 
       else: 
        # License code already redeemed, don't deposit another license. 
        pass 
     except: 
      # Handle exception 
      ... 

ответ

1

Что вы ищете является update функция на объекте QuerySet.

В зависимости от значения, вы можете сделать сравнение с случаем, когда объекты - проверить документы на conditional updatesПРИМЕЧАНИЯ, что ссылка на 1,10 - чехле/Когда пришел в 1.8.

Вы также можете найти полезное сообщение при использовании F, которое используется для ссылки на значение в поле.

Например:

мне нужно обновить значение в моей модели Модель:

(Model.objects 
.filter(id=my_id) 
.update(field_to_be_updated=Case(
     When(my_field=True, then=Value(get_new_license_string()), 
     default=Value(''), 
     output_field=models.CharField()))) 

Если вам нужно использовать F объект, просто ссылаться на него на правой стороне равенства в выражение обновления.

Обновление не требует использования менеджера transaction.atomic() контекста, но если вам нужно сделать какие-либо другие операции с базой данных, вы должны продолжать обернуть этот код с transaction.atomic()

Edit:

Вы также можете, как используйте метод queryset select_for_update, который реализует блокировки строк при выполнении запроса docs.

+0

Гарантируется ли это, что это атомный? Я не вижу этого в документах. Если это не так, то в моем случае использования для двух разных запросов можно ввести раздел «Депозит лицензии» – Danra

+0

Это единый вызов базы данных. Это настолько же атомно, насколько это возможно. –

+0

@ DanielRoseman, который является атомарным или нет? То есть, предполагая бэкэнд PostgreSQL, возможно ли для двух одновременных запросов одновременно вызвать этот метод, чтобы оба успешно обновили значение? Или это гарантирует, что в лучшем случае это будет успешным? – Danra