2012-02-03 1 views
0

Ниже упрощен пример того, что я пытаюсь достичь:вручную сохранение ModelForm относительные внешние ключи

class Product(models.Model): 
    # some data, does not really matter 

class ProductAttributeValue(models.Model): 
    product = models.ForeignKey('Product') 

    value=models.CharField(_("value"),max_length=100) 

....

class ProductForm(forms.ModelForm): 

    def __init__(self,*args, **kwargs): 
     super(ProductForm, self).__init__(*args, **kwargs) 

     #Here, I am dynamically constructing and injecting attributes. 
     #my products have dynamic attributes 
     # the filled-in values of these attributes need to be saved as ProductAttributeValue instances 
     #... 


    def save(commit): 
     m = super(ProductForm, self).save(commit=False) 

     #looping thru my custom attributes and constructing instances 
     #to simplify I will just put one example 
     attr_val=ProductAttributeValue(product=m) 
     attr_val.value=self.clean_data['myval'] 
     m.productattributevalue_set.add(attr_val) 

     if commit: 
      m.save() 

     # also doing m2m_save if exists 

     return m 

Итак, как я ожидал, что это терпит неудачу с product_id = None ошибка. Я также попытался понять, как работает InlineForm от django (на стороне администратора), но похоже, что они сначала сохраняют основной продукт, а затем ProductAttributeValue, и, если сказать, что сбой ProductAttributeValue не удается, они прекрасны. Для моего случая это неприемлемо, то есть либо я должен сохранить всю форму (как продукт, так и значение) или сбой. Я могу сэкономить с commit = True с самого начала, но, как я уже сказал, мне не нужен случай, когда продукт сохраняется, а значение - нет.

Любая помощь приветствуется.

+0

Конечно, вы не можете добавить несохраненный экземпляр в другой несохраненный экземпляр. Что случилось с вызовом 'save()' и присвоением атрибута? Вы ожидаете, что это так или иначе сработает? Если да, то есть ответ ниже о транзакциях. Мне просто интересно, почему вы ожидаете неудачи здесь. Вы должны сделать проверку заранее, чтобы убедиться, что она не сбой на уровне базы данных. –

ответ

1

https://docs.djangoproject.com/en/dev/topics/db/transactions/ Я думаю, вы можете сделать это, используя ручную транзакцию. Зафиксируйте первое сохранение, а затем откат, если второй сбой:

@transaction.commit_manually 
def viewfunc(request): 
    ... 
    # You can commit/rollback however and whenever you want 
    transaction.commit() 
    ... 

    # But you've got to remember to do it yourself! 
    try: 
     ... 
    except: 
     transaction.rollback() 
    else: 
     transaction.commit() 
+0

Спасибо за ответ! Мне было любопытно узнать, есть ли другие решения. Я уже использую transaction.commit_on_success в нескольких местах. Я предполагаю, что это позволяет избежать ручных откатов/фиксации вызовов. – user1039384

0

Поскольку для вашего продукта не создается идентификатор, пока он не будет сохранен, я не думаю, что вы сможете сохранить атрибуты перед продуктом. Во всяком случае, нужно сохранить их перед остальными - сначала можно сохранить Продукт, затем сохранить атрибуты и добавить некоторую логику для удаления Продукта, если по какой-то причине сбой ProductAttributeValues ​​невозможен. Я не понимаю, почему в любом случае сохранение в ProductAttributeValues ​​будет неудачным, поскольку проверка уже должна была быть выполнена в чистом виде.