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.+/>)'.
Related
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.
I'm manually displaying my formset as a table, with each form being looped over. At the bottom of each form I include the hidden fields like:
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
But the problem is that I am also including properties in my form like:
class AllocationForm(forms.ModelForm):
name = forms.CharField(widget=forms.TextInput(attrs={'size': '15'}))
def __init__(self, *args, **kwargs):
super(AllocationForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields['total_budgeted'] = self.instance.total_budgeted()
self.fields['total_budgeted_account_percent'] = self.instance.total_budgeted_account_percent()
self.fields['actual_spent'] = self.instance.actual_spent()
self.fields['actual_spent_account_percent'] = self.instance.actual_spent_account_percent()
self.fields['total_budgeted_category_percent'] = self.instance.total_budgeted_category_percent()
self.fields['actual_spent_category_percent'] = self.instance.actual_spent_category_percent()
class Meta:
model = Allocation
exclude = {'created', 'modified', 'source_account'}
And this works in the sense that I definitely see the properties being called, however they display as nothing so that's another issue.
The problem is when I keep the hidden fields in the template I will get errors such as 'int' object has no attribute 'get_bound_field' and so on depending on the return type of the property/method call.
My question is first: is there a check I can do to see if the field is a property in the template and therefore skip over it?
It may have something to do with how I'm using the property since in fact every property is displaying nothing (but I see it callback), so second would be about how to display the properties.
Well I am in the next step of the problem, but i have success in generating true form fields. In place of:
if self.instance:
self.fields['total_budgeted'] = self.instance.total_budgeted()
You can write:
if self.instance:
self.fields['total_budgeted'] = form.CharField(
initial=self.instance.total_budgeted(),
widget=HiddenInput()
)
In this code you I instantiate the form Field as CharField, you can use the FormField you want, and I hide it by choosing the Hidden input widget.
Hi Have a choice field
class PropertyReportForm(forms.Form):
property = forms.ChoiceField(widget = forms.RadioSelect,required = False)
def __init__(self,*args,**kwargs):
properties = kwargs.pop('properties')
property_choice = []
for property1 in properties:
index = (property1.id,"Name :"+property1.name+" Area:"+str(property1.area)+" "+property1.image)
property_choice.append(index)
super( PropertyReportForm, self).__init__(*args, **kwargs)
self.fields['property'].choices = property_choice
Now while displaying I want it to display
How can I do this?
Template code similar to what I want. This does not work. But I want this
{% for field in propertyreportform %}
{{ field }} <img src="/media/{{ field.image }}" />
{% endfor %}
To solve this problem you basically have three options:
Create your own renderer, and use it as argument for ChoiceField, unfortunately you will need to create it from scratch, since django doesn't allow you to simply override RadioFieldRenderer class. https://github.com/django/django/blob/1.5.4/django/forms/widgets.py#L693
Just loop over your choices and use manually created radio input tags, there's not a lot of validation to do and retrieving selected item or model object is also simple enough.
The simplest and less recommended way could be to include the whole image tag inside label string (use settings to get media url part).
My 'Note' model has a Charfield called 'tags'. I want to take the Note.tags string and render it as a . I have a method that will give me a python list and I am sort of hoping that I can use the form method '.as_ul' in the template. But I can't seem to get the variable into the template. Here is what I am trying:
My view class:
import string
...
class NoteDetailView(generic.DetailView):
model = Note
template_name = 'note_taker/note'
def tag_string_to_list(self):
tag_string = Note.tags
tag_list = string.split(tag_string)
return render(template_name, Context({'tag_list':tag_list}, note_taker))
My template:
<ul>
{{ tag_list.as_ul }}
</ul>
even if I am wrong about how to use '.as_ul' I can't even render the list with {{ tag_list }}
I suppose I am not understanding how view methods work then.
Use the get_context_data method.
class NoteDetailView(generic.DetailView):
def get_context_data(self, **kwargs):
context = super(NoteDetailView, self).get_context_data(**kwargs)
context['tag_list'] = Note.tags.split()
return context
Within the template, you won't be able to use .as_ul, but there is a built in filter unordered_list that will probably do what you want:
<ul>
{{ tag_list|unordered_list }}
</ul>
Although you should really consider defining a standalone Tag model and using a many-to-many relationship rather than just a char field. This is one of the classic examples of many-to-many relationships. Or using one of the third-party Django tagging packages.
I always use Django's standard ContextMixin. It makes sure that the view object is available in the template as view.
So the view becomes like
class NoteDetailView(generic.ContextMixin, generic.DetailView):
model = Note
template_name = 'note_taker/note'
def tag_string_as_list(self):
return Note.tags.split()
And in the view you do:
<ul>{{ view.tag_string_as_list }}</ul>
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)