Manually render Django form fields with variable label - django

I am creating forms with dynamic fields (and field names) using the below code
class BuyItemForm (forms.Form):
def __init__(self, inventory_list, *args, **kwargs):
super(BuyItemForm, self).__init__(*args, **kwargs)
for item in inventory_list:
self.fields["count%s"%item.item_name] = forms.IntegerField()
self.fields["price%s"%item.item_name] = forms.FloatField()
So I get a form that has field names like "counteggs", "priceeggs", "countmilk", etc... when these items are in the inventory_list
I now want to render the fields manually in my template. I am able to iterate through the set of fields, for example
{% for field in form %}
{{ field }}
{% endfor %}
But I am unable to pick out each field individually by using the field name in a string. I have tried
{{ form.fields['counteggs'] }}
but this doesnt work. Any ideas how I can make this work?

Did you try {{ form.fields.counteggs }} ? In the templates, dictionaries are accessed with dot notation.

So, I found a rather convoluted way of doing this by creating a filter in views.py that receives the form and a key as parameters. It iterates through the form till a field that has a label matching the key is found.
#register.filter
def get_countitem(myform, key):
for field in myform:
if field.label.lower() == key.lower():
return field
return "none"
and in the template
{{ form|get_countitem:"counteggs" }}
It works. I can get my template to render form fields by passing the field label in a string but doesnt seem like a particularly elegant solution to me. Any other ideas are most welcome.

Related

Manual Django Form Display

I want to manually render the form in my template, but what I'm trying is not yielding the expected result, and it is not apparent as to why.
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['field1'] = forms.BooleanField()
self.fields['field2'] = forms.BooleanField()
systems = System.objects.all()
for i in range(len(systems)):
self.fields['s' + str(i)] = forms.BooleanField()
self.fields['field3'] = forms.BooleanField()
self.initial_fields = [self.fields['field1'], self.fields['field2']]
now when I do this in my template:
{% for field in form.visible_fields %}
{{ field }}
{% endfor %}
it returns what you would expect...after looking up the method visible_fields it simply returns a list of fields. So in theory, if I create my own list of fields as with self.initial_fields, the form generator should render the following same as above:
{% for field in form.initial_fields %}
{{ field }}
{% endfor %}
but instead I get this output in my template:
<django.forms.fields.BooleanField object at 0x000001242F51E588>
<django.forms.fields.BooleanField object at 0x000001242F51E400>
I'm assuming I'm missing some initialization of the field itself? I don't understand why one works and the other doesn't. Anyone know?
You need to get the bound field object and not the field itself. It's not really a clean way of doing so, but if you are looking to hack around it, you should do like so,
...
self.initial_fields = [self.fields['field1'].get_bound_field(self, 'field1'),
self.fields['field2'].get_bound_field(self, 'field2')]
...
Hope this helps!

Correlate HTML element name with Django form field name

How can I correlate HTML element (input) name and "Django" form field name?
For example, I have HTML file:
...
<input name='some_name'>
...
And I have the Django form with "name" field:
class SomeForm(ModelForm):
name = forms.CharField()
When I create a form, pass data to it and invoke "is_valid":
form = SomeForm(request.data)
form.is_valid()
I have an error for "name" field: "This field is required". So, what I can I do if changing input name in HTML is undesirable?
As you're not rendering your form in template, then you don't even need to define it.
It's not valid in your case simply because it has no data bound to it.
You can always access your POST data in a view like this - request.POST['name']
Docs: https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.POST
They are named like your model attributes so it would be 'name' in your case.
And check if passing request.data as the 'data' argument for SomeForm is the proper way for you. I'd rather use request.POST.
Another way to do it:
You can pass your SomeForm instance to view context and render a ready to go form in your template:
def get_context_data(self, **kwargs):
# code
context['form'] = SomeForm() # with data or not
return context
and then in your template:
{{ form }}
or iterate through it:
{% for field in form %}
{{ field }}
{% endfor %}
that's always a way to do it and discover how django is naming your input's.

Formatting django form.non_field_errors in a template

How do I format django form.non_field_errors.as_text in a template without them being either an unordered list or having an * appended to the front?
{{ form.non_field_errors.as_text }} displays the errors with an * in front of the text.
This django ticket was also helpful in explaining why the * will not be removed, but that doesn't help me. I do not want the *.
Both {{ form.non_field_errors }} and {{ form.non_field_errors.as_ul }} display as an unordered list, and I do not want an unordered list.
{% for error in form.non_field_errors %}
{{error}}
{% endfor %}
When you call the list of errors as text it will try to display it as a list. Simply loop through the list to get the error by itself so you can apply your own styling.
More info about it on the django project website
Well, by default Django forms use ErrorList as error_class (proof link). As you can see, its as_text method formats list by prepending asterisks to values.
So, you can create some custom error_class with own as_text method and pass it to your form in a suitable manner.
Iterating through form.non_field_errors is not always the best approach, for instance in my case I wanted to display a tooltip like in this screenshot next to the field in question using Django Widget Tweaks.
project/app/templates/template.html
{% render_field field class="form-control is-invalid" data-toggle="tooltip" title=field.errors.as_text %}
Instead of messing around with template code to pass the HTML title attribute in one piece I followed the tip from #oblalex and wrote my own modified ErrorList class with a as_text method without asterisks.
project/app/utils.py
from django.forms.utils import ErrorList
class MyErrorList(ErrorList):
"""
Modified version of the original Django ErrorList class.
ErrorList.as_text() does not print asterisks anymore.
"""
def as_text(self):
return '\n'.join(self)
Now that the as_text() function is overwritten, you can pass your class MyErrorList as the error_class argument of your Form or ModelForm or like in my case some ModelForm formset:
project/app/views.py
from django.forms import formset_factory
from .forms import InputForm
from .utils import MyErrorList
def yourView(request):
InputFormSet = formset_factory(InputForm)
formset = InputFormSet(error_class=MyErrorList)
context = {'formset': formset}
return render(request, 'template.html', context)
And now the tooltip looks like this without the asterisk.
Instead of passing the error_class every time you instantiate a form in your views, you can just add this one-liner to your form class (see this answer from #Daniel):
project/app/forms.py
from django import forms
from .utils import MyErrorList
class InputForm(forms.ModelForm):
def __init__(self, *args, row, **kwargs):
super(InputForm, self).__init__(*args, **kwargs)
self.error_class = MyErrorList

Django: How to check if there are field errors from within custom widget definition?

I'd like to create widgets that add specific classes to element markup when the associated field has errors.
I'm having a hard time finding information on how to check whether a field has errors associated with it, from within widget definition code.
At the moment I have the following stub widget code (the final widget will use more complex markup).
from django import forms
from django.utils.safestring import mark_safe
class CustomTextWidget(forms.Widget):
def render(self, name, value, attrs):
field_has_errors=False # change to dynamically reflect field errors, somehow
if field_has_errors:
error_class_string="error"
else:
error_class_string=""
return mark_safe(
"<input type=\"text\" class=\"%s\" value=\"%s\" id=\"id_%s\" name=\"%s\">" % (error_class_string, value, name, name)
)
Can anyone shed light on a sensible way to populate the field_has_errors Boolean here? (or perhaps suggest a better way to accomplish what I'm trying to do). Thanks in advance.
As Jason says, the widget has no access to the field itself. I think a better solution though is to use the cascading nature of CSS.
{% for field in form %}
<div class="field{% if field.errors %} field_error{% endif %}">
{{ field }}
</div>
{% endfor %}
Now in your CSS you can do:
div.field_error input { color: red }
or whatever you need.
The widget has no knowledge of the field to which it is being applied. It is the field that maintains information about errors. You can check for error_messages in the init method of your form, and inject an error class to your widget accordingly:
class YourForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(YourForm, self).__init__(*args, **kwargs)
attrs = {}
if self.fields['your_field'].error_messages is not None:
attrs['class'] = 'errors'
self.fields['your_field'].widget = YourWidget(attrs=attrs)

Custom rendering a radioselect in django form / accessing single element?

I have a form like this:
CHOICES = [
('a', 'a_value'),
('b', 'b_value'),
('c', 'c_value')
]
self.fields["choice"] = forms.ChoiceField(
widget=RadioSelect(),
choices=CHOICES,
)
How can I select a single element of this form field in my template? I want to be able to do something like:
<tr><td>{{form.choice.a}}</td><td>some custom extra field</td></tr>
Or is there another way to change how the RadioSelect is rendered?
See full doc -
https://docs.djangoproject.com/en/dev/ref/forms/widgets/#radioselect
{% for radio in form.my_radio_select %}
- {{ radio.choice_label }}
- {{ radio.tag }}
{% endfor %}
You cannot do this via parameters or something, the way it is rendered is hardcoded in its widget class! See eg. http://code.djangoproject.com/browser/django/trunk/django/forms/widgets.py: RadioSelect.render (->RadioFieldRenderer.render); subclass it and override the render method, then use it in your form myfield = forms.MultipleChoiceField(widget=MyWidget(...)).
I labored over this for a few hours trying to find some way to use a custom renderer on a RadioSelect, but there is no way to pass in the choice number. Instead I went for a kludgey, but simple approach. In added an __init__ function to my form:
def __init__(self, *args, **kwargs):
super(FormName, self).__init__(*args, **kwargs)
self.radiofield_choice = re.findall(r'<li>(.+?)</li>',
unicode(self['radiofield_name']))
That uses the RadioSelect's default render to create the widget, and then parses out the individual choice HTML. You could even combine that with the defined choices to create a dictionary instead of a list.
In my template I used {{ form.radiofield_choice.0|safe }} to render just the first item. Increment the zero to get the other items.
If, for some reason, only the input fields without enclosing labels are needed, use r'(<input.+/>)'.