I understand form the documentation http://docs.djangoproject.com/en/dev/ref/models/fields/ that you can add error_messages to a model field and supply your own dict of error messages. However, what are they keys of the dict you are supposed to pass?
class MyModel(models.Model):
some_field = models.CharField(max_length=55, error_messages={'required': "My custom error"})
If it is easier to do this on the modelform that is used that would also work, however. I would rather not have to create explicitly creating each field and their type again. This is what I was trying to avoid:
class MyModelForm(forms.ModelForm):
some_field = forms.CharField(error_messages={'required' : 'Required error'})
Update 2: Test code used in my project
My Model:
class MyTestModel(models.Model):
name = models.CharField(max_length=127,error_messages={'blank' : 'BLANK','required' : 'REQUIRED'})
My Form:
class EditTestModel(ModelForm):
class Meta:
model = MyTestModel
My View:
tf = EditTestModel({'name' : ''})
print tf.is_valid() # prints False
print tf.full_clean() # prints None
print tf # prints the form, with a <li> error list containg the error "This field is required"
<tr><th><label for="id_name">Name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input id="id_name" type="text" name="name" maxlength="127" /></td></tr>
ModelForm
¶
-
class
ModelForm
¶
If you’re building a database-driven app, chances are you’ll have forms that
map closely to Django models. For instance, you might have a BlogComment
model, and you want to create a form that lets people submit comments. In this
case, it would be redundant to define the field types in your form, because
you’ve already defined the fields in your model.
For this reason, Django provides a helper class that lets you create a Form
class from a Django model.
For example:
>>> from django.forms import ModelForm >>> from myapp.models import Article # Create the form class. >>> class ArticleForm(ModelForm): ... class Meta: ... model = Article ... fields = ['pub_date', 'headline', 'content', 'reporter'] # Creating a form to add an article. >>> form = ArticleForm() # Creating a form to change an existing article. >>> article = Article.objects.get(pk=1) >>> form = ArticleForm(instance=article)
Field types¶
The generated Form
class will have a form field for every model field
specified, in the order specified in the fields
attribute.
Each model field has a corresponding default form field. For example, a
CharField
on a model is represented as a CharField
on a form. A model
ManyToManyField
is represented as a MultipleChoiceField
. Here is the
full list of conversions:
Model field | Form field |
---|---|
AutoField |
Not represented in the form |
BigAutoField |
Not represented in the form |
BigIntegerField |
IntegerField withmin_value set to -9223372036854775808and max_value set to 9223372036854775807. |
BinaryField |
CharField , ifeditable is set toTrue on the model field, otherwise notrepresented in the form. |
BooleanField |
BooleanField , orNullBooleanField ifnull=True . |
CharField |
CharField withmax_length set to the model field’smax_length andempty_value set to None if null=True . |
DateField |
DateField |
DateTimeField |
DateTimeField |
DecimalField |
DecimalField |
DurationField |
DurationField |
EmailField |
EmailField |
FileField |
FileField |
FilePathField |
FilePathField |
FloatField |
FloatField |
ForeignKey |
ModelChoiceField (see below) |
ImageField |
ImageField |
IntegerField |
IntegerField |
IPAddressField |
IPAddressField |
GenericIPAddressField |
GenericIPAddressField |
JSONField |
JSONField |
ManyToManyField |
ModelMultipleChoiceField (see below) |
PositiveBigIntegerField |
IntegerField |
PositiveIntegerField |
IntegerField |
PositiveSmallIntegerField |
IntegerField |
SlugField |
SlugField |
SmallAutoField |
Not represented in the form |
SmallIntegerField |
IntegerField |
TextField |
CharField withwidget=forms.Textarea |
TimeField |
TimeField |
URLField |
URLField |
UUIDField |
UUIDField |
As you might expect, the ForeignKey
and ManyToManyField
model field
types are special cases:
ForeignKey
is represented bydjango.forms.ModelChoiceField
,
which is aChoiceField
whose choices are a modelQuerySet
.ManyToManyField
is represented by
django.forms.ModelMultipleChoiceField
, which is a
MultipleChoiceField
whose choices are a modelQuerySet
.
In addition, each generated form field has attributes set as follows:
- If the model field has
blank=True
, thenrequired
is set to
False
on the form field. Otherwise,required=True
. - The form field’s
label
is set to theverbose_name
of the model
field, with the first character capitalized. - The form field’s
help_text
is set to thehelp_text
of the model
field. - If the model field has
choices
set, then the form field’swidget
will be set toSelect
, with choices coming from the model field’s
choices
. The choices will normally include the blank choice which is
selected by default. If the field is required, this forces the user to
make a selection. The blank choice will not be included if the model
field hasblank=False
and an explicitdefault
value (the
default
value will be initially selected instead).
Finally, note that you can override the form field used for a given model
field. See Overriding the default fields below.
A full example¶
Consider this set of models:
from django.db import models from django.forms import ModelForm TITLE_CHOICES = [ ('MR', 'Mr.'), ('MRS', 'Mrs.'), ('MS', 'Ms.'), ] class Author(models.Model): name = models.CharField(max_length=100) title = models.CharField(max_length=3, choices=TITLE_CHOICES) birth_date = models.DateField(blank=True, null=True) def __str__(self): return self.name class Book(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Author) class AuthorForm(ModelForm): class Meta: model = Author fields = ['name', 'title', 'birth_date'] class BookForm(ModelForm): class Meta: model = Book fields = ['name', 'authors']
With these models, the ModelForm
subclasses above would be roughly
equivalent to this (the only difference being the save()
method, which
we’ll discuss in a moment.):
from django import forms class AuthorForm(forms.Form): name = forms.CharField(max_length=100) title = forms.CharField( max_length=3, widget=forms.Select(choices=TITLE_CHOICES), ) birth_date = forms.DateField(required=False) class BookForm(forms.Form): name = forms.CharField(max_length=100) authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
Validation on a ModelForm
¶
There are two main steps involved in validating a ModelForm
:
- Validating the form
- Validating the model instance
Just like normal form validation, model form validation is triggered implicitly
when calling is_valid()
or accessing the
errors
attribute and explicitly when calling
full_clean()
, although you will typically not use the latter method in
practice.
Model
validation (Model.full_clean()
) is triggered from within the form
validation step, right after the form’s clean()
method is called.
Warning
The cleaning process modifies the model instance passed to the
ModelForm
constructor in various ways. For instance, any date fields on
the model are converted into actual date objects. Failed validation may
leave the underlying model instance in an inconsistent state and therefore
it’s not recommended to reuse it.
Overriding the clean()
method¶
You can override the clean()
method on a model form to provide additional
validation in the same way you can on a normal form.
A model form instance attached to a model object will contain an instance
attribute that gives its methods access to that specific model instance.
Warning
The ModelForm.clean()
method sets a flag that makes the model
validation step validate the uniqueness of model
fields that are marked as unique
, unique_together
or
unique_for_date|month|year
.
If you would like to override the clean()
method and maintain this
validation, you must call the parent class’s clean()
method.
Interaction with model validation¶
As part of the validation process, ModelForm
will call the clean()
method of each field on your model that has a corresponding field on your form.
If you have excluded any model fields, validation will not be run on those
fields. See the form validation documentation
for more on how field cleaning and validation work.
The model’s clean()
method will be called before any uniqueness checks are
made. See Validating objects for more information
on the model’s clean()
hook.
Considerations regarding model’s error_messages
¶
Error messages defined at the
form field
level or at the
form Meta level always take
precedence over the error messages defined at the
model field
level.
Error messages defined on model fields
are only used when the
ValidationError
is raised during the model validation step and no corresponding error messages are defined at
the form level.
You can override the error messages from NON_FIELD_ERRORS
raised by model
validation by adding the NON_FIELD_ERRORS
key
to the error_messages
dictionary of the ModelForm
’s inner Meta
class:
from django.core.exceptions import NON_FIELD_ERRORS from django.forms import ModelForm class ArticleForm(ModelForm): class Meta: error_messages = { NON_FIELD_ERRORS: { 'unique_together': "%(model_name)s's %(field_labels)s are not unique.", } }
The save()
method¶
Every ModelForm
also has a save()
method. This method creates and saves
a database object from the data bound to the form. A subclass of ModelForm
can accept an existing model instance as the keyword argument instance
; if
this is supplied, save()
will update that instance. If it’s not supplied,
save()
will create a new instance of the specified model:
>>> from myapp.models import Article >>> from myapp.forms import ArticleForm # Create a form instance from POST data. >>> f = ArticleForm(request.POST) # Save a new Article object from the form's data. >>> new_article = f.save() # Create a form to edit an existing Article, but use # POST data to populate the form. >>> a = Article.objects.get(pk=1) >>> f = ArticleForm(request.POST, instance=a) >>> f.save()
Note that if the form hasn’t been validated, calling save()
will do so by checking
form.errors
. A ValueError
will be raised if the data in the form
doesn’t validate – i.e., if form.errors
evaluates to True
.
If an optional field doesn’t appear in the form’s data, the resulting model
instance uses the model field default
, if
there is one, for that field. This behavior doesn’t apply to fields that use
CheckboxInput
,
CheckboxSelectMultiple
, or
SelectMultiple
(or any custom widget whose
value_omitted_from_data()
method always returns
False
) since an unchecked checkbox and unselected <select multiple>
don’t appear in the data of an HTML form submission. Use a custom form field or
widget if you’re designing an API and want the default fallback behavior for a
field that uses one of these widgets.
This save()
method accepts an optional commit
keyword argument, which
accepts either True
or False
. If you call save()
with
commit=False
, then it will return an object that hasn’t yet been saved to
the database. In this case, it’s up to you to call save()
on the resulting
model instance. This is useful if you want to do custom processing on the
object before saving it, or if you want to use one of the specialized
model saving options. commit
is True
by default.
Another side effect of using commit=False
is seen when your model has
a many-to-many relation with another model. If your model has a many-to-many
relation and you specify commit=False
when you save a form, Django cannot
immediately save the form data for the many-to-many relation. This is because
it isn’t possible to save many-to-many data for an instance until the instance
exists in the database.
To work around this problem, every time you save a form using commit=False
,
Django adds a save_m2m()
method to your ModelForm
subclass. After
you’ve manually saved the instance produced by the form, you can invoke
save_m2m()
to save the many-to-many form data. For example:
# Create a form instance with POST data. >>> f = AuthorForm(request.POST) # Create, but don't save the new author instance. >>> new_author = f.save(commit=False) # Modify the author in some way. >>> new_author.some_field = 'some_value' # Save the new instance. >>> new_author.save() # Now, save the many-to-many data for the form. >>> f.save_m2m()
Calling save_m2m()
is only required if you use save(commit=False)
.
When you use a save()
on a form, all data – including many-to-many data –
is saved without the need for any additional method calls. For example:
# Create a form instance with POST data. >>> a = Author() >>> f = AuthorForm(request.POST, instance=a) # Create and save the new author instance. There's no need to do anything else. >>> new_author = f.save()
Other than the save()
and save_m2m()
methods, a ModelForm
works
exactly the same way as any other forms
form. For example, the
is_valid()
method is used to check for validity, the is_multipart()
method is used to determine whether a form requires multipart file upload (and
hence whether request.FILES
must be passed to the form), etc. See
Binding uploaded files to a form for more information.
Selecting the fields to use¶
It is strongly recommended that you explicitly set all fields that should be
edited in the form using the fields
attribute. Failure to do so can easily
lead to security problems when a form unexpectedly allows a user to set certain
fields, especially when new fields are added to a model. Depending on how the
form is rendered, the problem may not even be visible on the web page.
The alternative approach would be to include all fields automatically, or
remove only some. This fundamental approach is known to be much less secure
and has led to serious exploits on major websites (e.g. GitHub).
There are, however, two shortcuts available for cases where you can guarantee
these security concerns do not apply to you:
-
Set the
fields
attribute to the special value'__all__'
to indicate
that all fields in the model should be used. For example:from django.forms import ModelForm class AuthorForm(ModelForm): class Meta: model = Author fields = '__all__'
-
Set the
exclude
attribute of theModelForm
’s innerMeta
class to
a list of fields to be excluded from the form.For example:
class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ['title']
Since the
Author
model has the 3 fieldsname
,title
and
birth_date
, this will result in the fieldsname
andbirth_date
being present on the form.
If either of these are used, the order the fields appear in the form will be the
order the fields are defined in the model, with ManyToManyField
instances
appearing last.
In addition, Django applies the following rule: if you set editable=False
on
the model field, any form created from the model via ModelForm
will not
include that field.
Note
Any fields not included in a form by the above logic
will not be set by the form’s save()
method. Also, if you
manually add the excluded fields back to the form, they will not
be initialized from the model instance.
Django will prevent any attempt to save an incomplete model, so if
the model does not allow the missing fields to be empty, and does
not provide a default value for the missing fields, any attempt to
save()
a ModelForm
with missing fields will fail. To
avoid this failure, you must instantiate your model with initial
values for the missing, but required fields:
author = Author(title='Mr') form = PartialAuthorForm(request.POST, instance=author) form.save()
Alternatively, you can use save(commit=False)
and manually set
any extra required fields:
form = PartialAuthorForm(request.POST) author = form.save(commit=False) author.title = 'Mr' author.save()
See the section on saving forms for more details on using
save(commit=False)
.
Overriding the default fields¶
The default field types, as described in the Field types table above, are
sensible defaults. If you have a DateField
in your model, chances are you’d
want that to be represented as a DateField
in your form. But ModelForm
gives you the flexibility of changing the form field for a given model.
To specify a custom widget for a field, use the widgets
attribute of the
inner Meta
class. This should be a dictionary mapping field names to widget
classes or instances.
For example, if you want the CharField
for the name
attribute of
Author
to be represented by a <textarea>
instead of its default
<input type="text">
, you can override the field’s widget:
from django.forms import ModelForm, Textarea from myapp.models import Author class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') widgets = { 'name': Textarea(attrs={'cols': 80, 'rows': 20}), }
The widgets
dictionary accepts either widget instances (e.g.,
Textarea(...)
) or classes (e.g., Textarea
). Note that the widgets
dictionary is ignored for a model field with a non-empty choices
attribute.
In this case, you must override the form field to use a different widget.
Similarly, you can specify the labels
, help_texts
and error_messages
attributes of the inner Meta
class if you want to further customize a field.
For example if you wanted to customize the wording of all user facing strings for
the name
field:
from django.utils.translation import gettext_lazy as _ class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') labels = { 'name': _('Writer'), } help_texts = { 'name': _('Some useful help text.'), } error_messages = { 'name': { 'max_length': _("This writer's name is too long."), }, }
You can also specify field_classes
to customize the type of fields
instantiated by the form.
For example, if you wanted to use MySlugFormField
for the slug
field, you could do the following:
from django.forms import ModelForm from myapp.models import Article class ArticleForm(ModelForm): class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter', 'slug'] field_classes = { 'slug': MySlugFormField, }
Finally, if you want complete control over of a field – including its type,
validators, required, etc. – you can do this by declaratively specifying
fields like you would in a regular Form
.
If you want to specify a field’s validators, you can do so by defining
the field declaratively and setting its validators
parameter:
from django.forms import CharField, ModelForm from myapp.models import Article class ArticleForm(ModelForm): slug = CharField(validators=[validate_slug]) class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter', 'slug']
Note
When you explicitly instantiate a form field like this, it is important to
understand how ModelForm
and regular Form
are related.
ModelForm
is a regular Form
which can automatically generate
certain fields. The fields that are automatically generated depend on
the content of the Meta
class and on which fields have already been
defined declaratively. Basically, ModelForm
will only generate fields
that are missing from the form, or in other words, fields that weren’t
defined declaratively.
Fields defined declaratively are left as-is, therefore any customizations
made to Meta
attributes such as widgets
, labels
, help_texts
,
or error_messages
are ignored; these only apply to fields that are
generated automatically.
Similarly, fields defined declaratively do not draw their attributes like
max_length
or required
from the corresponding model. If you want to
maintain the behavior specified in the model, you must set the relevant
arguments explicitly when declaring the form field.
For example, if the Article
model looks like this:
class Article(models.Model): headline = models.CharField( max_length=200, null=True, blank=True, help_text='Use puns liberally', ) content = models.TextField()
and you want to do some custom validation for headline
, while keeping
the blank
and help_text
values as specified, you might define
ArticleForm
like this:
class ArticleForm(ModelForm): headline = MyFormField( max_length=200, required=False, help_text='Use puns liberally', ) class Meta: model = Article fields = ['headline', 'content']
You must ensure that the type of the form field can be used to set the
contents of the corresponding model field. When they are not compatible,
you will get a ValueError
as no implicit conversion takes place.
See the form field documentation for more information
on fields and their arguments.
Enabling localization of fields¶
By default, the fields in a ModelForm
will not localize their data. To
enable localization for fields, you can use the localized_fields
attribute on the Meta
class.
>>> from django.forms import ModelForm >>> from myapp.models import Author >>> class AuthorForm(ModelForm): ... class Meta: ... model = Author ... localized_fields = ('birth_date',)
If localized_fields
is set to the special value '__all__'
, all fields
will be localized.
Form inheritance¶
As with basic forms, you can extend and reuse ModelForms
by inheriting
them. This is useful if you need to declare extra fields or extra methods on a
parent class for use in a number of forms derived from models. For example,
using the previous ArticleForm
class:
>>> class EnhancedArticleForm(ArticleForm): ... def clean_pub_date(self): ... ...
This creates a form that behaves identically to ArticleForm
, except there’s
some extra validation and cleaning for the pub_date
field.
You can also subclass the parent’s Meta
inner class if you want to change
the Meta.fields
or Meta.exclude
lists:
>>> class RestrictedArticleForm(EnhancedArticleForm): ... class Meta(ArticleForm.Meta): ... exclude = ('body',)
This adds the extra method from the EnhancedArticleForm
and modifies
the original ArticleForm.Meta
to remove one field.
There are a couple of things to note, however.
-
Normal Python name resolution rules apply. If you have multiple base
classes that declare aMeta
inner class, only the first one will be
used. This means the child’sMeta
, if it exists, otherwise the
Meta
of the first parent, etc. -
It’s possible to inherit from both
Form
andModelForm
simultaneously,
however, you must ensure thatModelForm
appears first in the MRO. This is
because these classes rely on different metaclasses and a class can only have
one metaclass. -
It’s possible to declaratively remove a
Field
inherited from a parent class by
setting the name to beNone
on the subclass.You can only use this technique to opt out from a field defined declaratively
by a parent class; it won’t prevent theModelForm
metaclass from generating
a default field. To opt-out from default fields, see
Selecting the fields to use.
Providing initial values¶
As with regular forms, it’s possible to specify initial data for forms by
specifying an initial
parameter when instantiating the form. Initial
values provided this way will override both initial values from the form field
and values from an attached model instance. For example:
>>> article = Article.objects.get(pk=1) >>> article.headline 'My headline' >>> form = ArticleForm(initial={'headline': 'Initial headline'}, instance=article) >>> form['headline'].value() 'Initial headline'
ModelForm factory function¶
You can create forms from a given model using the standalone function
modelform_factory()
, instead of using a class
definition. This may be more convenient if you do not have many customizations
to make:
>>> from django.forms import modelform_factory >>> from myapp.models import Book >>> BookForm = modelform_factory(Book, fields=("author", "title"))
This can also be used to make modifications to existing forms, for example by
specifying the widgets to be used for a given field:
>>> from django.forms import Textarea >>> Form = modelform_factory(Book, form=BookForm, ... widgets={"title": Textarea()})
The fields to include can be specified using the fields
and exclude
keyword arguments, or the corresponding attributes on the ModelForm
inner
Meta
class. Please see the ModelForm
Selecting the fields to use
documentation.
… or enable localization for specific fields:
>>> Form = modelform_factory(Author, form=AuthorForm, localized_fields=("birth_date",))
Model formsets¶
-
class
models.
BaseModelFormSet
¶
Like regular formsets, Django provides a couple
of enhanced formset classes to make working with Django models more
convenient. Let’s reuse the Author
model from above:
>>> from django.forms import modelformset_factory >>> from myapp.models import Author >>> AuthorFormSet = modelformset_factory(Author, fields=('name', 'title'))
Using fields
restricts the formset to use only the given fields.
Alternatively, you can take an “opt-out” approach, specifying which fields to
exclude:
>>> AuthorFormSet = modelformset_factory(Author, exclude=('birth_date',))
This will create a formset that is capable of working with the data associated
with the Author
model. It works just like a regular formset:
>>> formset = AuthorFormSet() >>> print(formset) <input type="hidden" name="form-TOTAL_FORMS" value="1" id="id_form-TOTAL_FORMS"><input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS"><input type="hidden" name="form-MIN_NUM_FORMS" value="0" id="id_form-MIN_NUM_FORMS"><input type="hidden" name="form-MAX_NUM_FORMS" value="1000" id="id_form-MAX_NUM_FORMS"> <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" maxlength="100"></td></tr> <tr><th><label for="id_form-0-title">Title:</label></th><td><select name="form-0-title" id="id_form-0-title"> <option value="" selected>---------</option> <option value="MR">Mr.</option> <option value="MRS">Mrs.</option> <option value="MS">Ms.</option> </select><input type="hidden" name="form-0-id" id="id_form-0-id"></td></tr>
Note
When using multi-table inheritance, forms
generated by a formset factory will contain a parent link field (by default
<parent_model_name>_ptr
) instead of an id
field.
Changing the queryset¶
By default, when you create a formset from a model, the formset will use a
queryset that includes all objects in the model (e.g.,
Author.objects.all()
). You can override this behavior by using the
queryset
argument:
>>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O'))
Alternatively, you can create a subclass that sets self.queryset
in
__init__
:
from django.forms import BaseModelFormSet from myapp.models import Author class BaseAuthorFormSet(BaseModelFormSet): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.queryset = Author.objects.filter(name__startswith='O')
Then, pass your BaseAuthorFormSet
class to the factory function:
>>> AuthorFormSet = modelformset_factory( ... Author, fields=('name', 'title'), formset=BaseAuthorFormSet)
If you want to return a formset that doesn’t include any preexisting
instances of the model, you can specify an empty QuerySet:
>>> AuthorFormSet(queryset=Author.objects.none())
Changing the form¶
By default, when you use modelformset_factory
, a model form will
be created using modelform_factory()
.
Often, it can be useful to specify a custom model form. For example,
you can create a custom model form that has custom validation:
class AuthorForm(forms.ModelForm): class Meta: model = Author fields = ('name', 'title') def clean_name(self): # custom validation for the name field ...
Then, pass your model form to the factory function:
AuthorFormSet = modelformset_factory(Author, form=AuthorForm)
It is not always necessary to define a custom model form. The
modelformset_factory
function has several arguments which are
passed through to modelform_factory
, which are described below.
Enabling localization for fields with localized_fields
¶
Using the localized_fields
parameter, you can enable localization for
fields in the form.
>>> AuthorFormSet = modelformset_factory( ... Author, fields=('name', 'title', 'birth_date'), ... localized_fields=('birth_date',))
If localized_fields
is set to the special value '__all__'
, all fields
will be localized.
Providing initial values¶
As with regular formsets, it’s possible to specify initial data for forms in the formset by specifying an initial
parameter when instantiating the model formset class returned by
modelformset_factory()
. However, with model
formsets, the initial values only apply to extra forms, those that aren’t
attached to an existing model instance. If the length of initial
exceeds
the number of extra forms, the excess initial data is ignored. If the extra
forms with initial data aren’t changed by the user, they won’t be validated or
saved.
Saving objects in the formset¶
As with a ModelForm
, you can save the data as a model object. This is done
with the formset’s save()
method:
# Create a formset instance with POST data. >>> formset = AuthorFormSet(request.POST) # Assuming all is valid, save the data. >>> instances = formset.save()
The save()
method returns the instances that have been saved to the
database. If a given instance’s data didn’t change in the bound data, the
instance won’t be saved to the database and won’t be included in the return
value (instances
, in the above example).
When fields are missing from the form (for example because they have been
excluded), these fields will not be set by the save()
method. You can find
more information about this restriction, which also holds for regular
ModelForms
, in Selecting the fields to use.
Pass commit=False
to return the unsaved model instances:
# don't save to the database >>> instances = formset.save(commit=False) >>> for instance in instances: ... # do something with instance ... instance.save()
This gives you the ability to attach data to the instances before saving them
to the database. If your formset contains a ManyToManyField
, you’ll also
need to call formset.save_m2m()
to ensure the many-to-many relationships
are saved properly.
After calling save()
, your model formset will have three new attributes
containing the formset’s changes:
-
models.BaseModelFormSet.
changed_objects
¶
-
models.BaseModelFormSet.
deleted_objects
¶
-
models.BaseModelFormSet.
new_objects
¶
Limiting the number of editable objects¶
As with regular formsets, you can use the max_num
and extra
parameters
to modelformset_factory()
to limit the number of
extra forms displayed.
max_num
does not prevent existing objects from being displayed:
>>> Author.objects.order_by('name') <QuerySet [<Author: Charles Baudelaire>, <Author: Paul Verlaine>, <Author: Walt Whitman>]> >>> AuthorFormSet = modelformset_factory(Author, fields=('name',), max_num=1) >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) >>> [x.name for x in formset.get_queryset()] ['Charles Baudelaire', 'Paul Verlaine', 'Walt Whitman']
Also, extra=0
doesn’t prevent creation of new model instances as you can
add additional forms with JavaScript
or send additional POST data. See Preventing new objects creation on how to do
this.
If the value of max_num
is greater than the number of existing related
objects, up to extra
additional blank forms will be added to the formset,
so long as the total number of forms does not exceed max_num
:
>>> AuthorFormSet = modelformset_factory(Author, fields=('name',), max_num=4, extra=2) >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) >>> for form in formset: ... print(form.as_table()) <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" value="Charles Baudelaire" maxlength="100"><input type="hidden" name="form-0-id" value="1" id="id_form-0-id"></td></tr> <tr><th><label for="id_form-1-name">Name:</label></th><td><input id="id_form-1-name" type="text" name="form-1-name" value="Paul Verlaine" maxlength="100"><input type="hidden" name="form-1-id" value="3" id="id_form-1-id"></td></tr> <tr><th><label for="id_form-2-name">Name:</label></th><td><input id="id_form-2-name" type="text" name="form-2-name" value="Walt Whitman" maxlength="100"><input type="hidden" name="form-2-id" value="2" id="id_form-2-id"></td></tr> <tr><th><label for="id_form-3-name">Name:</label></th><td><input id="id_form-3-name" type="text" name="form-3-name" maxlength="100"><input type="hidden" name="form-3-id" id="id_form-3-id"></td></tr>
A max_num
value of None
(the default) puts a high limit on the number
of forms displayed (1000). In practice this is equivalent to no limit.
Preventing new objects creation¶
New in Django 4.1.
Using the edit_only
parameter, you can prevent creation of any new
objects:
>>> AuthorFormSet = modelformset_factory( ... Author, ... fields=('name', 'title'), ... edit_only=True, ... )
Here, the formset will only edit existing Author
instances. No other
objects will be created or edited.
Using a model formset in a view¶
Model formsets are very similar to formsets. Let’s say we want to present a
formset to edit Author
model instances:
from django.forms import modelformset_factory from django.shortcuts import render from myapp.models import Author def manage_authors(request): AuthorFormSet = modelformset_factory(Author, fields=('name', 'title')) if request.method == 'POST': formset = AuthorFormSet(request.POST, request.FILES) if formset.is_valid(): formset.save() # do something. else: formset = AuthorFormSet() return render(request, 'manage_authors.html', {'formset': formset})
As you can see, the view logic of a model formset isn’t drastically different
than that of a “normal” formset. The only difference is that we call
formset.save()
to save the data into the database. (This was described
above, in Saving objects in the formset.)
Overriding clean()
on a ModelFormSet
¶
Just like with ModelForms
, by default the clean()
method of a
ModelFormSet
will validate that none of the items in the formset violate
the unique constraints on your model (either unique
, unique_together
or
unique_for_date|month|year
). If you want to override the clean()
method
on a ModelFormSet
and maintain this validation, you must call the parent
class’s clean
method:
from django.forms import BaseModelFormSet class MyModelFormSet(BaseModelFormSet): def clean(self): super().clean() # example custom validation across forms in the formset for form in self.forms: # your custom formset validation ...
Also note that by the time you reach this step, individual model instances
have already been created for each Form
. Modifying a value in
form.cleaned_data
is not sufficient to affect the saved value. If you wish
to modify a value in ModelFormSet.clean()
you must modify
form.instance
:
from django.forms import BaseModelFormSet class MyModelFormSet(BaseModelFormSet): def clean(self): super().clean() for form in self.forms: name = form.cleaned_data['name'].upper() form.cleaned_data['name'] = name # update the instance value. form.instance.name = name
Using a custom queryset¶
As stated earlier, you can override the default queryset used by the model
formset:
from django.forms import modelformset_factory from django.shortcuts import render from myapp.models import Author def manage_authors(request): AuthorFormSet = modelformset_factory(Author, fields=('name', 'title')) queryset = Author.objects.filter(name__startswith='O') if request.method == "POST": formset = AuthorFormSet( request.POST, request.FILES, queryset=queryset, ) if formset.is_valid(): formset.save() # Do something. else: formset = AuthorFormSet(queryset=queryset) return render(request, 'manage_authors.html', {'formset': formset})
Note that we pass the queryset
argument in both the POST
and GET
cases in this example.
Using the formset in the template¶
There are three ways to render a formset in a Django template.
First, you can let the formset do most of the work:
<form method="post"> {{ formset }} </form>
Second, you can manually render the formset, but let the form deal with
itself:
<form method="post"> {{ formset.management_form }} {% for form in formset %} {{ form }} {% endfor %} </form>
When you manually render the forms yourself, be sure to render the management
form as shown above. See the management form documentation.
Third, you can manually render each field:
<form method="post"> {{ formset.management_form }} {% for form in formset %} {% for field in form %} {{ field.label_tag }} {{ field }} {% endfor %} {% endfor %} </form>
If you opt to use this third method and you don’t iterate over the fields with
a {% for %}
loop, you’ll need to render the primary key field. For example,
if you were rendering the name
and age
fields of a model:
<form method="post"> {{ formset.management_form }} {% for form in formset %} {{ form.id }} <ul> <li>{{ form.name }}</li> <li>{{ form.age }}</li> </ul> {% endfor %} </form>
Notice how we need to explicitly render {{ form.id }}
. This ensures that
the model formset, in the POST
case, will work correctly. (This example
assumes a primary key named id
. If you’ve explicitly defined your own
primary key that isn’t called id
, make sure it gets rendered.)
Inline formsets¶
-
class
models.
BaseInlineFormSet
¶
Inline formsets is a small abstraction layer on top of model formsets. These
simplify the case of working with related objects via a foreign key. Suppose
you have these two models:
from django.db import models class Author(models.Model): name = models.CharField(max_length=100) class Book(models.Model): author = models.ForeignKey(Author, on_delete=models.CASCADE) title = models.CharField(max_length=100)
If you want to create a formset that allows you to edit books belonging to
a particular author, you could do this:
>>> from django.forms import inlineformset_factory >>> BookFormSet = inlineformset_factory(Author, Book, fields=('title',)) >>> author = Author.objects.get(name='Mike Royko') >>> formset = BookFormSet(instance=author)
BookFormSet
’s prefix is 'book_set'
(<model name>_set
). If Book
’s ForeignKey
to Author
has a
related_name
, that’s used instead.
Overriding methods on an InlineFormSet
¶
When overriding methods on InlineFormSet
, you should subclass
BaseInlineFormSet
rather than
BaseModelFormSet
.
For example, if you want to override clean()
:
from django.forms import BaseInlineFormSet class CustomInlineFormSet(BaseInlineFormSet): def clean(self): super().clean() # example custom validation across forms in the formset for form in self.forms: # your custom formset validation ...
See also Overriding clean() on a ModelFormSet.
Then when you create your inline formset, pass in the optional argument
formset
:
>>> from django.forms import inlineformset_factory >>> BookFormSet = inlineformset_factory(Author, Book, fields=('title',), ... formset=CustomInlineFormSet) >>> author = Author.objects.get(name='Mike Royko') >>> formset = BookFormSet(instance=author)
More than one foreign key to the same model¶
If your model contains more than one foreign key to the same model, you’ll
need to resolve the ambiguity manually using fk_name
. For example, consider
the following model:
class Friendship(models.Model): from_friend = models.ForeignKey( Friend, on_delete=models.CASCADE, related_name='from_friends', ) to_friend = models.ForeignKey( Friend, on_delete=models.CASCADE, related_name='friends', ) length_in_months = models.IntegerField()
To resolve this, you can use fk_name
to
inlineformset_factory()
:
>>> FriendshipFormSet = inlineformset_factory(Friend, Friendship, fk_name='from_friend', ... fields=('to_friend', 'length_in_months'))
Using an inline formset in a view¶
You may want to provide a view that allows a user to edit the related objects
of a model. Here’s how you can do that:
def manage_books(request, author_id): author = Author.objects.get(pk=author_id) BookInlineFormSet = inlineformset_factory(Author, Book, fields=('title',)) if request.method == "POST": formset = BookInlineFormSet(request.POST, request.FILES, instance=author) if formset.is_valid(): formset.save() # Do something. Should generally end with a redirect. For example: return HttpResponseRedirect(author.get_absolute_url()) else: formset = BookInlineFormSet(instance=author) return render(request, 'manage_books.html', {'formset': formset})
Notice how we pass instance
in both the POST
and GET
cases.
Built-in Field Validations in Django models are the validations that come predefined to all Django fields. Every field comes in with built-in validations from Django validators. One can also add more built-in field validations for applying or removing certain constraints on a particular field. error_messages attribute is used to modify error messages that are displayed in the admin interface during failure of some constraint.
For example, you can override “This field is required” message to your own message. It lets you override the default messages that the field will raise. Pass in a dictionary with keys matching the error messages you want to override. Error message keys include null, blank, invalid, invalid_choice, unique, and unique_for_date.
Syntax –
field_name = models.Field(error_messages = {"key": "message"})
Django Built-in Field Validation editable=False Explanation
Illustration of error_messages using an Example. Consider a project named geeksforgeeks having an app named geeks.
Refer to the following articles to check how to create a project and an app in Django.
- How to Create a Basic Project using MVT in Django?
- How to Create an App in Django ?
Enter the following code into models.py file of geeks app. We will be using CharField for experimenting for all field options.
Python3
from
django.db
import
models
from
django.db.models
import
Model
class
GeeksModel(Model):
geeks_field
=
models.CharField(
max_length
=
200
,
unique
=
True
)
After running makemigrations and migrate on Django and rendering the above model, let us create an instance from Django admin interface with string “a“. Now to break the constraint of unique=True, let us try to create one more instance of the model using same string. Now it will show this error.
Now let us modify this error message to “The Geeks Field you entered is not unique.” using error_messages. Change the models.py to
Python3
from
django.db
import
models
from
django.db.models
import
Model
class
GeeksModel(Model):
geeks_field
=
models.CharField(
max_length
=
200
,
unique
=
True
,
error_messages
=
{
"unique"
:
"The Geeks Field you entered is not unique."
}
)
Since models.py is modified run makemigrations and migrate again on the project. Open admin interface and try to make instance using the string “a” again.
You can see the modified error message. Therefore, error_messages modifies the field error messages. you can modify using other attributes such as null, blank, etc.
More Built-in Field Validations
.math-table { border-collapse: collapse; width: 100%; } .math-table td { border: 1px solid #5fb962; text-align: left !important; padding: 8px; } .math-table th { border: 1px solid #5fb962; padding: 8px; } .math-table tr>th{ background-color: #c6ebd9; vertical-align: middle; } .math-table tr:nth-child(odd) { background-color: #ffffff; }
Field Options | Description |
---|---|
Null | If True, Django will store empty values as NULL in the database. Default is False. |
Blank | If True, the field is allowed to be blank. Default is False. |
db_column | The name of the database column to use for this field. If this isn’t given, Django will use the field’s name. |
Default | The default value for the field. This can be a value or a callable object. If callable it will be called every time a new object is created. |
help_text | Extra “help” text to be displayed with the form widget. It’s useful for documentation even if your field isn’t used on a form. |
primary_key | If True, this field is the primary key for the model. |
editable | If False, the field will not be displayed in the admin or any other ModelForm. They are also skipped during model validation. Default is True. |
error_messages | The error_messages argument lets you override the default messages that the field will raise. Pass in a dictionary with keys matching the error messages you want to override. |
help_text | Extra “help” text to be displayed with the form widget. It’s useful for documentation even if your field isn’t used on a form. |
verbose_name | A human-readable name for the field. If the verbose name isn’t given, Django will automatically create it using the field’s attribute name, converting underscores to spaces. |
validators | A list of validators to run for this field. See the validators documentation for more information. |
Unique | If True, this field must be unique throughout the table. |
ModelForm
¶
-
class
ModelForm
[исходный код]¶
Если вы создаете приложение, управляемое базой данных, то, скорее всего, у вас будут формы, которые тесно связаны с моделями Django. Например, у вас может быть модель BlogComment
, и вы хотите создать форму, позволяющую людям оставлять комментарии. В этом случае было бы излишним определять типы полей в форме, потому что вы уже определили поля в модели.
По этой причине Django предоставляет вспомогательный класс, который позволяет вам создать класс Form
из модели Django.
Например:
>>> from django.forms import ModelForm >>> from myapp.models import Article # Create the form class. >>> class ArticleForm(ModelForm): ... class Meta: ... model = Article ... fields = ['pub_date', 'headline', 'content', 'reporter'] # Creating a form to add an article. >>> form = ArticleForm() # Creating a form to change an existing article. >>> article = Article.objects.get(pk=1) >>> form = ArticleForm(instance=article)
Типы полей¶
Созданный класс Form
будет иметь поле формы для каждого указанного поля модели, в порядке, указанном в атрибуте fields
.
Каждое поле модели имеет соответствующее поле формы по умолчанию. Например, CharField
в модели представляется как CharField
в форме. Поле модели ManyToManyField
представляется как MultipleChoiceField
. Вот полный список преобразований:
Модельное поле | Поле формы |
---|---|
AutoField |
Не представлены в форме |
BigAutoField |
Не представлены в форме |
BigIntegerField |
IntegerField с min_value , установленным на -9223372036854775808 и max_value , установленным на 9223372036854775807. |
BinaryField |
CharField , если editable установлен в True на поле модели, иначе не представлен в форме. |
BooleanField |
BooleanField , или NullBooleanField , если null=True . |
CharField |
CharField с max_length , установленным на max_length модельного поля, и empty_value , установленным на None , если null=True . |
DateField |
DateField |
DateTimeField |
DateTimeField |
DecimalField |
DecimalField |
DurationField |
DurationField |
EmailField |
EmailField |
FileField |
FileField |
FilePathField |
FilePathField |
FloatField |
FloatField |
ForeignKey |
ModelChoiceField (см. ниже) |
ImageField |
ImageField |
IntegerField |
IntegerField |
IPAddressField |
IPAddressField |
GenericIPAddressField |
GenericIPAddressField |
JSONField |
JSONField |
ManyToManyField |
ModelMultipleChoiceField (см. ниже) |
PositiveBigIntegerField |
IntegerField |
PositiveIntegerField |
IntegerField |
PositiveSmallIntegerField |
IntegerField |
SlugField |
SlugField |
SmallAutoField |
Не представлены в форме |
SmallIntegerField |
IntegerField |
TextField |
CharField с widget=forms.Textarea |
TimeField |
TimeField |
URLField |
URLField |
UUIDField |
UUIDField |
Как и следовало ожидать, типы полей модели ForeignKey
и ManyToManyField
являются особыми случаями:
ForeignKey
представленdjango.forms.ModelChoiceField
, который являетсяChoiceField
, выбор которого представляет собой модельQuerySet
.ManyToManyField
представленdjango.forms.ModelMultipleChoiceField
, который являетсяMultipleChoiceField
, выбор которого представляет собой модельQuerySet
.
Кроме того, каждое сгенерированное поле формы имеет атрибуты, установленные следующим образом:
- Если поле модели имеет
blank=True
, тоrequired
устанавливается вFalse
на поле формы. В противном случаеrequired=True
. - Для поля формы
label
устанавливаетсяverbose_name
поля модели, причем первый символ пишется заглавными буквами. - Значение
help_text
поля формы устанавливается на значениеhelp_text
поля модели. - Если для поля модели установлено значение
choices
, то для поля формыwidget
будет установлено значениеSelect
, а выбор будет осуществляться из поля моделиchoices
. Варианты выбора обычно включают пустой вариант, который выбирается по умолчанию. Если поле является обязательным, это заставит пользователя сделать выбор. Пустой выбор не будет включен, если поле модели имеетblank=False
и явное значениеdefault
(вместо него будет изначально выбрано значениеdefault
).
Наконец, обратите внимание, что вы можете переопределить поле формы, используемое для данного поля модели. См. Overriding the default fields ниже.
Полный пример¶
Рассмотрим этот набор моделей:
from django.db import models from django.forms import ModelForm TITLE_CHOICES = [ ('MR', 'Mr.'), ('MRS', 'Mrs.'), ('MS', 'Ms.'), ] class Author(models.Model): name = models.CharField(max_length=100) title = models.CharField(max_length=3, choices=TITLE_CHOICES) birth_date = models.DateField(blank=True, null=True) def __str__(self): return self.name class Book(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Author) class AuthorForm(ModelForm): class Meta: model = Author fields = ['name', 'title', 'birth_date'] class BookForm(ModelForm): class Meta: model = Book fields = ['name', 'authors']
С этими моделями подклассы ModelForm
, приведенные выше, будут примерно эквивалентны этому (единственное отличие — метод save()
, который мы обсудим в ближайшее время):
from django import forms class AuthorForm(forms.Form): name = forms.CharField(max_length=100) title = forms.CharField( max_length=3, widget=forms.Select(choices=TITLE_CHOICES), ) birth_date = forms.DateField(required=False) class BookForm(forms.Form): name = forms.CharField(max_length=100) authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
Валидация на ModelForm
¶
Существует два основных этапа проверки ModelForm
:
- Validating the form
- Validating the model instance
Как и обычная проверка формы, проверка формы модели срабатывает неявно при вызове is_valid()
или обращении к атрибуту errors
и явно при вызове full_clean()
, хотя на практике вы обычно не используете последний метод.
Валидация Model
(Model.full_clean()
) запускается внутри шага валидации формы, сразу после вызова метода формы clean()
.
Предупреждение
Процесс очистки изменяет экземпляр модели, переданный конструктору ModelForm
, различными способами. Например, любые поля даты в модели преобразуются в реальные объекты даты. Неудачная валидация может оставить базовый экземпляр модели в противоречивом состоянии, поэтому не рекомендуется использовать его повторно.
Переопределение метода clean()
¶
Вы можете переопределить метод clean()
на форме модели, чтобы обеспечить дополнительную проверку так же, как и на обычной форме.
Экземпляр формы модели, присоединенный к объекту модели, будет содержать атрибут instance
, который предоставляет его методам доступ к этому конкретному экземпляру модели.
Предупреждение
Метод ModelForm.clean()
устанавливает флаг, который заставляет шаг model validation проверять уникальность полей модели, отмеченных как unique
, unique_together
или unique_for_date|month|year
.
Если вы хотите переопределить метод clean()
и сохранить эту валидацию, вы должны вызвать метод родительского класса clean()
.
Взаимодействие с валидацией модели¶
В процессе валидации ModelForm
вызовет метод clean()
каждого поля вашей модели, которое имеет соответствующее поле на вашей форме. Если вы исключили какие-либо поля модели, валидация не будет выполняться для этих полей. Подробнее о том, как работают очистка и валидация полей, см. документацию form validation.
Метод модели clean()
будет вызван до того, как будут произведены проверки на уникальность. Более подробную информацию о хуке модели Validating objects смотрите в clean()
.
Соображения относительно модели error_messages
¶
Сообщения об ошибках, определенные на уровне form field
или на уровне form Meta, всегда имеют приоритет над сообщениями об ошибках, определенными на уровне model field
.
Сообщения об ошибках, определенные на model fields
, используются только тогда, когда на шаге ValidationError
поднимается вопрос model validation и на уровне формы не определены соответствующие сообщения об ошибках.
Вы можете переопределить сообщения об ошибках NON_FIELD_ERRORS
, выдаваемые при валидации модели, добавив ключ NON_FIELD_ERRORS
в словарь error_messages
внутреннего ModelForm
класса Meta
:
from django.core.exceptions import NON_FIELD_ERRORS from django.forms import ModelForm class ArticleForm(ModelForm): class Meta: error_messages = { NON_FIELD_ERRORS: { 'unique_together': "%(model_name)s's %(field_labels)s are not unique.", } }
Метод save()
¶
Каждый ModelForm
также имеет метод save()
. Этот метод создает и сохраняет объект базы данных из данных, привязанных к форме. Подкласс ModelForm
может принимать существующий экземпляр модели в качестве аргумента ключевого слова instance
; если оно предоставлено, save()
обновит этот экземпляр. Если он не указан, save()
создаст новый экземпляр указанной модели:
>>> from myapp.models import Article >>> from myapp.forms import ArticleForm # Create a form instance from POST data. >>> f = ArticleForm(request.POST) # Save a new Article object from the form's data. >>> new_article = f.save() # Create a form to edit an existing Article, but use # POST data to populate the form. >>> a = Article.objects.get(pk=1) >>> f = ArticleForm(request.POST, instance=a) >>> f.save()
Обратите внимание, что если форма hasn’t been validated, вызов save()
сделает это, проверив form.errors
. Вызов ValueError
будет вызван, если данные в форме не подтвердятся — т.е. если form.errors
оценивается как True
.
Если опциональное поле не появляется в данных формы, результирующий экземпляр модели использует поле модели default
, если оно есть, для этого поля. Это поведение не относится к полям, использующим CheckboxInput
, CheckboxSelectMultiple
или SelectMultiple
(или к любому пользовательскому виджету, чей метод value_omitted_from_data()
всегда возвращает False
), поскольку не установленный флажок и не выбранный <select multiple>
не появляются в данных HTML-формы. Используйте пользовательское поле формы или виджет, если вы разрабатываете API и хотите, чтобы поле, использующее один из этих виджетов, возвращалось по умолчанию.
Этот метод save()
принимает необязательный аргумент ключевого слова commit
, который принимает либо True
, либо False
. Если вы вызовете save()
с commit=False
, то он вернет объект, который еще не был сохранен в базе данных. В этом случае вы сами должны вызвать save()
на полученном экземпляре модели. Это полезно, если вы хотите выполнить пользовательскую обработку объекта перед его сохранением, или если вы хотите использовать один из специализированных model saving options. По умолчанию commit
является True
.
Другой побочный эффект использования commit=False
проявляется, когда ваша модель имеет отношение «многие-ко-многим» с другой моделью. Если ваша модель имеет отношение «многие-ко-многим» и вы указываете commit=False
при сохранении формы, Django не может немедленно сохранить данные формы для отношения «многие-ко-многим». Это происходит потому, что невозможно сохранить данные отношения «многие-ко-многим» для экземпляра, пока этот экземпляр не существует в базе данных.
Чтобы обойти эту проблему, каждый раз, когда вы сохраняете форму с помощью commit=False
, Django добавляет метод save_m2m()
к вашему подклассу ModelForm
. После того, как вы вручную сохранили экземпляр, созданный формой, вы можете вызвать save_m2m()
для сохранения данных формы «многие-ко-многим». Например:
# Create a form instance with POST data. >>> f = AuthorForm(request.POST) # Create, but don't save the new author instance. >>> new_author = f.save(commit=False) # Modify the author in some way. >>> new_author.some_field = 'some_value' # Save the new instance. >>> new_author.save() # Now, save the many-to-many data for the form. >>> f.save_m2m()
Вызов save_m2m()
требуется только в том случае, если вы используете save(commit=False)
. Когда вы используете save()
на форме, все данные, включая данные «многие ко многим», сохраняются без необходимости дополнительных вызовов методов. Например:
# Create a form instance with POST data. >>> a = Author() >>> f = AuthorForm(request.POST, instance=a) # Create and save the new author instance. There's no need to do anything else. >>> new_author = f.save()
За исключением методов save()
и save_m2m()
, ModelForm
работает точно так же, как и любая другая forms
форма. Например, метод is_valid()
используется для проверки валидности, метод is_multipart()
используется для определения того, требует ли форма многокомпонентной загрузки файла (и, следовательно, нужно ли передавать форме request.FILES
), и т.д. Более подробную информацию смотрите в Привязка загруженных файлов к форме.
Выбор полей для использования¶
Настоятельно рекомендуется явно задавать все поля, которые должны редактироваться в форме, с помощью атрибута fields
. Невыполнение этого требования может легко привести к проблемам безопасности, когда форма неожиданно позволит пользователю установить определенные поля, особенно когда в модель добавляются новые поля. В зависимости от того, как отображается форма, проблема может быть даже не видна на веб-странице.
Альтернативным подходом было бы автоматическое включение всех полей или удаление только некоторых. Этот фундаментальный подход, как известно, гораздо менее безопасен и привел к серьезным эксплойтам на крупных сайтах (например, GitHub).
Тем не менее, есть два коротких пути для случаев, когда вы можете гарантировать, что эти проблемы безопасности к вам не относятся:
-
Установите атрибут
fields
в специальное значение'__all__'
, чтобы указать, что все поля в модели должны быть использованы. Например:from django.forms import ModelForm class AuthorForm(ModelForm): class Meta: model = Author fields = '__all__'
-
Установите атрибут
exclude
внутреннегоModelForm
классаMeta
в список полей, которые необходимо исключить из формы.Например:
class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ['title']
Поскольку модель
Author
имеет 3 поляname
,title
иbirth_date
, это приведет к тому, что на форме будут присутствовать поляname
иbirth_date
.
Если используется любой из этих способов, то порядок появления полей в форме будет соответствовать порядку их определения в модели, при этом экземпляры ManyToManyField
будут появляться последними.
Кроме того, Django применяет следующее правило: если вы установили editable=False
на поле модели, любая форма, созданная на основе модели через ModelForm
, не будет включать это поле.
Примечание
Любые поля, не включенные в форму по вышеуказанной логике, не будут установлены методом save()
формы. Также, если вы вручную добавите исключенные поля обратно в форму, они не будут инициализированы из экземпляра модели.
Django предотвратит любую попытку сохранения неполной модели, поэтому, если модель не допускает, чтобы отсутствующие поля были пустыми, и не предоставляет значения по умолчанию для отсутствующих полей, любая попытка save()
a ModelForm
с отсутствующими полями будет неудачной. Чтобы избежать этого сбоя, вы должны инстанцировать свою модель с начальными значениями для отсутствующих, но необходимых полей:
author = Author(title='Mr') form = PartialAuthorForm(request.POST, instance=author) form.save()
В качестве альтернативы можно использовать save(commit=False)
и вручную задать все дополнительные обязательные поля:
form = PartialAuthorForm(request.POST) author = form.save(commit=False) author.title = 'Mr' author.save()
Более подробную информацию об использовании section on saving forms см. в save(commit=False)
.
Переопределение полей по умолчанию¶
Типы полей по умолчанию, описанные в таблице Field types выше, являются разумными значениями по умолчанию. Если у вас есть DateField
в вашей модели, скорее всего, вы захотите, чтобы оно было представлено как DateField
в вашей форме. Но ModelForm
дает вам возможность гибко изменять поле формы для данной модели.
Чтобы указать пользовательский виджет для поля, используйте атрибут widgets
внутреннего класса Meta
. Это должен быть словарь, отображающий имена полей на классы или экземпляры виджетов.
Например, если вы хотите, чтобы CharField
для атрибута name
в Author
было представлено <textarea>
вместо стандартного <input type="text">
, вы можете переопределить виджет поля:
from django.forms import ModelForm, Textarea from myapp.models import Author class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') widgets = { 'name': Textarea(attrs={'cols': 80, 'rows': 20}), }
Словарь widgets
принимает либо экземпляры виджетов (например, Textarea(...)
), либо классы (например, Textarea
). Обратите внимание, что словарь widgets
игнорируется для поля модели с непустым атрибутом choices
. В этом случае вы должны переопределить поле формы, чтобы использовать другой виджет.
Аналогично, вы можете указать атрибуты labels
, help_texts
и error_messages
внутреннего класса Meta
, если хотите дополнительно настроить поле.
Например, если вы хотите настроить формулировку всех строк для поля name
:
from django.utils.translation import gettext_lazy as _ class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') labels = { 'name': _('Writer'), } help_texts = { 'name': _('Some useful help text.'), } error_messages = { 'name': { 'max_length': _("This writer's name is too long."), }, }
Вы также можете указать field_classes
, чтобы настроить тип полей, создаваемых формой.
Например, если вы хотите использовать MySlugFormField
для поля slug
, вы можете сделать следующее:
from django.forms import ModelForm from myapp.models import Article class ArticleForm(ModelForm): class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter', 'slug'] field_classes = { 'slug': MySlugFormField, }
Наконец, если вам нужен полный контроль над полем — включая его тип, валидаторы, обязательность и т.д. – вы можете сделать это, декларативно указав поля, как в обычном Form
.
Если вы хотите указать валидаторы поля, вы можете сделать это, определив поле декларативно и задав его параметр validators
:
from django.forms import CharField, ModelForm from myapp.models import Article class ArticleForm(ModelForm): slug = CharField(validators=[validate_slug]) class Meta: model = Article fields = ['pub_date', 'headline', 'content', 'reporter', 'slug']
Примечание
Когда вы явно инстанцируете поле формы, подобное этому, важно понять, как связаны ModelForm
и обычное Form
.
ModelForm
— это обычный Form
, который может автоматически генерировать определенные поля. Поля, которые генерируются автоматически, зависят от содержимого класса Meta
и от того, какие поля уже были определены декларативно. В основном, ModelForm
будет только генерировать поля, которые отсутствуют в форме, или, другими словами, поля, которые не были определены декларативно.
Поля, определенные декларативно, остаются как есть, поэтому любые настройки, сделанные для атрибутов Meta
, таких как widgets
, labels
, help_texts
или error_messages
, игнорируются; они применяются только к полям, которые создаются автоматически.
Аналогичным образом, поля, определенные декларативно, не берут свои атрибуты типа max_length
или required
из соответствующей модели. Если вы хотите сохранить поведение, указанное в модели, вы должны задать соответствующие аргументы явно при объявлении поля формы.
Например, если модель Article
выглядит следующим образом:
class Article(models.Model): headline = models.CharField( max_length=200, null=True, blank=True, help_text='Use puns liberally', ) content = models.TextField()
и вы хотите сделать некоторую пользовательскую проверку для headline
, сохраняя значения blank
и help_text
как указано, вы можете определить ArticleForm
следующим образом:
class ArticleForm(ModelForm): headline = MyFormField( max_length=200, required=False, help_text='Use puns liberally', ) class Meta: model = Article fields = ['headline', 'content']
Вы должны убедиться, что тип поля формы может быть использован для задания содержимого соответствующего поля модели. Если они несовместимы, вы получите ValueError
, так как неявного преобразования не происходит.
Более подробную информацию о полях и их аргументах см. в form field documentation.
Обеспечение локализации полей¶
По умолчанию поля в ModelForm
не локализуют свои данные. Чтобы включить локализацию для полей, вы можете использовать атрибут localized_fields
на классе Meta
.
>>> from django.forms import ModelForm >>> from myapp.models import Author >>> class AuthorForm(ModelForm): ... class Meta: ... model = Author ... localized_fields = ('birth_date',)
Если localized_fields
установлено специальное значение '__all__'
, все поля будут локализованы.
Наследование формы¶
Как и в случае с базовыми формами, вы можете расширять и повторно использовать ModelForms
, наследуя их. Это полезно, если вам нужно объявить дополнительные поля или дополнительные методы в родительском классе для использования в ряде форм, производных от моделей. Например, используя предыдущий ArticleForm
класс:
>>> class EnhancedArticleForm(ArticleForm): ... def clean_pub_date(self): ... ...
Это создает форму, которая ведет себя идентично ArticleForm
, за исключением дополнительной валидации и очистки поля pub_date
.
Вы также можете подклассифицировать внутренний класс Meta
родителя, если хотите изменить списки Meta.fields
или Meta.exclude
:
>>> class RestrictedArticleForm(EnhancedArticleForm): ... class Meta(ArticleForm.Meta): ... exclude = ('body',)
Это добавляет дополнительный метод из EnhancedArticleForm
и модифицирует исходный ArticleForm.Meta
, чтобы удалить одно поле.
Однако есть несколько моментов, которые следует отметить.
-
Применяются обычные правила разрешения имен Python. Если у вас есть несколько базовых классов, которые объявляют внутренний класс
Meta
, будет использоваться только первый. Это означает дочернийMeta
, если он существует, иначеMeta
первого родителя и т.д. -
Можно наследоваться одновременно от
Form
иModelForm
, однако вы должны убедиться, чтоModelForm
появится первым в MRO. Это происходит потому, что эти классы полагаются на разные метаклассы, а класс может иметь только один метакласс. -
Можно декларативно удалить
Field
, унаследованный от родительского класса, установив имяNone
в подклассе.Вы можете использовать эту технику только для отказа от поля, определенного декларативно родительским классом; это не помешает метаклассу
ModelForm
сгенерировать поле по умолчанию. Чтобы отказаться от полей по умолчанию, смотрите Выбор полей для использования.
Предоставление начальных значений¶
Как и в обычных формах, можно указать начальные данные для форм, указав параметр initial
при инстанцировании формы. Начальные значения, указанные таким образом, будут переопределять как начальные значения поля формы, так и значения из присоединенного экземпляра модели. Например:
>>> article = Article.objects.get(pk=1) >>> article.headline 'My headline' >>> form = ArticleForm(initial={'headline': 'Initial headline'}, instance=article) >>> form['headline'].value() 'Initial headline'
Функция фабрики ModelForm¶
Вы можете создавать формы из заданной модели с помощью отдельной функции modelform_factory()
, вместо того, чтобы использовать определение класса. Это может быть удобнее, если вам не нужно делать много настроек:
>>> from django.forms import modelform_factory >>> from myapp.models import Book >>> BookForm = modelform_factory(Book, fields=("author", "title"))
Это также можно использовать для внесения изменений в существующие формы, например, указывая виджеты, которые будут использоваться для данного поля:
>>> from django.forms import Textarea >>> Form = modelform_factory(Book, form=BookForm, ... widgets={"title": Textarea()})
Поля для включения можно указать с помощью аргументов fields
и exclude
ключевых слов или соответствующих атрибутов внутреннего ModelForm
класса Meta
. Обратитесь к документации ModelForm
Выбор полей для использования.
… или включить локализацию для определенных полей:
>>> Form = modelform_factory(Author, form=AuthorForm, localized_fields=("birth_date",))
Модельные наборы форм¶
-
class
models.
BaseModelFormSet
¶
Как и regular formsets, Django предоставляет пару расширенных классов формсет, чтобы сделать работу с моделями Django более удобной. Давайте повторно используем модель Author
, описанную выше:
>>> from django.forms import modelformset_factory >>> from myapp.models import Author >>> AuthorFormSet = modelformset_factory(Author, fields=('name', 'title'))
Использование fields
ограничивает набор форм использованием только заданных полей. В качестве альтернативы можно использовать подход «opt-out», указав, какие поля следует исключить:
>>> AuthorFormSet = modelformset_factory(Author, exclude=('birth_date',))
Это создаст набор форм, способный работать с данными, связанными с моделью Author
. Он работает так же, как и обычный набор форм:
>>> formset = AuthorFormSet() >>> print(formset) <input type="hidden" name="form-TOTAL_FORMS" value="1" id="id_form-TOTAL_FORMS"><input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS"><input type="hidden" name="form-MIN_NUM_FORMS" value="0" id="id_form-MIN_NUM_FORMS"><input type="hidden" name="form-MAX_NUM_FORMS" value="1000" id="id_form-MAX_NUM_FORMS"> <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" maxlength="100"></td></tr> <tr><th><label for="id_form-0-title">Title:</label></th><td><select name="form-0-title" id="id_form-0-title"> <option value="" selected>---------</option> <option value="MR">Mr.</option> <option value="MRS">Mrs.</option> <option value="MS">Ms.</option> </select><input type="hidden" name="form-0-id" id="id_form-0-id"></td></tr>
Примечание
modelformset_factory()
использует formset_factory()
для генерации наборов форм. Это означает, что модельный набор форм является расширением базового набора форм, который знает, как взаимодействовать с конкретной моделью.
Примечание
При использовании multi-table inheritance формы, сгенерированные фабрикой форм, будут содержать поле родительской ссылки (по умолчанию <parent_model_name>_ptr
) вместо поля id
.
Изменение набора queryset¶
По умолчанию, когда вы создаете набор форм из модели, набор форм будет использовать кверисет, включающий все объекты модели (например, Author.objects.all()
). Вы можете отменить это поведение, используя аргумент queryset
:
>>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O'))
В качестве альтернативы можно создать подкласс, который устанавливает self.queryset
в __init__
:
from django.forms import BaseModelFormSet from myapp.models import Author class BaseAuthorFormSet(BaseModelFormSet): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.queryset = Author.objects.filter(name__startswith='O')
Затем передайте свой класс BaseAuthorFormSet
в функцию фабрики:
>>> AuthorFormSet = modelformset_factory( ... Author, fields=('name', 'title'), formset=BaseAuthorFormSet)
Если вы хотите вернуть набор форм, который не включает любые ранее существовавшие экземпляры модели, вы можете указать пустой QuerySet:
>>> AuthorFormSet(queryset=Author.objects.none())
Изменение формы¶
По умолчанию, когда вы используете modelformset_factory
, форма модели будет создана с помощью modelform_factory()
. Часто бывает полезно указать пользовательскую форму модели. Например, вы можете создать пользовательскую форму модели, которая будет иметь пользовательскую валидацию:
class AuthorForm(forms.ModelForm): class Meta: model = Author fields = ('name', 'title') def clean_name(self): # custom validation for the name field ...
Затем передайте форму вашей модели в функцию фабрики:
AuthorFormSet = modelformset_factory(Author, form=AuthorForm)
Не всегда необходимо определять пользовательскую форму модели. Функция modelformset_factory
имеет несколько аргументов, передаваемых в modelform_factory
, которые описаны ниже.
Включение локализации для полей с localized_fields
¶
Используя параметр localized_fields
, вы можете включить локализацию для полей формы.
>>> AuthorFormSet = modelformset_factory( ... Author, fields=('name', 'title', 'birth_date'), ... localized_fields=('birth_date',))
Если localized_fields
установлено специальное значение '__all__'
, все поля будут локализованы.
Предоставление начальных значений¶
Как и в обычных наборах форм, можно specify initial data для форм в наборе форм, указав параметр initial
при инстанцировании класса набора форм модели, возвращаемого modelformset_factory()
. Однако в модельных наборах форм начальные значения применяются только к дополнительным формам, тем, которые не прикреплены к существующему экземпляру модели. Если длина initial
превышает количество дополнительных форм, избыточные начальные данные игнорируются. Если дополнительные формы с начальными данными не были изменены пользователем, они не будут проверены или сохранены.
Сохранение объектов в наборе форм¶
Как и в случае с ModelForm
, вы можете сохранить данные как объект модели. Это делается с помощью метода save()
набора форм:
# Create a formset instance with POST data. >>> formset = AuthorFormSet(request.POST) # Assuming all is valid, save the data. >>> instances = formset.save()
Метод save()
возвращает экземпляры, которые были сохранены в базе данных. Если данные экземпляра не изменились в связанных данных, экземпляр не будет сохранен в базе данных и не будет включен в возвращаемое значение (instances
, в примере выше).
Если поля отсутствуют в форме (например, потому что они были исключены), эти поля не будут установлены методом save()
. Более подробную информацию об этом ограничении, которое также действует для обычных ModelForms
, можно найти в Selecting the fields to use.
Передайте commit=False
, чтобы вернуть несохраненные экземпляры модели:
# don't save to the database >>> instances = formset.save(commit=False) >>> for instance in instances: ... # do something with instance ... instance.save()
Это дает вам возможность прикреплять данные к экземплярам перед сохранением их в базе данных. Если ваш набор форм содержит ManyToManyField
, вам также необходимо вызвать formset.save_m2m()
, чтобы обеспечить правильное сохранение отношений «многие-ко-многим».
После вызова save()
у вашего набора форм модели появятся три новых атрибута, содержащих изменения набора форм:
-
models.BaseModelFormSet.
changed_objects
¶
-
models.BaseModelFormSet.
deleted_objects
¶
-
models.BaseModelFormSet.
new_objects
¶
Ограничение количества редактируемых объектов¶
Как и в случае с обычными наборами форм, вы можете использовать параметры max_num
и extra
к modelformset_factory()
, чтобы ограничить количество отображаемых дополнительных форм.
max_num
не препятствует отображению существующих объектов:
>>> Author.objects.order_by('name') <QuerySet [<Author: Charles Baudelaire>, <Author: Paul Verlaine>, <Author: Walt Whitman>]> >>> AuthorFormSet = modelformset_factory(Author, fields=('name',), max_num=1) >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) >>> [x.name for x in formset.get_queryset()] ['Charles Baudelaire', 'Paul Verlaine', 'Walt Whitman']
Также extra=0
не препятствует созданию новых экземпляров модели, как вы можете add additional forms with JavaScript, или отправке дополнительных POST-данных. Смотрите Предотвращение создания новых объектов о том, как это сделать.
Если значение max_num
больше, чем количество существующих связанных объектов, в набор форм будет добавлено до extra
дополнительных пустых форм, пока общее количество форм не превысит max_num
:
>>> AuthorFormSet = modelformset_factory(Author, fields=('name',), max_num=4, extra=2) >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) >>> for form in formset: ... print(form.as_table()) <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" value="Charles Baudelaire" maxlength="100"><input type="hidden" name="form-0-id" value="1" id="id_form-0-id"></td></tr> <tr><th><label for="id_form-1-name">Name:</label></th><td><input id="id_form-1-name" type="text" name="form-1-name" value="Paul Verlaine" maxlength="100"><input type="hidden" name="form-1-id" value="3" id="id_form-1-id"></td></tr> <tr><th><label for="id_form-2-name">Name:</label></th><td><input id="id_form-2-name" type="text" name="form-2-name" value="Walt Whitman" maxlength="100"><input type="hidden" name="form-2-id" value="2" id="id_form-2-id"></td></tr> <tr><th><label for="id_form-3-name">Name:</label></th><td><input id="id_form-3-name" type="text" name="form-3-name" maxlength="100"><input type="hidden" name="form-3-id" id="id_form-3-id"></td></tr>
Значение max_num
None
(по умолчанию) устанавливает высокий предел на количество отображаемых форм (1000). На практике это эквивалентно отсутствию ограничения.
Предотвращение создания новых объектов¶
New in Django 4.1.
Используя параметр edit_only
, вы можете предотвратить создание любых новых объектов:
>>> AuthorFormSet = modelformset_factory( ... Author, ... fields=('name', 'title'), ... edit_only=True, ... )
Здесь набор форм будет редактировать только существующие экземпляры Author
. Никакие другие объекты не будут созданы или отредактированы.
Использование набора форм модели в представлении¶
Формсеты моделей очень похожи на наборы форм. Допустим, мы хотим представить набор форм для редактирования Author
экземпляров модели:
from django.forms import modelformset_factory from django.shortcuts import render from myapp.models import Author def manage_authors(request): AuthorFormSet = modelformset_factory(Author, fields=('name', 'title')) if request.method == 'POST': formset = AuthorFormSet(request.POST, request.FILES) if formset.is_valid(): formset.save() # do something. else: formset = AuthorFormSet() return render(request, 'manage_authors.html', {'formset': formset})
Как вы можете видеть, логика представления модельного набора форм не сильно отличается от логики «обычного» набора форм. Единственное отличие заключается в том, что мы вызываем formset.save()
для сохранения данных в базе данных. (Это было описано выше, в Сохранение объектов в наборе форм).
Переопределение clean()
на ModelFormSet
¶
Как и в случае с ModelForms
, по умолчанию метод clean()
в ModelFormSet
будет проверять, что ни один из элементов набора форм не нарушает уникальные ограничения вашей модели (либо unique
, либо unique_together
, либо unique_for_date|month|year
). Если вы хотите переопределить метод clean()
на ModelFormSet
и сохранить эту проверку, вы должны вызвать метод clean
родительского класса:
from django.forms import BaseModelFormSet class MyModelFormSet(BaseModelFormSet): def clean(self): super().clean() # example custom validation across forms in the formset for form in self.forms: # your custom formset validation ...
Также обратите внимание, что к тому времени, когда вы дойдете до этого шага, отдельные экземпляры модели уже будут созданы для каждого Form
. Изменения значения в form.cleaned_data
недостаточно, чтобы повлиять на сохраненное значение. Если вы хотите изменить значение в ModelFormSet.clean()
, вы должны изменить form.instance
:
from django.forms import BaseModelFormSet class MyModelFormSet(BaseModelFormSet): def clean(self): super().clean() for form in self.forms: name = form.cleaned_data['name'].upper() form.cleaned_data['name'] = name # update the instance value. form.instance.name = name
Использование пользовательского набора запросов¶
Как говорилось ранее, вы можете переопределить набор запросов по умолчанию, используемый набором форм модели:
from django.forms import modelformset_factory from django.shortcuts import render from myapp.models import Author def manage_authors(request): AuthorFormSet = modelformset_factory(Author, fields=('name', 'title')) queryset = Author.objects.filter(name__startswith='O') if request.method == "POST": formset = AuthorFormSet( request.POST, request.FILES, queryset=queryset, ) if formset.is_valid(): formset.save() # Do something. else: formset = AuthorFormSet(queryset=queryset) return render(request, 'manage_authors.html', {'formset': formset})
Обратите внимание, что в этом примере мы передаем аргумент queryset
в обоих случаях POST
и GET
.
Использование набора форм в шаблоне¶
Существует три способа отображения набора форм в шаблоне Django.
Во-первых, вы можете позволить набору форм сделать большую часть работы:
<form method="post"> {{ formset }} </form>
Во-вторых, вы можете вручную отобразить набор форм, но позволить форме самой разобраться с собой:
<form method="post"> {{ formset.management_form }} {% for form in formset %} {{ form }} {% endfor %} </form>
Если вы сами вручную выводите формы, убедитесь, что форма управления выводится так, как показано выше. Смотрите management form documentation.
В-третьих, вы можете вручную визуализировать каждое поле:
<form method="post"> {{ formset.management_form }} {% for form in formset %} {% for field in form %} {{ field.label_tag }} {{ field }} {% endfor %} {% endfor %} </form>
Если вы решите использовать этот третий метод и не будете перебирать поля с помощью цикла {% for %}
, вам нужно будет отобразить поле первичного ключа. Например, если вы отображаете поля name
и age
модели:
<form method="post"> {{ formset.management_form }} {% for form in formset %} {{ form.id }} <ul> <li>{{ form.name }}</li> <li>{{ form.age }}</li> </ul> {% endfor %} </form>
Обратите внимание, что нам нужно явно отобразить {{ form.id }}
. Это гарантирует, что набор форм модели, в случае POST
, будет работать правильно. (В этом примере предполагается первичный ключ с именем id
. Если вы явно определили свой собственный первичный ключ, который не называется id
, убедитесь, что он будет отображен).
Встроенные наборы форм¶
-
class
models.
BaseInlineFormSet
¶
Inline formsets — это небольшой слой абстракции поверх model formsets. Они упрощают работу со связанными объектами через внешний ключ. Предположим, у вас есть две модели:
from django.db import models class Author(models.Model): name = models.CharField(max_length=100) class Book(models.Model): author = models.ForeignKey(Author, on_delete=models.CASCADE) title = models.CharField(max_length=100)
Если вы хотите создать набор форм, позволяющий редактировать книги, принадлежащие определенному автору, вы можете сделать следующее:
>>> from django.forms import inlineformset_factory >>> BookFormSet = inlineformset_factory(Author, Book, fields=('title',)) >>> author = Author.objects.get(name='Mike Royko') >>> formset = BookFormSet(instance=author)
BookFormSet
’s prefix is 'book_set'
(<model name>_set
). Если у Book
от ForeignKey
до Author
есть related_name
, то он используется вместо него.
Переопределение методов на InlineFormSet
¶
При переопределении методов на InlineFormSet
следует подкласс BaseInlineFormSet
, а не BaseModelFormSet
.
Например, если вы хотите переопределить clean()
:
from django.forms import BaseInlineFormSet class CustomInlineFormSet(BaseInlineFormSet): def clean(self): super().clean() # example custom validation across forms in the formset for form in self.forms: # your custom formset validation ...
См. также Переопределение clean() на ModelFormSet.
Затем, когда вы создаете свой набор встроенных форм, передайте необязательный аргумент formset
:
>>> from django.forms import inlineformset_factory >>> BookFormSet = inlineformset_factory(Author, Book, fields=('title',), ... formset=CustomInlineFormSet) >>> author = Author.objects.get(name='Mike Royko') >>> formset = BookFormSet(instance=author)
Более одного внешнего ключа к одной и той же модели¶
Если ваша модель содержит более одного внешнего ключа к одной и той же модели, вам придется разрешить неоднозначность вручную с помощью fk_name
. Например, рассмотрим следующую модель:
class Friendship(models.Model): from_friend = models.ForeignKey( Friend, on_delete=models.CASCADE, related_name='from_friends', ) to_friend = models.ForeignKey( Friend, on_delete=models.CASCADE, related_name='friends', ) length_in_months = models.IntegerField()
Чтобы решить эту проблему, вы можете использовать fk_name
для inlineformset_factory()
:
>>> FriendshipFormSet = inlineformset_factory(Friend, Friendship, fk_name='from_friend', ... fields=('to_friend', 'length_in_months'))
Использование встроенного набора форм в представлении¶
Вы можете захотеть предоставить представление, позволяющее пользователю редактировать связанные объекты модели. Вот как это можно сделать:
def manage_books(request, author_id): author = Author.objects.get(pk=author_id) BookInlineFormSet = inlineformset_factory(Author, Book, fields=('title',)) if request.method == "POST": formset = BookInlineFormSet(request.POST, request.FILES, instance=author) if formset.is_valid(): formset.save() # Do something. Should generally end with a redirect. For example: return HttpResponseRedirect(author.get_absolute_url()) else: formset = BookInlineFormSet(instance=author) return render(request, 'manage_books.html', {'formset': formset})
Обратите внимание, что мы передаем instance
в обоих случаях POST
и GET
.
Модель — это класс, который содержит одну таблицу в вашей базе данных. Каждая модель представляет собой подклассы Python django.db.models.Model, а каждый атрибут модели, представляет собой отдельную запись в базе данных. Чтобы модели были успешно обработаны Django, содержащие их приложения должный быть зарегистрированы в списке приложений проекта.
Приведем пример модели Django. В этом примере модели, описывается класс Student, у которого есть имя ( first_name ) и фамилия ( last_name ):
from django.db import models class Student(models.Model): first_name = models.CharField(max_length=25) last_name = models.CharField(max_length=25)
first_name и last_name являются полями модели. Каждое поле указывается как атрибут класса, и каждый атрибут сопоставляется со столбцом базы данных.
Модель Student создает вот такую таблицу базе данных:
CREATE TABLE myapp_student (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(25) NOT NULL,
"last_name" varchar(25) NOT NULL
);
1. Аргументы, доступные для всех типов полей Django
-
null
Если True, Django будет хранить пустые значения, как NULL в базе данных. По умолчанию False.
Избегайте использования null в строковых полях, таких как CharField и TextField. Если строковое поле имеет null=True, это означает, что у него есть два возможных значения для «нет данных»: NULL и пустая строка. В большинстве случаев иметь два возможных значения для «нет данных» излишне; соглашение Django заключается в использовании пустой строки, а не NULL. Единственное исключение — это когда CharField оба параметра unique=True и blank=True установлены. В этой ситуации null=True требуется избежать нарушения уникальных ограничений при сохранении нескольких объектов с пустыми значениями.
Как для строковых, так и для нестроковых полей вам также потребуется установить, blank=True, если вы хотите разрешить пустые значения в формах, поскольку null параметр влияет только на хранилище базы данных.
-
blank
Если True, то Django позволит занести в поле пустое значение, в таком случае поле станет не обязательным к заполнению. По умолчанию False.
Обратите внимание, что blank отличается от null. null связано исключительно с базой данных, тогда как blank связано с проверкой. Если в поле есть blank=True, проверка формы позволит ввести пустое значение. Если в поле есть blank=False, поле будет обязательным.
-
verbose_name
Удобочитаемое (» человеческое «) имя поля. Если имя не указано, то Django автоматически создаст его, используя имя атрибута поля, преобразовав подчеркивание в пробелы.
verbose_name="Пост"
-
help_text
Дополнительный поясняющий текст, отображаемый в виджете формы. Это полезно для документации, даже если ваше поле не используется в форме.
Обратите внимание, что это значение не экранируется HTML в автоматически сгенерированных формах и выводится как есть. Например:
help_text="Введите дату Вашего рождения"
-
db_column
Имя столбца базы данных, используемого для этого поля. Если это не указано, Django будет использовать имя поля.
Если имя столбца вашей базы данных является зарезервированным словом SQL или содержит символы, недопустимые в именах переменных Python, в частности дефис — это нормально. Django цитирует имена столбцов и таблиц за кулисами.
-
db_index
Если True, для этого поля будет создан индекс базы данных.
-
db_tablespace
Имя табличного пространства базы данных, которое будет использоваться для индекса этого поля, если это поле проиндексировано. По умолчанию используется параметр проекта DEFAULT_INDEX_TABLESPACE, если он установлен, или db_tablespace параметр модели, если он есть. Если серверная часть не поддерживает табличные пространства для индексов, этот параметр игнорируется.
-
default
Значение по умолчанию записываемое в поле. Это может быть значение или вызываемый объект. Если записывается вызываемый объект, он будет вызываться каждый раз при создании нового объекта.
-
editable
Если False, поле не будет отображаться ни в админке, ни в каком-либо другом ModelForm. Они также пропускаются во время проверки модели . По умолчанию True.
-
primary_key
Если True — это поле является первичным ключом модели.
Если вы не укажете primary_key=True в какое-либо поле своей модели, то Django автоматически добавит его в поле для хранения первичного ключа, поэтому вам не нужно устанавливать primary_key=True в какие-либо поля, если вы не хотите переопределить поведение первичного ключа по умолчанию. Тип автоматически создаваемых полей первичного ключа можно указать для каждого приложения AppConfig.default_auto_field или глобально в DEFAULT_AUTO_FIELD в настройках.
primary_key=True подразумевает что null=False и unique=True. Для объекта разрешен только один первичный ключ.
Поле первичного ключа доступно только для чтения. Если вы измените значение первичного ключа на существующем объекте, а затем сохраните его, новый объект будет создан рядом со старым.
-
unique
Если True, то в это поле должно быть занесено уникальное в пределах таблицы значение.
Это обеспечивается на уровне базы данных и проверкой модели. Если вы попытаетесь сохранить модель с повторяющимся значением в unique поле, то будет возбуждено исключение django.db.IntegrityError.
Эта опция действительна для всех типов полей, кроме ManyToManyField и OneToOneField.
Обратите внимание: когда unique=True, то указывать db_index не нужно, поскольку unique подразумевает создание индекса.
-
unique_for_date
Задайте для него имя DateField или DateTimeField, чтобы это поле было уникальным для значения поля даты.
Например, если у вас есть поле title, в котором есть unique_for_date=»pub_date», то Django не разрешит ввод двух записей с одинаковыми title и pub_date.
-
unique_for_month
То же самое, что и unique_for_date, но в расчет принимается только месяц.
-
unique_for_year
То же самое что unique_for_date и unique_for_month, но в расчет принимается год.
-
error_messages
error_messages, аргумент позволяет переопределить сообщения по умолчанию , что поле будет выводить. Передайте словарь с ключами, соответствующими сообщениям об ошибках, которые вы хотите переопределить.
Ключи сообщений об ошибках включают в себя null, blank, invalid, invalid_choice, unique, и unique_for_date. Дополнительные ключи сообщений об ошибках указаны для каждого поля в разделе Типы полей ниже.
2. Типы полей моделей Django
Каждое поле хранит значения определенного типа. Почти все поля поддерживают аргументы описанные раньше.
-
TextField
class TextField(**параметры)
Текстовое поле неограниченной длины. Рекомендуется применять для сохранения больших объемов текста. Поддерживает необязательный параметр max_length. Если он не указан, то можно записать значение любой длины.
context = models.TextField(max_length=5000)
-
CharField
class CharField ( max_length = None , ** параметры )
Строковое поле для строк от маленького до большого. Для больших объемов текста используйте TextField. CharField имеет один дополнительный обязательный аргумент max_length.
max_length — указывает максимальную длину заносимого в поле значения.
title = models.CharField(max_length=30)
-
EmailField
class EmailField(max_length=254, **параметры)
Адрес электронной почты в строковом виде. Поддерживает необязательный параметр max_length. По умолчанию стоит 254.
-
AutoField
class AutoField ( ** параметры )
Значение IntegerField, которое автоматически увеличивается в соответствии с доступными идентификаторами. Обычно вам не нужно использовать это напрямую Поле первичного ключа будет автоматически добавлено в вашу модель, если вы не укажете иное.
-
BigAutoField
class BigAutoField(**параметры)
64-битное целое число, тоже самое что и AutoField, за исключением того, что оно гарантированно соответствует числам от 1до 9223372036854775807.
-
BigIntegerField
class BigIntegerField(**параметры)
64-битное целое число, тоже самое что и IntegerField, за исключением того, что оно гарантированно соответствует числам от -9223372036854775808 до 9223372036854775807.
-
BinaryField
class BinaryField(max_length=None, **параметры)
Поле для хранения необработанных двоичных данных. Он может быть назначен bytes, bytearray или memoryview. По умолчанию BinaryField устанавливает editable на False, в этом случае она не может быть включена в ModelForm. BinaryField имеет один дополнительный необязательный аргумент max_length.
-
BooleanField
class BooleanField(**параметры)
Поле хранящее значение True или False. Значение по умолчанию None, а не False.
-
DateField
class DateField ( auto_now = False , auto_now_add = False , ** параметры )
Дата, представленная в Python datetime.date экземпляром. Имеет несколько дополнительных необязательных аргументов:
— DateField.auto_now
Автоматически при каждом сохранение записи будет устанавливаться текущее время.
— DateField.auto_now_add
Автоматически устанавливает текущую дату только при создание записи. В последующем при изменение дата не меняется.
-
DateTimeField
class DateTimeField ( auto_now = False , auto_now_add = False , ** параметры )
Дата и время, представленные в Python datetime.datetime экземпляром. Принимает те же дополнительные аргументы, что и DateField. Виджет формы по умолчанию для этого поля — одиночный DateTimeInput.
-
DecimalField
class DecimalField ( max_digits = None , decimal_places = None , ** параметры )
Десятичное число фиксированной точности, представленное в Python Decimal экземпляром. Он проверяет ввод с помощью DecimalValidator.
Имеет два обязательных аргумента:
— DecimalField.max_digits
Максимально допустимое количество цифр в номере. Обратите внимание, что это число должно быть больше или равно decimal_places.
— DecimalField.decimal_places
Количество десятичных знаков, которые нужно сохранить вместе с номером.
Например, для хранения чисел 999 с разрешением до 2 знаков после запятой вы должны использовать:
models.DecimalField(…, max_digits=5, decimal_places=2)
И для хранения чисел примерно до одного миллиарда с разрешением до 10 знаков после запятой:
models.DecimalField(…, max_digits=19, decimal_places=10)
-
DurationField
class DurationField ( ** параметры )
Поле для хранения периодов времени — смоделировано на Python с помощью timedelta. При использовании в PostgreSQL используется тип данных, intervalа в Oracle тип данных . В противном случае используется микросекунды. INTERVAL DAY(9) TO SECOND(6)bigint
-
IntegerField
class IntegerField ( ** параметры )
Целое число ( 32 разрядное ) . Значения от -2147483648 до 2147483647 безопасны во всех базах данных, поддерживаемых Django. Он использует MinValueValidator и MaxValueValidator для проверки ввода на основе значений, поддерживаемых базой данных по умолчанию.
-
GenericIPAddressField
class GenericIPAddressField ( протокол = ‘both’ , unpack_ipv4 = False , ** параметры )
IP-адрес записанный по для протокола IPv4 или IPv6 в виде строки. Поддерживает два необязательных параметра:
— GenericIPAddressField.protocol
Ограничивает допустимые входные данные указанным протоколом. Допустимые значения: ‘both'(по умолчанию) ‘IPv4’ или ‘IPv6’. При сопоставлении регистр не учитывается.
— GenericIPAddressField.unpack_ipv4
Распаковывает сопоставленные адреса IPv4, например ::ffff:192.0.2.1. Если эта опция включена, этот адрес будет распакован 192.0.2.1. По умолчанию отключено. Может использоваться, только если для параметра protocol установлено значение ‘both’.
-
PositiveBigIntegerField
class PositiveBigIntegerField ( ** параметры)
Допускает значения только в определенной (зависящей от базы данных) точке. Значения от 0до 9223372036854775807 безопасны во всех базах данных, поддерживаемых Django.
-
PositiveIntegerField
class PositiveIntegerField ( ** параметры )
Должно быть либо положительным, либо нулевым ( 0). Значения от 0 до 2147483647 безопасны во всех базах данных, поддерживаемых Django. Значение 0 принято из соображений обратной совместимости.
-
PositiveSmallIntegerField
class PositiveSmallIntegerField ( ** параметры )
Допускает значения только в определенной (зависящей от базы данных) точке. Значения от 0 до 32767 безопасны во всех базах данных, поддерживаемых Django.
-
SlugField
class SlugField ( max_length = 50 , ** параметры )
Слаг — это короткая метка для чего-либо, содержащая только буквы, цифры, подчеркивания или дефисы. Обычно они используются в URL-адресах. Как и CharField, вы можете указать max_length. Если max_length не указано, Django будет использовать длину по умолчанию 50.
Часто бывает полезно автоматически предварительно заполнить SlugField на основе значения некоторого другого значения. Вы можете сделать это автоматически в админке с помощью prepopulated_fields.
prepopulated_fields = {‘slug’: (‘title’,)}
Если True, поле принимает буквы Юникода в дополнение к буквам ASCII. По умолчанию False.
-
SmallAutoField
class SmallAutoField(**параметры)
Допускает значения только в пределах определенного (зависящего от базы данных) предела. Значения от 1
до 32767
безопасны во всех базах данных, поддерживаемых Django.
-
SmallIntegerField
class SmallIntegerField ( ** параметры )
Допускает значения только в определенной (зависящей от базы данных) точке. Значения от -32768 до 32767 безопасны во всех базах данных, поддерживаемых Django.
-
TimeField
class TimeField ( auto_now = False , auto_now_add = False , ** параметры )
Время, представленное в Python datetime.time экземпляром. Принимает те же параметры автозаполнения, что и DateField.
-
URLField
class URLField ( max_length = 200 , ** параметры )
Виджет формы по умолчанию для этого поля — файл URLInput.
Необязательный параметр max_length указывает максимальную длину заносимого в поле интернет-адреса. Значение по умолчанию 200.
-
UUIDField
class UUIDField ( ** параметры )
Поле для хранения универсальных уникальных идентификаторов. Использует UUIDкласс Python . При использовании в PostgreSQL хранится в uuidтипе данных, в противном случае — в файле char(32).
Универсальные уникальные идентификаторы — хорошая альтернатива AutoFieldfor primary_key. База данных не будет генерировать UUID для вас, поэтому рекомендуется использовать default:
import uuid
from django.db import models
class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
28.12.2019Python
Встроенные проверки полей в моделях Django — это проверки, которые предопределены для всех полей Django. Каждое поле имеет встроенные валидаторы от валидаторов Django. Можно также добавить больше встроенных проверок полей для применения или удаления определенных ограничений для определенного поля. error_messages
используется для изменения сообщений об ошибках, которые отображаются в интерфейсе администратора во время сбоя какого-либо ограничения.
Например, вы можете переопределить сообщение «Это поле обязательно» для вашего собственного сообщения. Это позволяет вам переопределить сообщения по умолчанию, которые будут появляться в поле. Передайте словарь с ключами, соответствующими сообщениям об ошибках, которые вы хотите переопределить. Ключи сообщения об ошибке включают в себя null , blank , invalid , invalid_choice , unique и unique_for_date .
Синтаксис —
field_name = models.Field(error_messages = {"key": "message"})
Редактирование встроенного поля в Django editable=False
объяснение
Иллюстрация error_messages с использованием примера. Рассмотрим проект с именем geeksforgeeks
, в котором есть приложение с именем geeks
.
Refer to the following articles to check how to create a project and an app in Django.
- How to Create a Basic Project using MVT in Django?
- How to Create an App in Django ?
Введите следующий код в файл models.py
приложения geeks . Мы будем использовать CharField для экспериментов по всем вариантам поля.
from
django.db
import
models
from
django.db.models
import
Model
class
GeeksModel(Model):
geeks_field
=
models.CharField(
max_length
=
200
,
unique
=
True
)
После запуска makemigrations
и миграции на Django и рендеринга описанной выше модели, давайте создадим экземпляр из интерфейса администратора Django со строкой « a ». Теперь, чтобы нарушить ограничение unique = True , попробуем создать еще один экземпляр модели с использованием той же строки. Теперь он покажет эту ошибку.
Теперь давайте изменим это сообщение об ошибке на «Поле Geeks, которое вы указали, не является уникальным», используя error_messages . Измените models.py
на
from
django.db
import
models
from
django.db.models
import
Model
class
GeeksModel(Model):
geeks_field
=
models.CharField(
max_length
=
200
,
unique
=
True
,
error_messages
=
{
"unique"
:
"The Geeks Field you enetered is not unique."
}
)
Так как models.py изменен, запустите makemigrations и снова перенесите проект. Откройте интерфейс администратора и попробуйте создать экземпляр снова, используя строку «a».
Вы можете увидеть измененное сообщение об ошибке. Поэтому error_messages
изменяет поля сообщений об ошибках. Вы можете изменить, используя другие атрибуты, такие как ноль , пустое и т. д.
Рекомендуемые посты:
- verbose_name — проверка встроенного поля Django
- начальный — проверка поля формы Django
- label_suffix — проверка поля формы Django
- help_text — проверка встроенного поля Django
- help_text — проверка поля формы Django
- требуется — проверка поля формы Django
- label — проверка поля формы Django
- primary_key — проверка встроенного поля Django
- по умолчанию — проверка встроенного поля Django
- blank = True — проверка встроенного поля в Django
- null = True — проверка встроенного поля в Django
- editable = False — проверка встроенного поля в Django
- unique = True — проверка встроенного поля Django
- Python | Проверка формы с использованием Django
- Как использовать Django Field Choices?
error_messages — проверка встроенного поля в Django
0.00 (0%) 0 votes