combine modelformset and inlineformset in django views - python

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.

Related

What is the difference between initial and instance in django formset?

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.

How to send django models into a template with additional custom fields?

I faintly remember seeing this done before in tutorials in the past. However, im having trouble finding out exactly how in the docs currently.
Suppose we had a model called Post. This model has a field called timestamp. However, when we send this model into the template we don't care about timestamps. Instead, we want the more popular "age" (created X mins/hrs ago), which thankfully, can be deduced from the timestamp.
Instead of creating a whole new field for timestamp, and instead of using custom template tags, can we somehow add a field to a model temporarily before sending it over to our template?
Ex.
# views.py
# Is the below code right? do I need to save()?
posts = Posts.objects.filter(...).filter(...)[:X]
for post in posts:
# Post does not have an age field, we are creating one
# temporarily before sending it to the template
post.age = some_function(post.timestamp)
return render_to_response(template, {'posts' : posts}, etc...)
Thank you.
Just make it a property on the model.
class Post(Model)
#property
def age(self):
return now() - self.timestamp

Pass an entire inital object in an edit form (data pre-population) in Django

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.

Django 1.1 FormWizard, Dynamically extend form

I am trying to create a multipage form, where the number of field elements on the second page is defined by the answers given on the first.
I have a formWizard set up and my understanding is that I need to use process_step() to alter the setup for the next page. I can either extend an existing form definition to add more elements, or merge 2 or more form definitions together to produce the correct number of form elements, but i have no idea how to do this.
Eg
Page 1 - Select interested subjects:
Page 2 - for each subject: ask relevant questions. Questions are defined as seperate forms in application, but need to be shown on one page, or merged into a single form.
Any help much appreiciated.
Spender
Spender,
At least at the moment I don't know a way of merging multiple forms onto one page in a FormWizard. In django 1.2 you will be able to include FormSets as steps in FormWizards (as per this ticket) but those only deal with multiple copies of identical forms, not compilations of many forms. But there is a way to do what you ask:
from django.contrib.formtools.wizard import FormWizard
from django import forms
class SubjectForm(forms.Form):
subjects = forms.MultipleChoiceField(choices = (('language', 'language'),
('sport','sport')))
class RelatedQForm(forms.Form):
"""Overload the __init__ operator to take a list of forms as the first input and generate the
fields that way."""
def __init__(self, interested_subjects, *args, **kwargs):
super(RelatedQForm, self).__init__(*args, **kwargs)
for sub in interested_subjects:
self.field[sub] = forms.CharField(label = "What do you think about %s" % subject)
class SubjectWizard(FormWizard):
def done(self, request, form_list):
process_form_list(form_list)
def process_step(self, request, form, step):
if step == 1:
chosen_subs = form.cleaned_data['subjects']
self.form_list[1] = RelatedQForm(chosen_subs)
With this code you instantiate your FormWizard as you normally would in the view and then let the wizard class take care of everything behind the scenes.
The general idea is to overload the init class of a "RelatedQForm" to dynamically alter the fields. This code snippet was taken from here. You can make the processing within the init operator as complex as you'd like, read "include the fields from your forms as if-elif blocks inside the for-loop" ... you could probably even figure out a way to strip the fields from your current forms programatically, I'd have to see them to figure it out though.
Your "process_form_list" function will need to loop over the fields using something like:
for field, val in form.cleaned_data.items():
do_stuff
Hope this gets you on your way :)
I don't think the
self.form_list[1] = RelatedQForm(chosen_subs)
part works. I always get the error message:
object is not callable
It seems to be like form_list only accepts RelatedQForm (the name of the form), not an instance of it.

How do I build a custom "list-type" entry to request.POST

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?

Categories