Dynamically add fields to Form objects in Django

I tried the content written in the digression of Dynamic specification of queryset of ModelChoiceField in Django, so memo memo.

Last homework

By playing with Form.fields, you can change various attributes of other fields after the form is generated. Is it possible to add / remove the field itself?

I actually tried it

Launch Django's shell command and try it on an interactive shell

Create an empty Form object

>>> from django import forms
>>> f = forms.Form()
>>> f.as_p()
''
>>> f.fields
OrderedDict()

No fields are defined at this point, so calling ʻas_p ()does not render anything Form.fields only shows empty ʻOrderdDict objects

… For the first time, we know that the type of Form.fields is a class called OrderedDict.

Add fields to Form.fields

>>> f.fields['name'] = forms.CharField(label='name')
>>> f.as_p()
'<p><label for="id_name">name:</label> <input id="id_name" name="name" type="text" /></p>'

>>> f.fields
OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000045E5B388>)])

Add CharField with the key'name'. Rendered properly with ʻas_p (). The contents of Form.fields also contain the object of CharField` with the key'name'.

Calling ʻis_valid () and checking ʻerrors, cleaned_data

>>> f.is_valid()
Flase

>>> f.errors
{}

>>> f.cleaned_data
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Form' object has no attribute 'cleaned_data'

Since the added name field is required = True (default value), it is expected that ʻis_valid ()will be False, but for some reasonForm.errors` is empty.

When I looked it up, if the Form object didn't change the value of each field, or didn't receive a data as an argument, it would just create an empty ʻerrors and handle full_clean () Seems to finish (cleaned_data` is not generated either).

There is no help for it, so pass the data argument to the Form object and regenerate it. I'll give it a try.

>>> f = forms.Form(data={})
>>> f.fields['name'] = forms.CharField(label='name')
>>> f.is_valid()
False

>>> f.errors
{'name': ['This field is required.']}

>>> f.cleaned_data
{}

As expected, the content of ʻerrorscontained a mandatory check error for'name'. An empty dict is also generated forcleaned_data`.

Detour: Behavior of ʻis_valid () and ʻerrors

At first, I tried to directly assign an empty dict to Form.data to deal with the problem of ↑, but the result did not change. When I followed the source of django, it was because of the following processing at the beginning of __init __ () of the BaseForm class, which is the parent of the Form class.

forms.py


        self.is_bound = data is not None or files is not None

So, the implementation of ʻis_valid` is only one line below

forms.py


    def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
        return self.is_bound and not bool(self.errors)

In addition, errors are defined as methods with property decorators rather than fields.

forms.py


    @property
    def errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors

So, you couldn't just edit data directly, you had to do the following two things.

Well, if you think about using it as a normal web application, you shouldn't have to change the parameters and redo the validation while processing one request ... It is natural that validation (call of full_clean ()) is not executed every time ʻis_valid () or ʻerrors is accessed, but only the first time.

Change data so that validation is True

>>> f.data = {'name':'hoge'}
>>> f.full_clean()
>>> f.is_valid()
True

>>> f.errors
{}

>>> f.cleaned_data
{'name':'hoge'}

Try adding more fields

>>> f.fields['date'] = forms.DateField(label='date',required=False)

>>> f.as_p()
'<ul class="errorlist"><li>This field is required.</li></ul>\n<p><label for="id_name">name:</label> <input id="id_name" name="name" type="text" /></p>\n<p><label for="id_date">date:</label> <input id="id_date" name="date" type="text" /></p>'

>>> f.fields
OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000045E5B38>), ('date', <django.forms.fields.DateField object at 0x0000000002A3C828>)])

>>> f.full_clean()
>>> f.is_valid()
True

>>> f.errors
{}

>>> f.cleaned_data
{'date':None, 'name':'hoge'}

>>> f.data['date'] = '2014-12-04'
>>> f.full_clean()
>>> f.is_valid()
True

>>> f.errors
{}

>>> f.cleaned_data
{'date':datetime.date(2014, 12, 4), 'name':'hoge'}

Remove fields from Form.fields

>>> f.fields.popitem()
('date', <django.forms.fields.DateField object at 0x0000000002A3C828>)

>>> f.as_p()
'<p><label for="id_name">name:</label> <input id="id_name" name="name" type="text" value="hoge" /></p>'

>>> f.fields
OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000045E5B38>)])

>>> f.full_clean()
>>> f.cleaned_data
{'name': 'hoge'}

>>> f.data
{'date': '2014-12-04', 'name': 'hoge'}

Form.fields deletes the item with ʻOrderedDict.popitem. If the argument last` is True (default), it will be LIFO, and if it is False, it will be LILO. This time it's True, so the date field I added later was removed.

Summary

So, Django's Form object can be added / removed later in the field itself. I think it can respond to requests such as "Allow you to freely add screen input items in the master settings!"

Recommended Posts

Dynamically add fields to Form objects in Django
Dynamically add form fields in Django
How to get multiple model objects randomly in Django
Set the form DateField to type = date in Django
Implementing Add Form Buttons in Django Inline Forms Set Part2
Implement an add form button in your Django inline form set
[Django] How to test Form [TDD]
Set placeholders in input fields in Django
Add fields to features with ArcPy
Errors related to memcached in django
How to reflect CSS in Django
6 ways to string objects in Python
How to dynamically define variables in Python
How to delete expired sessions in Django
Minimum knowledge to use Form in Flask
In Jupyter, add IPerl to the kernel.
Dynamically specify a ModelChoiceField queryset in Django
How to dynamically zero pad in Python
How to do Server-Sent Events in Django
How to convert DateTimeField format in Django
[Django memo] I want to set the login user information in the form in advance
Allow HTML5 <input type = "date / time"> to be used in DatetimeField form in Django
How to add pre-save processing when adding objects on the Django admin site
Try using django-import-export to add csv data to django
How to implement Rails helper-like functionality in Django
I want to pin Datetime.now in Django tests
Save multiple models in one form with Django
How to reflect ImageField in Django + Docker (pillow)
How to run some script regularly in Django
To dynamically replace the next method in python
To add a module to python put in Julialang
Pass login user information to view in Django
I implemented Google's Speech to text in Django
How to create a Rest Api in Django
Add functionality to modify Django day shift information
Add totals to rows and columns in pandas
Django Contact Form 2
Models in Django
Django Suggested Form
I want to get an error message in Japanese with django Password Change Form
Django Form Gleaning
Forms in Django
Django contact form
[Python] [Django] How to use ChoiceField and how to add options
Add auto-completion to EV3 Micropyhon programming in VS Code
Add mail form to serverless web application ~ Backend version ~
How to use bootstrap in Django generic class view
TemplateView patterns you'll want to learn first in Django
Preparing to script control Rhino objects in Grasshopper / Python
How to add page numbers to PDF files (in Python)
Django / Related objects (add, create, remove, clear, set) Summary
Dynamically create tables in schema with Django, dynamically generate models
How to upload files in Django generic class view
How to use Decorator in Django and how to make it
How to add color bars in matplotlib bar plot
How to reference static files in a Django project
[Django] A story about getting stuck in a swamp trying to validate a zip with form [TDD]