What is the difference between initial and instance in django formset?
When to use which?
What should I use to show the prepopulated data in an invalid form in the following case:
formset= FormSet(initial=[{'a':list.b} for list in listslist])
if request.method =='post':
formset = FormSet(request.post, ---instance/initial? ---)
initial is to set defaults for new forms.
instances is to set models instances you use as initial models, which will be populated with entered data. You can use this attribute to edit your model instances.
Related
I'm trying to understand how the initialization of formset data works. To create a formset you use formset_factory and define arguments "extra" and "max_num", which determine the number of extra blank forms after the initial/filled forms and the max number of forms posted respectfully. But then, when you create an instance of the defined formset to pass to the template as data, you pass into that instance more data, specifically 'form-TOTAL_FORMS', 'form-INITIAL_FORMS', and 'form-MAX_NUM_FORMS'. But haven't we already defined in the formset_factory the max_num_forms? Why are we defining this twice? For documentation initial forms seems to be just what it sounds--forms already filled in, which is different than "extra". Just don't understand defining the max arg twice.
According to the docs:
You may have noticed the additional data (form-TOTAL_FORMS,
form-INITIAL_FORMS and form-MAX_NUM_FORMS) that was required in the
formset’s data above. This data is required for the ManagementForm.
This form is used by the formset to manage the collection of forms
contained in the formset. If you don’t provide this management data,
an exception will be raised.
https://docs.djangoproject.com/en/2.2/topics/forms/formsets/#understanding-the-managementform
Is there any easy way to change the instance of a Django model form after it's been initialised?
For example the following would pre-populate the form in a template with the contents currently in the db for the object with an id of 1:
form = exampleModelForm(instance = Model.objects.get(pk=1)
This will also save the relevant object when save() is called rather than create a new one.
Where this will create a new object and not pre-populate the form in the template:
form = exampleModelForm()
As I'm returning a blank form from a different method, I would then like to assign an instance to it after it's been created and amend the object so that it can be saved and the template is pre-populate with the values that exist in the db. I want something like this but what I've tried doesn't seem to work:
form = methodThatGetsForm(somearg)
form.instance = ExampleModel.objects.get(pk = getId(somearg))
Is there a simple function that I'm missing here?
I have NOT tested this, but I wonder if you can reinitialize a new form with the returned form data and the instance as the new object.
form = methodThatGetsForm(somearg)
new_form = ExampleModelForm(form.cleaned_data, instance=ExampleModel.objects.get(pk=getID(somearg)))
new_form.save()
If not, there should be a way to get the data as a dictionary of key/value pairs for the fields from the form returned, using it's ._meta data. I think if you send that dictionary to the form init it will assign those values to the instance object, just like sending it request.POST.
i have two models
class A(models.Model):
name = models.CharField(50)
type = models.CharField(50)
class B(models.Model):
field1 = ForeignKeyField(A)
value = IntegerField()
I'd like to display a number of forms via a ModelFormSet (from model A) where each one of the forms displays in turn InlineFormSets (from model B) for all objects connected to the object.
How can i combine both modelformsets (model A) and inline formsets (mdoel B) on save method in django views?
I've struggled with this a week ago. I would suggest you start using the inlineformset_factory method: https://docs.djangoproject.com/en/dev/topics/forms/modelforms/
Then just add your base form and your formset to your view and render them.
Here's some blog post that helped me figure this out: http://charlesleifer.com/blog/djangos-inlineformsetfactory-and-you/
There's also a solution to make it work using Class Based Views: http://haineault.com/blog/155/
I'm not sure this is a good solution, but it's the only solution I can come up with that will work.
I would create a List of Dictionaries. Each Dictionary will have a baseForm key which will hold your B class form and a inline_forms key which will hold your class A inline_formset.
In your view code iterate over the list and print the forms accordingly. Generate a different value for each of the forms submit button.
For example submit_1 for the first form , etch ..
When a form is submitted, check this value (the value of the submit button), if the value is for example submit_2 you are certain the second item/form of your list has been submitted.
From their you can start processing the submitted data.
I have a very complicated form and I choose to not use ModelForm since I needed flexibility and control over the fields. Since I am not using ModelForm, I can't simply do something like instance=order, where order = Order.objects.get(pk=1).
Currently I am pre-populating every field with initial in the forms.py as oppose to the views.py like this
self.fields['work_type'] = forms.ChoiceField(choices=Order.WORK_TYPE_CHOICES, initial=order.work_type)
But I was wondering if I could pass the entire order object to a form or do I have to declare initial to every field?
Is there a way to do something like
order_form = OrderEditForm(data=request.POST, initial=order)
in views.py?
I have a very complicated form and I choose to not use ModelForm since
I needed flexibility and control over the fields
Everything that you can do using a Form, you can do in a ModelForm such as adding new fields or over-riding attributes on the fields etc.
But I was wondering if I could pass the entire order object to a form
or do I have to declare initial to every field?
You can pass the order object into the form but you will still have to populate each field individually either in the forms or in the view function.
So in your view you would do something like this:
intial = {'order_number': order.number, 'order_id': order.id}
form = OrderForm(initial=initial)
The easiest way to prepopulate data to a form is passing a dictionary as first argument to de form constructor.
order_form = OrderEditForm(order.__dict__())
where __dict__() is a method that passes "order" object attributes to a dictionary with each attribute's name as a key and their content as value.
An example of how to "invent" such a method could be something like:
order_initial = Order.objects.filter(pk=order.pk).values()[0]
and then construct the form with:
order_form = OrderEditForm(order_initial)
Look at this example (how they populate values at "post" time):
For future reference to other people:
I have since found out after reading SO's comments and answers that it's better to use ModelForm even if you end up explicitly defining every field manually (using something like self.fields['foo'] = forms.CharField()).
In any case, if you are trying to pass a dictionary of current values in a form then the best (built-in) way to convert a model to a dictionary is actually using model_to_dict:
from django.forms.models import model_to_dict
order = Order.objects.get(pk=1)
dictionary = model_to_dict(order)
form = OrderEditForm(dictionary)
I got the solution from this blog. I hope this will be helpful for someone.
Basically I have a model with a ManyToMany field, and then a modelform derived from that model where that field is rendered as a "multiple choice" selectbox. In my template I'm having that field omitted, electing instead to prepare the values for that field in the view, then pass those prepared values into request.POST (actually a copy of request.POST because request.POST is immutable), then feeding request.POST to the form and then carry on as normal. I can't figure out how to do this, because request.POST isn't just a simple python dictionary, but instead a QueryDict, which behaves a little differently.
The field I need to populate is called "not_bases". When I create the widget using the form, it works perfectly well internally, but just not to my liking UI-wise. When I inspect the django-form submitted POST value via django's handy debug error window, the working QueryDict looks like this:
<QueryDict: {u'not_bases': [u'005', u'00AR', u'00F', u'00FD'], [...] }>
It appears the value for "not_bases" is a list, but it's not simply a list. I can't just .append() to it because it won't work. I dug around the documentation and found .update(), which appears to work, but doesn't. Here is my code:
newPOST = request.POST.copy()
for base in bases:
newPOST.update({"not_bases": base.identifier})
and here is the output:
<QueryDict: {u'not_bases': [u'KMER', u'KYIP'], u'reference': [u''], [...] }>
But when I feed that QueryDict to the form, I get an form validation error that says "not_bases: Enter a list of values.". Its obvious that the list-looking things coming from the str() representation of the QueryDict are not the same in the two cases above, even though they look exactly the same
So how do I do this?
It's really not clear what you're trying to do here, but I doubt that hacking the QueryDict is the right way to achieve it.
If you are trying to customise the display of the not_bases field, you can simply override the definition in your modelform declaration:
class MyModelForm(forms.ModelForm):
not_bases = forms.ChoiceField(choices=[(base, base) for base in bases])
class Meta:
model = MyModel
Or, if you simply want to avoid showing it on the form, you can exclude it from the form and set the value after validation.
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
exclude = ['not_bases']
....
if request.POST:
if form.is_valid():
instance = form.save(commit=False)
instance.not_bases = bases
instance.save()
Does either of these do what you want?