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.
The classical Formsets version did not work at all. I could not find a method to link the linked object (foreign key, here Credit) to the Formset Object.
So, if I use this way:
You create the inlineformset_factory as you’ve identified, but the instance of the parent class is identified when the formset instance itself is created, not when you create the factory class.
But no, it’s not required that you use an InlineFormSet - it’s just that it does the work that you would otherwise need to be doing.
Thanks, that specific document was the solution.
I solved my Problem with the section “Using an inline formset in a view”
But If i use a ModelForm FormSet with a referenced object.
How would I set the object when I receive the form data?
The referenced object field in my case is required, but at the state of receiving this object through the form it is not stored in the database either.
Usually I would just do something like this:
if request.method == "POST":
data = request.POST
form = MainModelForm(data)
if form.is_valid():
obj = form.save()
formset = ReferencingModelFormFormSet(data, initial=[dict(ref=obj)])
if formset.is_valid():
formset.save()
But I cannot get it to work. It does not take the initializing data (ref=obj) that should tell the object in the FormSet that the required reference is stored in database and is the obj
Would be great if you have a hint
But maybe I just blind …