Django: Why is form.id within a formset empty? - django

I came across a strange issue.
I create a regular formset and everything works as intended:
deal_formset = formset_factory(DealCForm, extra=0)
...
formset = deal_formset(data)
Within the template though I don't seem to get the id of the current form, which I am iterating through. why is form.id empty?
{{ formset.management_form }}
{% if formset %}
{% for form in formset %}
...
{{form.id}}
{% endfor %}
{% endif %}

Related

Django forms how to dynamically render fields in a loop

Let's say I have a form with some fields. Some of those fields are units of measurements:
['quantity_units', 'weight_units', 'dimension_units']
I also have these fields:
['quantity', 'weight', 'dimension']
I would like to display the unit of measurement right beside the field:
Quantity: ___________________ _______<unit of measurement drop-down list>______
I thought I should first loop through the form's fields. For each iteration through the form's fields, I would check if the field name is in the units_list, if it is, then I would render the field and its unit field like so:
{% for field in form %}
{% for field_name in units_fields %}
{% if field.name in field_name %}
{{ field|add_class:"site-form-control" }} {{ form.field_name}}
{% else %}
{% ifchanged %}
{{ field|add_class:"site-form-control" }}
{% endifchanged %}
{% endif %}
{% endfor %}
{% endfor %}
This does not render the unit of measurement field with the drop-down list widget.
Any ideas how to fix this?
Edit:
I noticed that for some reason, Django displayed the {{ field }} twice and did not display {{ form.field_name }}. To manually choose the field, I wrote a template filter to get the value given the key for a dictionary. Then, I used it on the {{ form.fields }} which is an orderedDict
{% for field_name in units_fields %}
{% if field.name in field_name %}
{{ form.fields|dict_key:field_name }}
{% else %}
{% ifchanged %}
{{ field|add_class:"site-form-control" }}
{% endifchanged %}
{% endif %}
{% endfor %}
This renders the text representation of the field I want:
<django.forms.fields.ChoiceField object at 0x000001E5F35F3C18>
Any help to convert this text to an actual field is appreciated
The way I solved this was to write a simple tag that returns a boundfield object <django.forms.boundfield.BoundField object at 0x0000025 instead of <django.forms.fields.ChoiceField object at 0x000001E5F35F3C18>
#register.simple_tag(takes_context=True)
def get_form_field(context, form, field_name):
''' Given a form and a field_name, it returns form.field'''
try:
field = form[field_name]
except KeyError:
field = None
return field

Django - Add 3 users with one form

I'm trying to create a form that save 3 users.
my forms:
class UserForm(forms.ModelForm):
class Meta:
model = MyUsers
exclude = ('address',)
my views:
def adduser(request):
if request.method == "POST":
rform = UserForm(request.POST, instance=MyUsers())
if rform.is_valid():
new_users = rform.save()
return HttpResponseRedirect...
else:
rform = UserForm(instance=MyUsers())
return render_to_response...
my template structure:
<form method="post">
{% for field in rform %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% endfor %}
{% for field in rform %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% endfor %}
{% for field in rform %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% endfor %}
<input type="submit" value="Save" />
</form>
the problem
The form works properly but add only the last inserted user.
Alasdair's suggestion (formset) is a way to generate multiple instances of the same form at once.
The forms can be rendered in the template by looping over the formset instance:
{% for form in formset %}
{{ form.id }} #PK field is MANDATORY, see reference[2] below
{{ form.field1 }}
{{ form.field2 }} #or just as something {{ form.as_p }}
{% endfor %}
In your views, formset validation is made once for all if formset.is_valid(): and rformset = formset_factory(MyUsers, extra=1, max_num=3) to create the formset.
Don't forget the imports.
References, both from Django Docs:
Formsets {1}
Creating forms from models {2}

django: how to access only first error message in form.errors?

I have a model called Users and I have a form for that model called UsersForm. In my views.py, I created a version of UsersForm, like so
form = UsersForm()
if reqest.method == POST:
form = UsersForm(request.POST)
if form.is_valid():
form.save()
c = {}
c.update(csrf(request))
c.update({'form':form})
return render_to_response('home_page.html', c)
Now, my home_page.html is this:
<html>
<body>
<form method="post" action="">{% csrf_token %}
{{ form }}
<input type="submit" value="Register"/>
</form>
{% if form.errors %}
{% for field in form %}
<p> {{field.errors}} </p>
{% endfor %}
{% endif %}
</body>
</html>
So, what I want is, I want to display only the first error in {{ field.errors}}.
What I was thinking was something like:
{% if form.errors %}
{% for field in form %}
<p> {{field.errors}} </p> {{ break}}
{% endfor %}
{% endif %}
but there is no break in the django template language, right? I also tried thinking about using {% for field in form|slice:":1" %} but that wouldn't work either. Anyone know how to do this?
You can index lists in a django template by using the dot notation:
{{ field.errors.0 }}
Be sure to check that there is at least 1 error before doing that though, or you will get an Index out of range error.
Take the template tag route.
Here's a sample template tag:
from django.template.defaulttags import register
#register.filter(name='show_error')
def show_error(dictionary):
try:
return dictionary.values()[0][0]
except (TypeError,IndexError,AttributeError):
return 'tip: try again'
And use it in your template like so:
{% if form.errors %}{{ form.errors|show_error }}</span>{% endif %}

how do I know I am dealing with the extra (empty) form within a formset?

deals_formset_factory = modelformset_factory(Deal, form=DealForm, extra=1)
formset = deals_formset_factory(queryset=query, prefix='deals')
{% for fs in formset.forms %}
{{ fs.id }}
{% endfor %}
While traversing through the forms of a formset, is it possible to find out which form contains instance data and which one is extra and hence empty?
You could check to see whether the form instance has a primary key. If it does, then it exists in the database. If it doesn't, then it's an extra form.
Untested code:
{% for form in formset.forms %}
{% if form.instance.pk %}
Form instance is saved in db
{% else %}
New instance
{% endif %}
{% endfor %}

formatting formset results values not input

my template looks like:
<form method="post" action="">
{{ formset.management_form }}
{% for form in formset.forms %}
{{ form.contractor }} {{ form.date }} {{ form.value }} {{ form.comment }} {{ form.operation_type }} {{ form.category }} {{ form.account }}
{% endfor %}
</form>
but the result allows to change all of fields - but i want only one.
I thought that (please notice ".value" after all but category field) solves the problem, but not.
<form method="post" action="">
{{ formset.management_form }}
{% for form in formset.forms %}
{{ form.contractor.value }} {{ form.date.value }} {{ form.value.value }} {{ form.comment.value }} {{ form.operation_type.value }} {{ form.category }} {{ form.account.value }}
{% endfor %}
</form>
UPD:
relevant view code
def detail(request, category_id):
from django.forms.models import modelformset_factory
OperationFormSet = modelformset_factory(Operation)
if request.method == "POST":
formset = OperationFormSet(request.POST, request.FILES,
queryset=Operation.objects.filter(category=category_id))
if formset.is_valid():
formset.save()
# Do something.
else:
formset = OperationFormSet(queryset=Operation.objects.filter(category=category_id))
return render_to_response("reports/operation_list.html", {
"formset": formset,
})
Your problem is that the readonly values are NOT passed back to the server, and thus your formset should fail as it's only receiving one field.
You'd want to show the value AND set a hidden field to store the data the formset is expecting. But I'd recommend a different approach...
Ultimately, a formset and form is used to display html forms and not built for anything else. You'd have to hack it to show hidden widgets, make sure users can't POST arbitrary data, etc. So instead, I would use the forms framework to only display your one editable field.
Create a ModelForm that only has one editable field
class MyModelForm(forms.ModelForm):
class Meta:
model = Operation
fields = ('category',)
MyModelFormSet = modelformset_factory(Operation, form=MyModelForm)
As for displaying values in the template, ModelForms should have their instance directly accessible via form.instance so you should be able to do something like this:
{% for form in formset %}
{{ form.instance.some_attribute }}
{{ form.category }}
{% endfor %}