I am a creating a Django project, where i am using inline_formset to add multiple forms from the frontend (Just like the backend). I am trying create a new object now from the frontend, the main models get saved but the the model that is a foreignKey to the main model is not saving (Inline Model).
I am getting the error message: save() prohibited to prevent data loss due to unsaved related object 'predictions'. Here is my CreateView Code.
views.py
class ProductInline():
form_class = PredictionForm
model = Predictions
template_name = "core/create/create_bet.html"
def form_valid(self, form):
named_formsets = self.get_named_formsets()
if not all((x.is_valid() for x in named_formsets.values())):
return self.render_to_response(self.get_context_data(form=form))
# self.object = form.save()
new_form = form.save(commit=False)
new_form.user = self.request.user
new_form.save()
# for every formset, attempt to find a specific formset save function
# otherwise, just save.
for name, formset in named_formsets.items():
formset_save_func = getattr(self, 'formset_{0}_valid'.format(name), None)
if formset_save_func is not None:
formset_save_func(formset)
else:
formset.save()
return redirect('core:dashboard')
def formset_variants_valid(self, formset):
"""
Hook for custom formset saving.. useful if you have multiple formsets
"""
variants = formset.save(commit=False) # self.save_formset(formset, contact)
# add this, if you have can_delete=True parameter set in inlineformset_factory func
for obj in formset.deleted_objects:
obj.delete()
for variant in variants:
variant.prediction = self.object
variant.save()
def formset_images_valid(self, formset):
"""
Hook for custom formset saving.. useful if you have multiple formsets
"""
images = formset.save(commit=False) # self.save_formset(formset, contact)
# add this, if you have can_delete=True parameter set in inlineformset_factory func
for obj in formset.deleted_objects:
obj.delete()
for image in images:
image.product = self.object
image.save()
class PredictionCreate(ProductInline, CreateView):
def get_context_data(self, **kwargs):
ctx = super(PredictionCreate, self).get_context_data(**kwargs)
ctx['named_formsets'] = self.get_named_formsets()
return ctx
def get_named_formsets(self):
if self.request.method == "GET":
return {
'variants': PredictionDataFormSet(prefix='variants'),
}
else:
return {
'variants': PredictionDataFormSet(self.request.POST or None, self.request.FILES or None, prefix='variants')
}
If you look at the docs and example at Using an inlineformset in a view, you’ll see that when you’re binding data to a formset on a post, you should specify the instance of the parent class so that the foreign keys of the child class is set to refer to the proper object.
Since you appear to be submitting both the parent and child classes in the same post, that means you need to save the parent object before you bind the post data to the formset to be able to supply that instance to the formset.
I found out a couple things, thanks to your refrence.
i was supposed to pass in variant.predictions instead or variant.product which was what i was using.
Now i noticed something else. when i create a new object using the frontend form, it saved the initial model but does not save the inline model, and that is because of this block of code.
but when i uncomment the self.object = form.save() everything works well apart from the fact that the model won’t have any user instance that created the post.
I want to save the user that made the current post and at the same time save the inline objects, is there something wrong with this code.
First, I would get rid of having PredictionCreate inherit from ProductInline. It makes the flow of events here very difficult to follow.
Then I would get rid of worrying about having a “name” for the formset. It’s a formset on a page, it really doesn’t matter what it’s called internally.
Finally, I would implement the inline formset as a field on the base form.
Doing all this - which includes refactoring your code to simplify it - will make it more clear how this should work.
Again, I refer you to the example at Using an inlineformset in a view for the overall organization of the sequence of events that needs to occur.
There is no functional difference between an FBV and a CBV.
A CBV is merely a “code-organization” tool that, in some situations, greatly reduces the amount of code needing to be written for a particular type of view.
The Django-provided CBVs are not the only CBVs that can be created - they’re a starting point, not an ending point, and generally useful in a well-defined set of circumstances. (For an example of an alternative implementation of the CBV concept, see http://django-vanilla-views.org/)
Likewise, there are many situations where the Django generic CBVs are a poor choice. (I’ve written about that in other posts here in the forum.) In those situations, you would want to either create your own CBVs to enhance what Django has provide, or use FBVs.
There are others here who more strongly express the idea that FBVs are the only way to go. (See Django Views — The Right Way as an example)
In my opinion there is no absolute “right” or “wrong” here - it’s an architectural decision that should be made in the context of the overall system being developed.