Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

save() prohibited to prevent data loss due to unsaved related object

Tags:

python

django

I need to pass a primary key from a newly created ModelForm to another form field in the same view but I get an error. Any suggestions to make this work? It looks like in the past, this would be the answer:

def contact_create(request):     if request.method == 'POST':         form = ContactForm(request.POST)         if form.is_valid():             form.save()             return HttpResponseRedirect(reverse(contact_details, args=(form.pk,)))     else:         form = ContactForm() 

From the documentation, this is what is happening in the newer Django version > 1.8.3

p3 = Place(name='Demon Dogs', address='944 W. Fullerton') Restaurant.objects.create(place=p3, serves_hot_dogs=True, serves_pizza=False)
Traceback (most recent call last):
...
ValueError: save() prohibited to prevent data loss due to unsaved related object 'place'.

This is how I am getting my pk from the view:

my_id = "" if form.is_valid():     # deal with form first to get id     model_instance = form.save(commit=False)     model_instance.pub_date= timezone.now()     model_instance.user= current_user.id     model_instance.save()     my_id = model_instance.pk  if hourformset.is_valid():     hourformset.save(commit=False)     for product in hourformset:         if product.is_valid():             product.save(commit=False)             product.company =  my_id             product.save() else:     print(" modelform not saved") return HttpResponseRedirect('/bizprofile/success') 
like image 714
Edwinner Avatar asked Nov 21 '15 00:11

Edwinner


2 Answers

This was introduced in Django 1.8. Previously you could assign not saved instance to One-To-One relation and in case of fail it was silently skipped. Starting from Django 1.8 you will get error message in this case. Check a documentation of Django 1.7 -> 1.8 upgrade.

It says:

Assigning unsaved objects to a ForeignKey, GenericForeignKey, and OneToOneField now raises a ValueError.

If you are interested in more details, you can check save method in django.db.models.base: Some part of it:

for field in self._meta.concrete_fields:     if field.is_relation:         # If the related field isn't cached, then an instance hasn't         # been assigned and there's no need to worry about this check.         try:             getattr(self, field.get_cache_name())         except AttributeError:             continue         obj = getattr(self, field.name, None)         # A pk may have been assigned manually to a model instance not         # saved to the database (or auto-generated in a case like         # UUIDField), but we allow the save to proceed and rely on the         # database to raise an IntegrityError if applicable. If         # constraints aren't supported by the database, there's the         # unavoidable risk of data corruption.         if obj and obj.pk is None:             raise ValueError(                 "save() prohibited to prevent data loss due to "                 "unsaved related object '%s'." % field.name             ) 

Last 5 rows are where this error is raised. basically your related obj which is not saved will have obj.pk == None and ValueError will be raised.

like image 42
wolendranh Avatar answered Sep 21 '22 15:09

wolendranh


it is simple:

p3 = Place(name='Demon Dogs', address='944 W. Fullerton')    p3.save() # <--- you need to save the instance first, and then assign Restaurant.objects.create(     place=p3, serves_hot_dogs=True, serves_pizza=False )  
like image 157
doniyor Avatar answered Sep 23 '22 15:09

doniyor