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.
Related
I have a model which contains a number of user uploaded files that other than the file itself also contain a description and some other meta information.
class ArchiveFile(models.Model):
archive_file = models.FileField(upload_to=grab_archive_folder, default=None, blank=False)
description = models.CharField(max_length=255)
What I want is for a user to (1) upload new files. And (2) be able to edit the descriptions of all files associated with the user, including the recently uploaded. The uploading of new files is done via AJAX / JQuery and new forms (as part of a formset) are generated dynamically.
In order to do be able to edit the descriptions in an efficient matter, it would help for a user to know of what file it is changing the description, and so I would like the filename to be displayed.
My initial solution was the following:
forms.py
class ArchiveDataForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['archive_file'].widget.attrs['disabled'] = True
class Meta:
model = ArchiveFile
fields = ['archive_file','description']
views
def archive_data_update(request):
if request.method == 'GET':
ArchiveDataFormSet=modelformset_factory(ArchiveFile, form=ArchiveDataForm, extra=0)
archive_formset = ArchiveDataFormSet(queryset=ArchiveFile.objects.filter(user_id=request.user.id)
template = 'archive_data_edit.html'
template_context = {
'archive_formset': archive_formset,
...
}
return render(request, template, template_context)
if request.method == 'POST':
ArchiveDataFormSet=modelformset_factory(ArchiveFile, form=ArchiveDataForm, extra=0)
archive_formset = ArchiveDataFormSet(request.POST, queryset=ArchiveFile.objects.filter(user_id=request.user.id)
if archive_formset.is_valid():
for archive_form in archive_formset:
archive_form.save()
return HttpRespone('ok')
template
{% for archive_form in archive_formset %}
{{ archive_form.archive_file.value }}
{{ archive_form.description }}
{% endfor %}
My issue is that I am getting validation errors on the dynamically created forms, saying that no file is present. Which I suppose is correct since all I do is inject the filename to the dynamically created form via my AJAX/JQuery. Is there a way I can ignore this validation for the purpose of this form only? or is there an easier/different way to display the filenames?
Some comments:
If you only want to edit the descriptions you should not include as a form field the archive_file field.
You could instead pass in your view the instance of the form to the context of the request. And then interpolate the title of the file in the template.
If you could provide your view code, we can discuss an actual implementation.
UPDATE:
Looking at the source code of model form, you hava always available the instance of the object of the form. why don't you try using that?
As in:
# template
{% for archive_form in archive_formset %}
{{ archive_form.instance.archive_file.filename }}
{{ archive_form.description }}
{% endfor %}
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.
Take this example form.
class TestForm(forms.Form):
def __init__(self, *args, **kwargs):
super(TestForm, self).__init__(*args, **kwargs)
self.fields["test"] = forms.CharField()
In my view, I have this code:
test_form = TestForm()
context['test_form'] = test_form
If I print(test_form), it will appear just fine. Now, when I use {% debug %} in my template, this is the output: { ... 'test_form': , ... }. In other words, the form disappears. I suspect it has something to do with adding a field after initialization, but I would really like to know why? Also how to get around the seeming limitation, if possible.
To test if your form has disappeared, you can try displaying it:
{% for field in test_form %}
{{ field }}
{% endfor %}
This should display the each field of the form as HTML. Hope it helps!
Your contact variable hasn't disappeared: it's right there, in the output you show us. If it had disappeared, you wouldn't see the test_form key in that dictionary.
As to why you don't see a value for that key, that's simply because the default representation of a python object is like this <TestForm object at ....>, and your browser is interpreting the angle brackets as an unknown HTML tag which it ignores.
Put your debug tag within <pre>...</pre> tags and you'll see it just fine.
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)
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.+/>)'.