2015-07-22 2 views
1

У меня есть следующий объект:подкачка значения в OneToOneField: IntegrityError

from django.db import models 

class Page(models.Model): 
    prev_sibling = models.OneToOneField('self', related_name='next_sibling', 
             null=True, blank=True) 

У меня есть несколько экземпляров этих объектов. Скажем, A, B, C и D. Я назначил отношения брата, так что A имеет B как prev_sibling, а C имеет D как prev_sibling.

Теперь представьте, что я хочу обменять B и D вокруг. Я просто повторно назначить атрибуты, следующим образом:

A.prev_sibling = D 
A.save() 
C.prev_sibling = B 
C.save() 

Однако это не удается с IntegrityError, как я уже не удовлетворяет ограничение уникальности, которое подразумевается по OneToOneField после первого save().

Я попробовал оборачивать код в сделке, в надежде, что это будет убедиться, что сохранение будет происходить атомарно, и, таким образом, предотвратить временное ограничение нарушения, например, так:

from django.db import transaction 

with transaction.atomic(): 
    A.prev_sibling = D 
    A.save() 
    C.prev_sibling = B 
    C.save() 

Но это не сработало. Каков правильный способ решить эту проблему?

EDIT: Для записи: Я также попытался последовательно назначать, а затем сохранять (как указано ниже), но, как можно было бы ожидать, поведение ничем не отличается.

with transaction.atomic(): 
    A.prev_sibling = D 
    C.prev_sibling = B 
    A.save() 
    C.save() 
+0

Вы пробовали с [совершить параметров в 'save' метод] (https://docs.djangoproject.com/en/stable/topics/forms/modelforms/#the-save-method)? – Gocht

+0

Я не совсем понимаю, как это уместно. Метод 'save', который поставляется с формами, похоже, имеет этот параметр, а не' save() 'на моделях, и это, похоже, не затрагивает эту проблему. – Joost

+0

Извините, транзакции имеют функцию [commit] (https://docs.djangoproject.com/en/stable/topics/db/transactions/#django.db.transaction.commit). – Gocht

ответ

0

Вы нарушаете ограничения базы данных. Перед тем, как сохранить его, необходимо очистить его, чтобы база данных всегда находилась в «правильном» состоянии. Конечно, после того, как вы установили их в None, это не технически желаемое состояние, но оно является допустимым для ограничений базы данных.

Это должно сработать для вас, но вы, очевидно, рискуете потерять отношения, если произойдет что-то плохое, чтобы сохранить их как None и установить свойства.

Page.objects.filter(id__in=[A.id, C.id]).update(prev_sibling=None) 
A.prev_sibling = D 
A.save() 
C.prev_sibling = B 
C.save() 
+0

Хм .. Я боялся, что это будет единственный способ, и я надеялся избежать этого. Думаю, мне придется принять это, потому что ничего лучше. – Joost

+0

Возможно, вы сможете сделать это за один раз, используя '' 'raw'''. – schillingt

+0

Hm .. это означало бы динамическое построение запроса, учитывая, что ситуация, о которой я делаю выше, немного упрощена, а количество объектов не фиксировано. Это то, что я хотел бы оставить в ORM. Я перейду к настройке в None, а затем сохраню. – Joost

Смежные вопросы