diff options
| author | django-bot <ops@djangoproject.com> | 2023-02-28 20:53:28 +0100 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2023-03-01 13:03:56 +0100 |
| commit | 14459f80ee3a9e005989db37c26fd13bb6d2fab2 (patch) | |
| tree | eb62429ed696ed3a5389f3a676aecfc6d15a99cc /docs/topics/forms | |
| parent | 6015bab80e28aef2669f6fac53423aa65f70cb08 (diff) | |
Fixed #34140 -- Reformatted code blocks in docs with blacken-docs.
Diffstat (limited to 'docs/topics/forms')
| -rw-r--r-- | docs/topics/forms/formsets.txt | 217 | ||||
| -rw-r--r-- | docs/topics/forms/index.txt | 29 | ||||
| -rw-r--r-- | docs/topics/forms/media.txt | 58 | ||||
| -rw-r--r-- | docs/topics/forms/modelforms.txt | 160 |
4 files changed, 280 insertions, 184 deletions
diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index 85c35dc2d0..a2e265a91f 100644 --- a/docs/topics/forms/formsets.txt +++ b/docs/topics/forms/formsets.txt @@ -16,6 +16,7 @@ form: >>> class ArticleForm(forms.Form): ... title = forms.CharField() ... pub_date = forms.DateField() + ... You might want to allow the user to create several articles at once. To create a formset out of an ``ArticleForm`` you would do: @@ -34,6 +35,7 @@ in the formset and display them as you would with a regular form: >>> formset = ArticleFormSet() >>> for form in formset: ... print(form) + ... <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" id="id_form-0-title"></div> <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" id="id_form-0-pub_date"></div> @@ -71,13 +73,18 @@ example: >>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) - >>> formset = ArticleFormSet(initial=[ - ... {'title': 'Django is now open source', - ... 'pub_date': datetime.date.today(),} - ... ]) + >>> formset = ArticleFormSet( + ... initial=[ + ... { + ... "title": "Django is now open source", + ... "pub_date": datetime.date.today(), + ... } + ... ] + ... ) >>> for form in formset: ... print(form) + ... <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title"></div> <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" value="2023-02-11" id="id_form-0-pub_date"></div> <div><label for="id_form-1-title">Title:</label><input type="text" name="form-1-title" id="id_form-1-title"></div> @@ -114,6 +121,7 @@ gives you the ability to limit the number of forms the formset will display: >>> formset = ArticleFormSet() >>> for form in formset: ... print(form) + ... <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" id="id_form-0-title"></div> <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" id="id_form-0-pub_date"></div> @@ -153,8 +161,8 @@ protects against memory exhaustion attacks using forged ``POST`` requests: >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, absolute_max=1500) >>> data = { - ... 'form-TOTAL_FORMS': '1501', - ... 'form-INITIAL_FORMS': '0', + ... "form-TOTAL_FORMS": "1501", + ... "form-INITIAL_FORMS": "0", ... } >>> formset = ArticleFormSet(data) >>> len(formset.forms) @@ -182,8 +190,8 @@ all forms in the formset: >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm) >>> data = { - ... 'form-TOTAL_FORMS': '1', - ... 'form-INITIAL_FORMS': '0', + ... "form-TOTAL_FORMS": "1", + ... "form-INITIAL_FORMS": "0", ... } >>> formset = ArticleFormSet(data) >>> formset.is_valid() @@ -196,12 +204,12 @@ provide an invalid article: .. code-block:: pycon >>> data = { - ... 'form-TOTAL_FORMS': '2', - ... 'form-INITIAL_FORMS': '0', - ... 'form-0-title': 'Test', - ... 'form-0-pub_date': '1904-06-16', - ... 'form-1-title': 'Test', - ... 'form-1-pub_date': '', # <-- this date is missing but required + ... "form-TOTAL_FORMS": "2", + ... "form-INITIAL_FORMS": "0", + ... "form-0-title": "Test", + ... "form-0-pub_date": "1904-06-16", + ... "form-1-title": "Test", + ... "form-1-pub_date": "", # <-- this date is missing but required ... } >>> formset = ArticleFormSet(data) >>> formset.is_valid() @@ -239,10 +247,10 @@ sent without any data): .. code-block:: pycon >>> data = { - ... 'form-TOTAL_FORMS': '1', - ... 'form-INITIAL_FORMS': '0', - ... 'form-0-title': '', - ... 'form-0-pub_date': '', + ... "form-TOTAL_FORMS": "1", + ... "form-INITIAL_FORMS": "0", + ... "form-0-title": "", + ... "form-0-pub_date": "", ... } >>> formset = ArticleFormSet(data) >>> formset.has_changed() @@ -262,8 +270,8 @@ provide this management data, the formset will be invalid: .. code-block:: pycon >>> data = { - ... 'form-0-title': 'Test', - ... 'form-0-pub_date': '', + ... "form-0-title": "Test", + ... "form-0-pub_date": "", ... } >>> formset = ArticleFormSet(data) >>> formset.is_valid() @@ -339,7 +347,9 @@ And here is a custom error message: .. code-block:: pycon - >>> formset = ArticleFormSet({}, error_messages={'missing_management_form': 'Sorry, something went wrong.'}) + >>> formset = ArticleFormSet( + ... {}, error_messages={"missing_management_form": "Sorry, something went wrong."} + ... ) >>> formset.is_valid() False >>> formset.non_form_errors() @@ -368,19 +378,20 @@ is where you define your own validation that works at the formset level: ... for form in self.forms: ... if self.can_delete and self._should_delete_form(form): ... continue - ... title = form.cleaned_data.get('title') + ... title = form.cleaned_data.get("title") ... if title in titles: ... raise ValidationError("Articles in a set must have distinct titles.") ... titles.append(title) + ... >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) >>> data = { - ... 'form-TOTAL_FORMS': '2', - ... 'form-INITIAL_FORMS': '0', - ... 'form-0-title': 'Test', - ... 'form-0-pub_date': '1904-06-16', - ... 'form-1-title': 'Test', - ... 'form-1-pub_date': '1912-06-23', + ... "form-TOTAL_FORMS": "2", + ... "form-INITIAL_FORMS": "0", + ... "form-0-title": "Test", + ... "form-0-pub_date": "1904-06-16", + ... "form-1-title": "Test", + ... "form-1-pub_date": "1912-06-23", ... } >>> formset = ArticleFormSet(data) >>> formset.is_valid() @@ -512,12 +523,15 @@ Lets you create a formset with the ability to order: >>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, can_order=True) - >>> formset = ArticleFormSet(initial=[ - ... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, - ... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, - ... ]) + >>> formset = ArticleFormSet( + ... initial=[ + ... {"title": "Article #1", "pub_date": datetime.date(2008, 5, 10)}, + ... {"title": "Article #2", "pub_date": datetime.date(2008, 5, 11)}, + ... ] + ... ) >>> for form in formset: ... print(form) + ... <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title"></div> <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date"></div> <div><label for="id_form-0-ORDER">Order:</label><input type="number" name="form-0-ORDER" value="1" id="id_form-0-ORDER"></div> @@ -536,27 +550,31 @@ happen when the user changes these values: .. code-block:: pycon >>> data = { - ... 'form-TOTAL_FORMS': '3', - ... 'form-INITIAL_FORMS': '2', - ... 'form-0-title': 'Article #1', - ... 'form-0-pub_date': '2008-05-10', - ... 'form-0-ORDER': '2', - ... 'form-1-title': 'Article #2', - ... 'form-1-pub_date': '2008-05-11', - ... 'form-1-ORDER': '1', - ... 'form-2-title': 'Article #3', - ... 'form-2-pub_date': '2008-05-01', - ... 'form-2-ORDER': '0', + ... "form-TOTAL_FORMS": "3", + ... "form-INITIAL_FORMS": "2", + ... "form-0-title": "Article #1", + ... "form-0-pub_date": "2008-05-10", + ... "form-0-ORDER": "2", + ... "form-1-title": "Article #2", + ... "form-1-pub_date": "2008-05-11", + ... "form-1-ORDER": "1", + ... "form-2-title": "Article #3", + ... "form-2-pub_date": "2008-05-01", + ... "form-2-ORDER": "0", ... } - >>> formset = ArticleFormSet(data, initial=[ - ... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, - ... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, - ... ]) + >>> formset = ArticleFormSet( + ... data, + ... initial=[ + ... {"title": "Article #1", "pub_date": datetime.date(2008, 5, 10)}, + ... {"title": "Article #2", "pub_date": datetime.date(2008, 5, 11)}, + ... ], + ... ) >>> formset.is_valid() True >>> for form in formset.ordered_forms: ... print(form.cleaned_data) + ... {'pub_date': datetime.date(2008, 5, 1), 'ORDER': 0, 'title': 'Article #3'} {'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1, 'title': 'Article #2'} {'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2, 'title': 'Article #1'} @@ -583,8 +601,11 @@ Set ``ordering_widget`` to specify the widget class to be used with >>> from myapp.forms import ArticleForm >>> class BaseArticleFormSet(BaseFormSet): ... ordering_widget = HiddenInput + ... - >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_order=True) + >>> ArticleFormSet = formset_factory( + ... ArticleForm, formset=BaseArticleFormSet, can_order=True + ... ) ``get_ordering_widget`` ^^^^^^^^^^^^^^^^^^^^^^^ @@ -600,9 +621,12 @@ use with ``can_order``: >>> from myapp.forms import ArticleForm >>> class BaseArticleFormSet(BaseFormSet): ... def get_ordering_widget(self): - ... return HiddenInput(attrs={'class': 'ordering'}) + ... return HiddenInput(attrs={"class": "ordering"}) + ... - >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_order=True) + >>> ArticleFormSet = formset_factory( + ... ArticleForm, formset=BaseArticleFormSet, can_order=True + ... ) ``can_delete`` -------------- @@ -618,12 +642,15 @@ Lets you create a formset with the ability to select forms for deletion: >>> from django.forms import formset_factory >>> from myapp.forms import ArticleForm >>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True) - >>> formset = ArticleFormSet(initial=[ - ... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, - ... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, - ... ]) + >>> formset = ArticleFormSet( + ... initial=[ + ... {"title": "Article #1", "pub_date": datetime.date(2008, 5, 10)}, + ... {"title": "Article #2", "pub_date": datetime.date(2008, 5, 11)}, + ... ] + ... ) >>> for form in formset: ... print(form) + ... <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title"></div> <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date"></div> <div><label for="id_form-0-DELETE">Delete:</label><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE"></div> @@ -641,23 +668,26 @@ delete fields you can access them with ``deleted_forms``: .. code-block:: pycon >>> data = { - ... 'form-TOTAL_FORMS': '3', - ... 'form-INITIAL_FORMS': '2', - ... 'form-0-title': 'Article #1', - ... 'form-0-pub_date': '2008-05-10', - ... 'form-0-DELETE': 'on', - ... 'form-1-title': 'Article #2', - ... 'form-1-pub_date': '2008-05-11', - ... 'form-1-DELETE': '', - ... 'form-2-title': '', - ... 'form-2-pub_date': '', - ... 'form-2-DELETE': '', + ... "form-TOTAL_FORMS": "3", + ... "form-INITIAL_FORMS": "2", + ... "form-0-title": "Article #1", + ... "form-0-pub_date": "2008-05-10", + ... "form-0-DELETE": "on", + ... "form-1-title": "Article #2", + ... "form-1-pub_date": "2008-05-11", + ... "form-1-DELETE": "", + ... "form-2-title": "", + ... "form-2-pub_date": "", + ... "form-2-DELETE": "", ... } - >>> formset = ArticleFormSet(data, initial=[ - ... {'title': 'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, - ... {'title': 'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, - ... ]) + >>> formset = ArticleFormSet( + ... data, + ... initial=[ + ... {"title": "Article #1", "pub_date": datetime.date(2008, 5, 10)}, + ... {"title": "Article #2", "pub_date": datetime.date(2008, 5, 11)}, + ... ], + ... ) >>> [form.cleaned_data for form in formset.deleted_forms] [{'DELETE': True, 'pub_date': datetime.date(2008, 5, 10), 'title': 'Article #1'}] @@ -676,6 +706,7 @@ them: >>> instances = formset.save(commit=False) >>> for obj in formset.deleted_objects: ... obj.delete() + ... On the other hand, if you are using a plain ``FormSet``, it's up to you to handle ``formset.deleted_forms``, perhaps in your formset's ``save()`` method, @@ -703,8 +734,11 @@ Set ``deletion_widget`` to specify the widget class to be used with >>> from myapp.forms import ArticleForm >>> class BaseArticleFormSet(BaseFormSet): ... deletion_widget = HiddenInput + ... - >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_delete=True) + >>> ArticleFormSet = formset_factory( + ... ArticleForm, formset=BaseArticleFormSet, can_delete=True + ... ) ``get_deletion_widget`` ^^^^^^^^^^^^^^^^^^^^^^^ @@ -720,9 +754,12 @@ use with ``can_delete``: >>> from myapp.forms import ArticleForm >>> class BaseArticleFormSet(BaseFormSet): ... def get_deletion_widget(self): - ... return HiddenInput(attrs={'class': 'deletion'}) + ... return HiddenInput(attrs={"class": "deletion"}) + ... - >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet, can_delete=True) + >>> ArticleFormSet = formset_factory( + ... ArticleForm, formset=BaseArticleFormSet, can_delete=True + ... ) ``can_delete_extra`` -------------------- @@ -751,11 +788,13 @@ fields/attributes of the order and deletion fields: ... def add_fields(self, form, index): ... super().add_fields(form, index) ... form.fields["my_field"] = forms.CharField() + ... >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) >>> formset = ArticleFormSet() >>> for form in formset: ... print(form) + ... <div><label for="id_form-0-title">Title:</label><input type="text" name="form-0-title" id="id_form-0-title"></div> <div><label for="id_form-0-pub_date">Pub date:</label><input type="text" name="form-0-pub_date" id="id_form-0-pub_date"></div> <div><label for="id_form-0-my_field">My field:</label><input type="text" name="form-0-my_field" id="id_form-0-my_field"></div> @@ -778,9 +817,10 @@ You can pass this parameter when instantiating the formset: ... def __init__(self, *args, user, **kwargs): ... self.user = user ... super().__init__(*args, **kwargs) + ... >>> ArticleFormSet = formset_factory(MyArticleForm) - >>> formset = ArticleFormSet(form_kwargs={'user': request.user}) + >>> formset = ArticleFormSet(form_kwargs={"user": request.user}) The ``form_kwargs`` may also depend on the specific form instance. The formset base class provides a ``get_form_kwargs`` method. The method takes a single @@ -795,8 +835,9 @@ argument - the index of the form in the formset. The index is ``None`` for the >>> class BaseArticleFormSet(BaseFormSet): ... def get_form_kwargs(self, index): ... kwargs = super().get_form_kwargs(index) - ... kwargs['custom_kwarg'] = index + ... kwargs["custom_kwarg"] = index ... return kwargs + ... >>> ArticleFormSet = formset_factory(MyArticleForm, formset=BaseArticleFormSet) >>> formset = ArticleFormSet() @@ -924,16 +965,17 @@ use the management form inside the template. Let's look at a sample view:: from django.shortcuts import render from myapp.forms import ArticleForm + def manage_articles(request): ArticleFormSet = formset_factory(ArticleForm) - if request.method == 'POST': + if request.method == "POST": formset = ArticleFormSet(request.POST, request.FILES) if formset.is_valid(): # do something with the formset.cleaned_data pass else: formset = ArticleFormSet() - return render(request, 'manage_articles.html', {'formset': formset}) + return render(request, "manage_articles.html", {"formset": formset}) The ``manage_articles.html`` template might look like this: @@ -1009,22 +1051,27 @@ a look at how this might be accomplished:: from django.shortcuts import render from myapp.forms import ArticleForm, BookForm + def manage_articles(request): ArticleFormSet = formset_factory(ArticleForm) BookFormSet = formset_factory(BookForm) - if request.method == 'POST': - article_formset = ArticleFormSet(request.POST, request.FILES, prefix='articles') - book_formset = BookFormSet(request.POST, request.FILES, prefix='books') + if request.method == "POST": + article_formset = ArticleFormSet(request.POST, request.FILES, prefix="articles") + book_formset = BookFormSet(request.POST, request.FILES, prefix="books") if article_formset.is_valid() and book_formset.is_valid(): # do something with the cleaned_data on the formsets. pass else: - article_formset = ArticleFormSet(prefix='articles') - book_formset = BookFormSet(prefix='books') - return render(request, 'manage_articles.html', { - 'article_formset': article_formset, - 'book_formset': book_formset, - }) + article_formset = ArticleFormSet(prefix="articles") + book_formset = BookFormSet(prefix="books") + return render( + request, + "manage_articles.html", + { + "article_formset": article_formset, + "book_formset": book_formset, + }, + ) You would then render the formsets as normal. It is important to point out that you need to pass ``prefix`` on both the POST and non-POST cases so that diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt index 3d409f5f06..fec2b03251 100644 --- a/docs/topics/forms/index.txt +++ b/docs/topics/forms/index.txt @@ -231,8 +231,9 @@ it in Django is this: from django import forms + class NameForm(forms.Form): - your_name = forms.CharField(label='Your name', max_length=100) + your_name = forms.CharField(label="Your name", max_length=100) This defines a :class:`Form` class with a single field (``your_name``). We've applied a human-friendly label to the field, which will appear in the @@ -284,9 +285,10 @@ want it to be published: from .forms import NameForm + def get_name(request): # if this is a POST request we need to process the form data - if request.method == 'POST': + if request.method == "POST": # create a form instance and populate it with data from the request: form = NameForm(request.POST) # check whether it's valid: @@ -294,13 +296,13 @@ want it to be published: # process the data in form.cleaned_data as required # ... # redirect to a new URL: - return HttpResponseRedirect('/thanks/') + return HttpResponseRedirect("/thanks/") # if a GET (or any other method) we'll create a blank form else: form = NameForm() - return render(request, 'name.html', {'form': form}) + return render(request, "name.html", {"form": form}) If we arrive at this view with a ``GET`` request, it will create an empty form instance and place it in the template context to be rendered. This is what we @@ -408,6 +410,7 @@ to implement "contact me" functionality on a personal website: from django import forms + class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField(widget=forms.Textarea) @@ -458,17 +461,17 @@ Here's how the form data could be processed in the view that handles this form: from django.core.mail import send_mail if form.is_valid(): - subject = form.cleaned_data['subject'] - message = form.cleaned_data['message'] - sender = form.cleaned_data['sender'] - cc_myself = form.cleaned_data['cc_myself'] + subject = form.cleaned_data["subject"] + message = form.cleaned_data["message"] + sender = form.cleaned_data["sender"] + cc_myself = form.cleaned_data["cc_myself"] - recipients = ['info@example.com'] + recipients = ["info@example.com"] if cc_myself: recipients.append(sender) send_mail(subject, message, sender, recipients) - return HttpResponseRedirect('/thanks/') + return HttpResponseRedirect("/thanks/") .. tip:: @@ -532,9 +535,11 @@ Then you can configure the :setting:`FORM_RENDERER` setting: from django.forms.renderers import TemplatesSetting + class CustomFormRenderer(TemplatesSetting): form_template_name = "form_snippet.html" + FORM_RENDERER = "project.settings.CustomFormRenderer" … or for a single form:: @@ -549,8 +554,8 @@ the :meth:`.Form.render`. Here's an example of this being used in a view:: def index(request): form = MyForm() rendered_form = form.render("form_snippet.html") - context = {'form': rendered_form} - return render(request, 'index.html', context) + context = {"form": rendered_form} + return render(request, "index.html", context) See :ref:`ref-forms-api-outputting-html` for more details. diff --git a/docs/topics/forms/media.txt b/docs/topics/forms/media.txt index ab37a19182..5fdd37437e 100644 --- a/docs/topics/forms/media.txt +++ b/docs/topics/forms/media.txt @@ -53,12 +53,13 @@ Here's an example:: from django import forms + class CalendarWidget(forms.TextInput): class Media: css = { - 'all': ['pretty.css'], + "all": ["pretty.css"], } - js = ['animations.js', 'actions.js'] + js = ["animations.js", "actions.js"] This code defines a ``CalendarWidget``, which will be based on ``TextInput``. Every time the CalendarWidget is used on a form, that form will be directed @@ -98,8 +99,8 @@ provide two CSS options -- one for the screen, and one for print:: class Media: css = { - 'screen': ['pretty.css'], - 'print': ['newspaper.css'], + "screen": ["pretty.css"], + "print": ["newspaper.css"], } If a group of CSS files are appropriate for multiple output media types, @@ -109,9 +110,9 @@ requirements:: class Media: css = { - 'screen': ['pretty.css'], - 'tv,projector': ['lo_res.css'], - 'print': ['newspaper.css'], + "screen": ["pretty.css"], + "tv,projector": ["lo_res.css"], + "print": ["newspaper.css"], } If this last CSS definition were to be rendered, it would become the following HTML: @@ -145,9 +146,10 @@ example above: >>> class FancyCalendarWidget(CalendarWidget): ... class Media: ... css = { - ... 'all': ['fancy.css'], + ... "all": ["fancy.css"], ... } - ... js = ['whizbang.js'] + ... js = ["whizbang.js"] + ... >>> w = FancyCalendarWidget() >>> print(w.media) @@ -167,9 +169,10 @@ an ``extend=False`` declaration to the ``Media`` declaration: ... class Media: ... extend = False ... css = { - ... 'all': ['fancy.css'], + ... "all": ["fancy.css"], ... } - ... js = ['whizbang.js'] + ... js = ["whizbang.js"] + ... >>> w = FancyCalendarWidget() >>> print(w.media) @@ -198,8 +201,9 @@ be defined in a dynamic fashion:: class CalendarWidget(forms.TextInput): @property def media(self): - return forms.Media(css={'all': ['pretty.css']}, - js=['animations.js', 'actions.js']) + return forms.Media( + css={"all": ["pretty.css"]}, js=["animations.js", "actions.js"] + ) See the section on `Media objects`_ for more details on how to construct return values for dynamic ``media`` properties. @@ -235,9 +239,10 @@ was ``None``: >>> class CalendarWidget(forms.TextInput): ... class Media: ... css = { - ... 'all': ['/css/pretty.css'], + ... "all": ["/css/pretty.css"], ... } - ... js = ['animations.js', 'http://othersite.com/actions.js'] + ... js = ["animations.js", "http://othersite.com/actions.js"] + ... >>> w = CalendarWidget() >>> print(w.media) @@ -283,10 +288,12 @@ outputting the complete HTML ``<script>`` or ``<link>`` tag content: ... class JSPath: ... def __str__(self): ... return '<script src="https://example.org/asset.js" rel="stylesheet">' + ... >>> class SomeWidget(forms.TextInput): ... class Media: ... js = [JSPath()] + ... ``Media`` objects ================= @@ -313,7 +320,7 @@ operator to filter out a medium of interest. For example: <script src="http://static.example.com/animations.js"></script> <script src="http://static.example.com/actions.js"></script> - >>> print(w.media['css']) + >>> print(w.media["css"]) <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet"> When you use the subscript operator, the value that is returned is a @@ -332,13 +339,15 @@ specified by both: >>> class CalendarWidget(forms.TextInput): ... class Media: ... css = { - ... 'all': ['pretty.css'], + ... "all": ["pretty.css"], ... } - ... js = ['animations.js', 'actions.js'] + ... js = ["animations.js", "actions.js"] + ... >>> class OtherWidget(forms.TextInput): ... class Media: - ... js = ['whizbang.js'] + ... js = ["whizbang.js"] + ... >>> w1 = CalendarWidget() >>> w2 = OtherWidget() @@ -365,10 +374,12 @@ For example: >>> from django import forms >>> class CalendarWidget(forms.TextInput): ... class Media: - ... js = ['jQuery.js', 'calendar.js', 'noConflict.js'] + ... js = ["jQuery.js", "calendar.js", "noConflict.js"] + ... >>> class TimeWidget(forms.TextInput): ... class Media: - ... js = ['jQuery.js', 'time.js', 'noConflict.js'] + ... js = ["jQuery.js", "time.js", "noConflict.js"] + ... >>> w1 = CalendarWidget() >>> w2 = TimeWidget() >>> print(w1.media + w2.media) @@ -400,6 +411,7 @@ are part of the form: >>> class ContactForm(forms.Form): ... date = DateField(widget=CalendarWidget) ... name = CharField(max_length=40, widget=OtherWidget) + ... >>> f = ContactForm() >>> f.media @@ -416,11 +428,11 @@ CSS for form layout -- add a ``Media`` declaration to the form: >>> class ContactForm(forms.Form): ... date = DateField(widget=CalendarWidget) ... name = CharField(max_length=40, widget=OtherWidget) - ... ... class Media: ... css = { - ... 'all': ['layout.css'], + ... "all": ["layout.css"], ... } + ... >>> f = ContactForm() >>> f.media diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt index 50fb12ef6e..7f3c042f30 100644 --- a/docs/topics/forms/modelforms.txt +++ b/docs/topics/forms/modelforms.txt @@ -28,7 +28,8 @@ For example: >>> class ArticleForm(ModelForm): ... class Meta: ... model = Article - ... fields = ['pub_date', 'headline', 'content', 'reporter'] + ... fields = ["pub_date", "headline", "content", "reporter"] + ... # Creating a form to add an article. >>> form = ArticleForm() @@ -173,11 +174,12 @@ Consider this set of models:: from django.forms import ModelForm TITLE_CHOICES = [ - ('MR', 'Mr.'), - ('MRS', 'Mrs.'), - ('MS', 'Ms.'), + ("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) @@ -186,19 +188,22 @@ Consider this set of models:: 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'] + fields = ["name", "title", "birth_date"] + class BookForm(ModelForm): class Meta: model = Book - fields = ['name', 'authors'] + fields = ["name", "authors"] With these models, the ``ModelForm`` subclasses above would be roughly @@ -207,6 +212,7 @@ we'll discuss in a moment.):: from django import forms + class AuthorForm(forms.Form): name = forms.CharField(max_length=100) title = forms.CharField( @@ -215,6 +221,7 @@ we'll discuss in a moment.):: ) birth_date = forms.DateField(required=False) + class BookForm(forms.Form): name = forms.CharField(max_length=100) authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all()) @@ -305,11 +312,12 @@ to the ``error_messages`` dictionary of the ``ModelForm``’s inner ``Meta`` cla 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.", + "unique_together": "%(model_name)s's %(field_labels)s are not unique.", } } @@ -388,7 +396,7 @@ you've manually saved the instance produced by the form, you can invoke >>> new_author = f.save(commit=False) # Modify the author in some way. - >>> new_author.some_field = 'some_value' + >>> new_author.some_field = "some_value" # Save the new instance. >>> new_author.save() @@ -440,10 +448,11 @@ these security concerns do not apply to you: from django.forms import ModelForm + class AuthorForm(ModelForm): class Meta: model = Author - fields = '__all__' + fields = "__all__" 2. Set the ``exclude`` attribute of the ``ModelForm``’s inner ``Meta`` class to a list of fields to be excluded from the form. @@ -453,7 +462,7 @@ these security concerns do not apply to you: class PartialAuthorForm(ModelForm): class Meta: model = Author - exclude = ['title'] + exclude = ["title"] Since the ``Author`` model has the 3 fields ``name``, ``title`` and ``birth_date``, this will result in the fields ``name`` and ``birth_date`` @@ -481,7 +490,7 @@ include that field. avoid this failure, you must instantiate your model with initial values for the missing, but required fields:: - author = Author(title='Mr') + author = Author(title="Mr") form = PartialAuthorForm(request.POST, instance=author) form.save() @@ -490,7 +499,7 @@ include that field. form = PartialAuthorForm(request.POST) author = form.save(commit=False) - author.title = 'Mr' + author.title = "Mr" author.save() See the `section on saving forms`_ for more details on using @@ -519,12 +528,13 @@ For example, if you want the ``CharField`` for the ``name`` attribute of from django.forms import ModelForm, Textarea from myapp.models import Author + class AuthorForm(ModelForm): class Meta: model = Author - fields = ['name', 'title', 'birth_date'] + fields = ["name", "title", "birth_date"] widgets = { - 'name': Textarea(attrs={'cols': 80, 'rows': 20}), + "name": Textarea(attrs={"cols": 80, "rows": 20}), } The ``widgets`` dictionary accepts either widget instances (e.g., @@ -540,19 +550,20 @@ the ``name`` field:: from django.utils.translation import gettext_lazy as _ + class AuthorForm(ModelForm): class Meta: model = Author - fields = ['name', 'title', 'birth_date'] + fields = ["name", "title", "birth_date"] labels = { - 'name': _('Writer'), + "name": _("Writer"), } help_texts = { - 'name': _('Some useful help text.'), + "name": _("Some useful help text."), } error_messages = { - 'name': { - 'max_length': _("This writer's name is too long."), + "name": { + "max_length": _("This writer's name is too long."), }, } @@ -565,12 +576,13 @@ 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'] + fields = ["pub_date", "headline", "content", "reporter", "slug"] field_classes = { - 'slug': MySlugFormField, + "slug": MySlugFormField, } or:: @@ -578,11 +590,13 @@ or:: from django.forms import ModelForm from myapp.models import Article + def formfield_for_dbfield(db_field, **kwargs): if db_field.name == "slug": return MySlugFormField() return db_field.formfield(**kwargs) + class ArticleForm(ModelForm): class Meta: model = Article @@ -599,12 +613,13 @@ 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'] + fields = ["pub_date", "headline", "content", "reporter", "slug"] .. note:: @@ -635,7 +650,7 @@ the field declaratively and setting its ``validators`` parameter:: max_length=200, null=True, blank=True, - help_text='Use puns liberally', + help_text="Use puns liberally", ) content = models.TextField() @@ -647,12 +662,12 @@ the field declaratively and setting its ``validators`` parameter:: headline = MyFormField( max_length=200, required=False, - help_text='Use puns liberally', + help_text="Use puns liberally", ) class Meta: model = Article - fields = ['headline', 'content'] + 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, @@ -695,6 +710,7 @@ 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. @@ -706,7 +722,8 @@ the ``Meta.fields`` or ``Meta.exclude`` lists: >>> class RestrictedArticleForm(EnhancedArticleForm): ... class Meta(ArticleForm.Meta): - ... exclude = ['body'] + ... exclude = ["body"] + ... This adds the extra method from the ``EnhancedArticleForm`` and modifies the original ``ArticleForm.Meta`` to remove one field. @@ -744,8 +761,8 @@ 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() + >>> form = ArticleForm(initial={"headline": "Initial headline"}, instance=article) + >>> form["headline"].value() 'Initial headline' .. _modelforms-factory: @@ -770,8 +787,7 @@ specifying the widgets to be used for a given field: .. code-block:: pycon >>> from django.forms import Textarea - >>> Form = modelform_factory(Book, form=BookForm, - ... widgets={"title": 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 @@ -799,7 +815,7 @@ 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']) + >>> 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 @@ -807,7 +823,7 @@ exclude: .. code-block:: pycon - >>> AuthorFormSet = modelformset_factory(Author, exclude=['birth_date']) + >>> 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: @@ -848,7 +864,7 @@ queryset that includes all objects in the model (e.g., .. code-block:: pycon - >>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O')) + >>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith="O")) Alternatively, you can create a subclass that sets ``self.queryset`` in ``__init__``:: @@ -856,17 +872,19 @@ Alternatively, you can create a subclass that sets ``self.queryset`` in 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') + self.queryset = Author.objects.filter(name__startswith="O") Then, pass your ``BaseAuthorFormSet`` class to the factory function: .. code-block:: pycon >>> AuthorFormSet = modelformset_factory( - ... Author, fields=['name', 'title'], formset=BaseAuthorFormSet) + ... 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: @@ -886,7 +904,7 @@ you can create a custom model form that has custom validation:: class AuthorForm(forms.ModelForm): class Meta: model = Author - fields = ['name', 'title'] + fields = ["name", "title"] def clean_name(self): # custom validation for the name field @@ -911,8 +929,10 @@ class of a ``ModelForm`` works: .. code-block:: pycon >>> AuthorFormSet = modelformset_factory( - ... Author, fields=['name', 'title'], - ... widgets={'name': Textarea(attrs={'cols': 80, 'rows': 20})}) + ... Author, + ... fields=["name", "title"], + ... widgets={"name": Textarea(attrs={"cols": 80, "rows": 20})}, + ... ) Enabling localization for fields with ``localized_fields`` ---------------------------------------------------------- @@ -975,6 +995,7 @@ Pass ``commit=False`` to return the unsaved model instances: >>> 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 @@ -1001,11 +1022,11 @@ extra forms displayed. .. code-block:: pycon - >>> Author.objects.order_by('name') + >>> 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')) + >>> 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'] @@ -1020,10 +1041,11 @@ so long as the total number of forms does not exceed ``max_num``: .. code-block:: pycon - >>> AuthorFormSet = modelformset_factory(Author, fields=['name'], max_num=4, extra=2) - >>> formset = AuthorFormSet(queryset=Author.objects.order_by('name')) + >>> 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) + ... <div><label for="id_form-0-name">Name:</label><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"></div> <div><label for="id_form-1-name">Name:</label><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"></div> <div><label for="id_form-2-name">Name:</label><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"></div> @@ -1044,7 +1066,7 @@ objects: >>> AuthorFormSet = modelformset_factory( ... Author, - ... fields=['name', 'title'], + ... fields=["name", "title"], ... edit_only=True, ... ) @@ -1061,16 +1083,17 @@ formset to edit ``Author`` model instances:: 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': + 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}) + 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 @@ -1091,6 +1114,7 @@ class's ``clean`` method:: from django.forms import BaseModelFormSet + class MyModelFormSet(BaseModelFormSet): def clean(self): super().clean() @@ -1107,13 +1131,14 @@ to modify a value in ``ModelFormSet.clean()`` you must modify 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 + name = form.cleaned_data["name"].upper() + form.cleaned_data["name"] = name # update the instance value. form.instance.name = name @@ -1127,12 +1152,14 @@ formset:: 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') + AuthorFormSet = modelformset_factory(Author, fields=["name", "title"]) + queryset = Author.objects.filter(name__startswith="O") if request.method == "POST": formset = AuthorFormSet( - request.POST, request.FILES, + request.POST, + request.FILES, queryset=queryset, ) if formset.is_valid(): @@ -1140,7 +1167,7 @@ formset:: # Do something. else: formset = AuthorFormSet(queryset=queryset) - return render(request, 'manage_authors.html', {'formset': formset}) + 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. @@ -1222,9 +1249,11 @@ 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) @@ -1235,8 +1264,8 @@ a particular author, you could do this: .. code-block:: pycon >>> from django.forms import inlineformset_factory - >>> BookFormSet = inlineformset_factory(Author, Book, fields=['title']) - >>> author = Author.objects.get(name='Mike Royko') + >>> BookFormSet = inlineformset_factory(Author, Book, fields=["title"]) + >>> author = Author.objects.get(name="Mike Royko") >>> formset = BookFormSet(instance=author) ``BookFormSet``'s :ref:`prefix <formset-prefix>` is ``'book_set'`` @@ -1264,6 +1293,7 @@ For example, if you want to override ``clean()``:: from django.forms import BaseInlineFormSet + class CustomInlineFormSet(BaseInlineFormSet): def clean(self): super().clean() @@ -1280,9 +1310,10 @@ Then when you create your inline formset, pass in the optional argument .. code-block:: pycon >>> from django.forms import inlineformset_factory - >>> BookFormSet = inlineformset_factory(Author, Book, fields=['title'], - ... formset=CustomInlineFormSet) - >>> author = Author.objects.get(name='Mike Royko') + >>> 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 @@ -1296,12 +1327,12 @@ the following model:: from_friend = models.ForeignKey( Friend, on_delete=models.CASCADE, - related_name='from_friends', + related_name="from_friends", ) to_friend = models.ForeignKey( Friend, on_delete=models.CASCADE, - related_name='friends', + related_name="friends", ) length_in_months = models.IntegerField() @@ -1310,8 +1341,9 @@ To resolve this, you can use ``fk_name`` to .. code-block:: pycon - >>> FriendshipFormSet = inlineformset_factory(Friend, Friendship, fk_name='from_friend', - ... fields=['to_friend', 'length_in_months']) + >>> FriendshipFormSet = inlineformset_factory( + ... Friend, Friendship, fk_name="from_friend", fields=["to_friend", "length_in_months"] + ... ) Using an inline formset in a view --------------------------------- @@ -1321,7 +1353,7 @@ 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']) + BookInlineFormSet = inlineformset_factory(Author, Book, fields=["title"]) if request.method == "POST": formset = BookInlineFormSet(request.POST, request.FILES, instance=author) if formset.is_valid(): @@ -1330,7 +1362,7 @@ of a model. Here's how you can do that:: return HttpResponseRedirect(author.get_absolute_url()) else: formset = BookInlineFormSet(instance=author) - return render(request, 'manage_books.html', {'formset': formset}) + return render(request, "manage_books.html", {"formset": formset}) Notice how we pass ``instance`` in both the ``POST`` and ``GET`` cases. |
