Django Template to Render Again When Swiping Back on Phone
Django Tutorial Part 9: Working with forms
In this tutorial, nosotros'll evidence y'all how to work with HTML Forms in Django, and, in particular, the easiest style to write forms to create, update, and delete model instances. As office of this demonstration, nosotros'll extend the LocalLibrary website so that librarians can renew books, and create, update, and delete authors using our own forms (rather than using the admin awarding).
Overview
An HTML Form is a group of one or more fields/widgets on a web page, which tin can be used to collect data from users for submission to a server. Forms are a flexible mechanism for collecting user input considering there are suitable widgets for entering many unlike types of data, including text boxes, checkboxes, radio buttons, date pickers and and then on. Forms are also a relatively secure way of sharing data with the server, as they allow us to send data in Mail service
requests with cross-site request forgery protection.
While nosotros haven't created any forms in this tutorial and so far, we've already encountered them in the Django Admin site — for case, the screenshot beneath shows a form for editing one of our Book models, comprised of a number of option lists and text editors.
Working with forms can be complicated! Developers demand to write HTML for the class, validate and properly sanitize entered information on the server (and possibly also in the browser), repost the class with mistake messages to inform users of any invalid fields, handle the information when it has successfully been submitted, and finally reply to the user in some way to signal success. Django Forms take a lot of the work out of all these steps, by providing a framework that lets y'all define forms and their fields programmatically, and then utilise these objects to both generate the form HTML code and handle much of the validation and user interaction.
In this tutorial, nosotros're going to show yous a few of the ways you can create and work with forms, and in particular, how the generic editing views can significantly reduce the corporeality of work y'all demand to do to create forms to manipulate your models. Forth the way, nosotros'll extend our LocalLibrary awarding by calculation a form to allow librarians to renew library books, and we'll create pages to create, edit and delete books and authors (reproducing a basic version of the grade shown higher up for editing books).
HTML Forms
Outset, a cursory overview of HTML Forms. Consider a simple HTML class, with a single text field for entering the name of some "team", and its associated label:
The form is divers in HTML as a collection of elements inside <form>...</form>
tags, containing at least ane input
chemical element of type="submit"
.
<form action = "/team_name_url/" method = "post" > <label for = "team_name" > Enter name: </characterization > <input id = "team_name" type = "text" name = "name_field" value = "Default proper noun for team." > <input type = "submit" value = "OK" > </form >
While here we merely have 1 text field for entering the team name, a form may take whatsoever number of other input elements and their associated labels. The field's type
attribute defines what sort of widget will be displayed. The proper noun
and id
of the field are used to identify the field in JavaScript/CSS/HTML, while value
defines the initial value for the field when it is first displayed. The matching team label is specified using the label
tag (encounter "Enter name" in a higher place), with a for
field containing the id
value of the associated input
.
The submit
input will be displayed equally a push button by default. This tin can be pressed to upload the information in all the other input elements in the form to the server (in this case, just the team_name
field). The course attributes define the HTTP method
used to send the information and the destination of the data on the server (action
):
-
action
: The resources/URL where data is to exist sent for processing when the form is submitted. If this is not set (or set to an empty string), then the class will be submitted back to the current page URL. -
method
: The HTTP method used to send the data: post or get.- The
POST
method should ever exist used if the data is going to effect in a change to the server's database, because information technology can be fabricated more resistant to cross-site forgery request attacks. - The
GET
method should merely be used for forms that don't change user data (for example, a search class). Information technology is recommended for when y'all want to be able to bookmark or share the URL.
- The
The role of the server is first to render the initial class country — either containing blank fields or pre-populated with initial values. Later the user presses the submit button, the server will receive the form data with values from the web browser and must validate the data. If the form contains invalid data, the server should display the form again, this time with user-entered data in "valid" fields and letters to draw the problem for the invalid fields. Once the server gets a request with all valid form data, it tin can perform an advisable activity (such as: saving the data, returning the result of a search, uploading a file, etc.) and and then notify the user.
As you can imagine, creating the HTML, validating the returned data, re-displaying the entered data with error reports if needed, and performing the desired functioning on valid data tin can all take quite a lot of effort to "get right". Django makes this a lot easier past taking away some of the heavy lifting and repetitive code!
Django form handling procedure
Django's course handling uses all of the same techniques that we learned about in previous tutorials (for displaying information about our models): the view gets a request, performs any deportment required including reading data from the models, then generates and returns an HTML folio (from a template, into which we pass a context containing the data to be displayed). What makes things more complicated is that the server likewise needs to exist able to procedure data provided by the user, and redisplay the folio if there are any errors.
A process flowchart of how Django handles grade requests is shown beneath, starting with a asking for a page containing a class (shown in green).
Based on the diagram higher up, the main things that Django's form handling does are:
- Display the default form the get-go time it is requested by the user.
- The form may contain bare fields if you're creating a new record, or information technology may be pre-populated with initial values (for example, if you are irresolute a record, or have useful default initial values).
- The class is referred to as unbound at this point, because it isn't associated with any user-entered information (though it may accept initial values).
- Receive data from a submit asking and bind it to the course.
- Binding data to the form means that the user-entered data and any errors are available when we demand to redisplay the class.
- Clean and validate the information.
- Cleaning the information performs sanitization of the input fields, such as removing invalid characters that might be used to send malicious content to the server, and converts them into consistent Python types.
- Validation checks that the values are advisable for the field (for case, that they are in the correct date range, aren't too short or too long, etc.)
- If any data is invalid, re-display the grade, this time with any user populated values and mistake messages for the trouble fields.
- If all data is valid, perform required actions (such equally salvage the data, send an email, return the result of a search, upload a file, and so on).
- Once all actions are consummate, redirect the user to another page.
Django provides a number of tools and approaches to assist you lot with the tasks detailed in a higher place. The most fundamental is the Form
class, which simplifies both generation of grade HTML and data cleaning/validation. In the adjacent department, nosotros depict how forms work using the practical example of a page to allow librarians to renew books.
Notation: Understanding how Course
is used will help you lot when we discuss Django's more "high level" form framework classes.
Renew-volume form using a Form and function view
Adjacent, nosotros're going to add a page to let librarians to renew borrowed books. To do this we'll create a form that allows users to enter a appointment value. We'll seed the field with an initial value 3 weeks from the current date (the normal borrowing period), and add together some validation to ensure that the librarian can't enter a date in the past or a date too far in the future. When a valid date has been entered, we'll write it to the electric current record'due south BookInstance.due_back
field.
The example will utilize a role-based view and a Form
form. The following sections explicate how forms work, and the changes you need to make to our ongoing LocalLibrary project.
Course
The Course
class is the heart of Django'south class treatment system. It specifies the fields in the class, their layout, display widgets, labels, initial values, valid values, and (once validated) the error letters associated with invalid fields. The course besides provides methods for rendering itself in templates using predefined formats (tables, lists, etc.) or for getting the value of any element (enabling fine-grained manual rendering).
Declaring a Form
The declaration syntax for a Form
is very like to that for declaring a Model
, and shares the same field types (and some similar parameters). This makes sense because in both cases we demand to ensure that each field handles the right types of information, is constrained to valid data, and has a description for brandish/documentation.
Form data is stored in an awarding's forms.py file, inside the awarding directory. Create and open the file locallibrary/itemize/forms.py. To create a Form
, we import the forms
library, derive from the Class
class, and declare the grade'southward fields. A very basic form class for our library book renewal form is shown below — add this to your new file:
from django import forms class RenewBookForm (forms.Form) : renewal_date = forms.DateField(help_text= "Enter a date between now and 4 weeks (default 3)." )
Form fields
In this case, we take a single DateField
for entering the renewal date that volition render in HTML with a blank value, the default label "Renewal engagement:", and some helpful usage text: "Enter a engagement between now and 4 weeks (default iii weeks)." Equally none of the other optional arguments are specified the field volition have dates using the input_formats: YYYY-MM-DD (2016-11-06), MM/DD/YYYY (02/26/2016), MM/DD/YY (ten/25/16), and volition be rendered using the default widget: DateInput.
There are many other types of form fields, which you lot will largely recognize from their similarity to the equivalent model field classes: BooleanField
, CharField
, ChoiceField
, TypedChoiceField
, DateField
, DateTimeField
, DecimalField
, DurationField
, EmailField
, FileField
, FilePathField
, FloatField
, ImageField
, IntegerField
, GenericIPAddressField
, MultipleChoiceField
, TypedMultipleChoiceField
, NullBooleanField
, RegexField
, SlugField
, TimeField
, URLField
, UUIDField
, ComboField
, MultiValueField
, SplitDateTimeField
, ModelMultipleChoiceField
, ModelChoiceField
.
The arguments that are common to most fields are listed beneath (these have sensible default values):
-
required
: IfTrue
, the field may not exist left blank or given aNone
value. Fields are required by default, so you would set uprequired=False
to allow blank values in the grade. -
label
: The label to employ when rendering the field in HTML. If a label is not specified, Django will create ane from the field proper name past capitalizing the first alphabetic character and replacing underscores with spaces (eastward.g. Renewal date). -
label_suffix
: By default, a colon is displayed after the label (e.grand. Renewal date:). This argument allows you to specify a different suffix containing other character(due south). -
initial
: The initial value for the field when the form is displayed. -
widget
: The display widget to use. -
help_text
(as seen in the instance to a higher place): Additional text that tin exist displayed in forms to explain how to use the field. -
error_messages
: A listing of error letters for the field. Yous tin override these with your own messages if needed. -
validators
: A list of functions that will be called on the field when it is validated. -
localize
: Enables the localization of form data input (come across link for more information). -
disabled
: The field is displayed simply its value cannot exist edited if this isTrue
. The default isFake
.
Validation
Django provides numerous places where you tin can validate your data. The easiest way to validate a unmarried field is to override the method clean_<fieldname>()
for the field you want to check. Then for instance, we can validate that entered renewal_date
values are between now and iv weeks by implementing clean_renewal_date()
as shown below.
Update your forms.py file so information technology looks similar this:
import datetime from django import forms from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ class RenewBookForm (forms.Form) : renewal_date = forms.DateField(help_text= "Enter a date between now and 4 weeks (default 3)." ) def clean_renewal_date (self) : data = cocky.cleaned_data[ 'renewal_date' ] # Check if a date is not in the past. if data < datetime.date.today( ) : enhance ValidationError(_( 'Invalid date - renewal in by' ) ) # Check if a engagement is in the immune range (+iv weeks from today). if information > datetime.date.today( ) + datetime.timedelta(weeks= 4 ) : raise ValidationError(_( 'Invalid date - renewal more than iv weeks alee' ) ) # Remember to always render the cleaned data. return information
In that location are ii important things to notation. The first is that we get our information using cocky.cleaned_data['renewal_date']
and that we return this data whether or not nosotros change it at the cease of the function. This step gets u.s. the information "cleaned" and sanitized of potentially unsafe input using the default validators, and converted into the correct standard type for the data (in this case a Python datetime.datetime
object).
The second bespeak is that if a value falls outside our range we raise a ValidationError
, specifying the mistake text that we desire to display in the form if an invalid value is entered. The example above likewise wraps this text in one of Django'south translation functions, gettext_lazy()
(imported as _()
), which is good practice if you lot desire to interpret your site later.
Note: In that location are numerous other methods and examples for validating forms in Form and field validation (Django docs). For case, in cases where you have multiple fields that depend on each other, you can override the Form.make clean() function and once again raise a ValidationError
.
That'due south all nosotros need for the form in this case!
URL configuration
Before nosotros create our view, let's add a URL configuration for the renew-books page. Re-create the following configuration to the bottom of locallibrary/catalog/urls.py:
urlpatterns += [ path( 'volume/<uuid:pk>/renew/' , views.renew_book_librarian, name= 'renew-book-librarian' ) , ]
The URL configuration will redirect URLs with the format /itemize/book/<bookinstance_id>/renew/ to the function named renew_book_librarian()
in views.py, and ship the BookInstance
id as the parameter named pk
. The pattern only matches if pk
is a correctly formatted uuid
.
Note: We can proper noun our captured URL data "pk
" anything nosotros like, considering nosotros have consummate control over the view function (we're not using a generic detail view class that expects parameters with a certain proper name). However, pk
short for "principal key", is a reasonable convention to utilise!
View
Equally discussed in the Django grade handling process above, the view has to render the default form when it is start called and and so either re-render information technology with error messages if the information is invalid, or process the data and redirect to a new page if the data is valid. In order to perform these different deportment, the view has to be able to know whether it is being called for the start time to return the default form, or a subsequent time to validate data.
For forms that use a POST
asking to submit data to the server, the most common pattern is for the view to test against the POST
request type (if request.method == 'Post':
) to identify class validation requests and Become
(using an else
condition) to identify the initial course creation request. If y'all want to submit your information using a Become
asking, then a typical approach for identifying whether this is the first or subsequent view invocation is to read the course information (east.grand. to read a hidden value in the form).
The volume renewal process will be writing to our database, so, past convention, nosotros utilize the POST
request approach. The code fragment beneath shows the (very standard) pattern for this sort of part view.
import datetime from django.shortcuts import render, get_object_or_404 from django.http import HttpResponseRedirect from django.urls import reverse from catalog.forms import RenewBookForm def renew_book_librarian (asking, pk) : book_instance = get_object_or_404(BookInstance, pk=pk) # If this is a Post request then procedure the Grade data if request.method == 'Mail service' : # Create a form instance and populate information technology with data from the asking (binding): form = RenewBookForm(asking.Post) # Bank check if the form is valid: if grade.is_valid( ) : # process the data in class.cleaned_data as required (hither nosotros just write it to the model due_back field) book_instance.due_back = form.cleaned_data[ 'renewal_date' ] book_instance.save( ) # redirect to a new URL: return HttpResponseRedirect(reverse( 'all-borrowed' ) ) # If this is a Go (or whatever other method) create the default form. else : proposed_renewal_date = datetime.date.today( ) + datetime.timedelta(weeks= 3 ) form = RenewBookForm(initial= { 'renewal_date' : proposed_renewal_date} ) context = { 'form' : form, 'book_instance' : book_instance, } return render(request, 'catalog/book_renew_librarian.html' , context)
Kickoff, we import our grade (RenewBookForm
) and a number of other useful objects/methods used in the torso of the view function:
-
get_object_or_404()
: Returns a specified object from a model based on its master key value, and raises anHttp404
exception (not found) if the record does non be. -
HttpResponseRedirect
: This creates a redirect to a specified URL (HTTP status code 302). -
reverse()
: This generates a URL from a URL configuration proper noun and a prepare of arguments. It is the Python equivalent of theurl
tag that we've been using in our templates. -
datetime
: A Python library for manipulating dates and times.
In the view, nosotros first use the pk
argument in get_object_or_404()
to get the current BookInstance
(if this does non be, the view will immediately exit and the page will display a "not found" mistake). If this is not a POST
request (handled by the else
clause) then we create the default form passing in an initial
value for the renewal_date
field, 3 weeks from the current date.
book_instance = get_object_or_404(BookInstance, pk=pk) # If this is a GET (or whatever other method) create the default course else : proposed_renewal_date = datetime.date.today( ) + datetime.timedelta(weeks= 3 ) form = RenewBookForm(initial= { 'renewal_date' : proposed_renewal_date} ) context = { 'form' : form, 'book_instance' : book_instance, } return return(request, 'itemize/book_renew_librarian.html' , context)
After creating the form, we call render()
to create the HTML page, specifying the template and a context that contains our grade. In this case, the context also contains our BookInstance
, which we'll use in the template to provide information about the book we're renewing.
However, if this is a POST
asking, then we create our form
object and populate it with information from the asking. This process is called "binding" and allows us to validate the form.
We then check if the grade is valid, which runs all the validation code on all of the fields — including both the generic code to check that our date field is actually a valid appointment and our specific form's clean_renewal_date()
office to check the engagement is in the right range.
book_instance = get_object_or_404(BookInstance, pk=pk) # If this is a POST request then procedure the Grade data if asking.method == 'POST' : # Create a form example and populate information technology with data from the asking (binding): class = RenewBookForm(request.Postal service) # Check if the course is valid: if form.is_valid( ) : # process the information in course.cleaned_data as required (here we just write it to the model due_back field) book_instance.due_back = class.cleaned_data[ 'renewal_date' ] book_instance.save( ) # redirect to a new URL: return HttpResponseRedirect(opposite( 'all-borrowed' ) ) context = { 'class' : form, 'book_instance' : book_instance, } return render(asking, 'catalog/book_renew_librarian.html' , context)
If the form is not valid we phone call return()
again, but this time the form value passed in the context will include error messages.
If the class is valid, then nosotros tin start to utilize the data, accessing it through the course.cleaned_data
attribute (e.g. data = form.cleaned_data['renewal_date']
). Here, we just save the information into the due_back
value of the associated BookInstance
object.
Alarm: While you can likewise access the form data directly through the request (for case, request.POST['renewal_date']
or asking.Get['renewal_date']
if using a Go asking), this is Not recommended. The cleaned data is sanitized, validated, and converted into Python-friendly types.
The final footstep in the grade-handling part of the view is to redirect to another page, ordinarily a "success" page. In this case, we employ HttpResponseRedirect
and reverse()
to redirect to the view named 'all-borrowed'
(this was created as the "claiming" in Django Tutorial Part 8: User authentication and permissions). If yous didn't create that folio consider redirecting to the home folio at URL '/
').
That'due south everything needed for the form treatment itself, merely we withal demand to restrict access to the view to only logged-in librarians who accept permission to renew books. We use @login_required
to crave that the user is logged in, and the @permission_required
function decorator with our existing can_mark_returned
permission to allow access (decorators are processed in order). Annotation that nosotros probably should have created a new permission setting in BookInstance
("can_renew
"), simply we volition reuse the existing one to keep the example uncomplicated.
The final view is therefore as shown below. Delight copy this into the lesser of locallibrary/itemize/views.py.
import datetime from django.contrib.auth.decorators import login_required, permission_required from django.shortcuts import get_object_or_404 from django.http import HttpResponseRedirect from django.urls import opposite from itemize.forms import RenewBookForm @login_required @permission_required ( 'itemize.can_mark_returned' , raise_exception= Truthful ) def renew_book_librarian (asking, pk) : """View function for renewing a specific BookInstance by librarian.""" book_instance = get_object_or_404(BookInstance, pk=pk) # If this is a Mail request and so process the Form data if request.method == 'POST' : # Create a form instance and populate it with data from the request (bounden): course = RenewBookForm(request.Mail service) # Cheque if the form is valid: if form.is_valid( ) : # process the data in form.cleaned_data as required (here we just write it to the model due_back field) book_instance.due_back = form.cleaned_data[ 'renewal_date' ] book_instance.relieve( ) # redirect to a new URL: render HttpResponseRedirect(reverse( 'all-borrowed' ) ) # If this is a Get (or any other method) create the default form. else : proposed_renewal_date = datetime.appointment.today( ) + datetime.timedelta(weeks= 3 ) form = RenewBookForm(initial= { 'renewal_date' : proposed_renewal_date} ) context = { 'form' : form, 'book_instance' : book_instance, } return render(request, 'itemize/book_renew_librarian.html' , context)
The template
Create the template referenced in the view (/catalog/templates/catalog/book_renew_librarian.html) and re-create the lawmaking below into it:
{% extends "base_generic.html" %} {% block content %} <h1 > Renew: {{ book_instance.book.title }} </h1 > <p > Borrower: {{ book_instance.borrower }} </p > <p{% if book_instance.is_overdue %} class="text-danger"{% endif %}>Due date: {{ book_instance.due_back }} </p > <class action = " " method = "post" > {% csrf_token %} <table > {{ form.as_table }} </tabular array > <input type = "submit" value = "Submit" > </form > {% endblock %}
Most of this will exist completely familiar from previous tutorials.
Nosotros extend the base template and and then redefine the content cake. We are able to reference {{ book_instance }}
(and its variables) because it was passed into the context object in the return()
function, and we use these to list the book championship, borrower, and the original due date.
The form code is relatively uncomplicated. Starting time, we declare the grade
tags, specifying where the form is to be submitted (activeness
) and the method
for submitting the data (in this case an "HTTP POST
") — if you recall the HTML Forms overview at the top of the folio, an empty action
as shown, means that the form data will exist posted back to the current URL of the page (which is what we want). Inside the tags, nosotros define the submit
input, which a user can press to submit the data. The {% csrf_token %}
added just inside the course tags is role of Django's cantankerous-site forgery protection.
Annotation: Add the {% csrf_token %}
to every Django template you create that uses Post
to submit data. This will reduce the risk of forms being hijacked by malicious users.
All that's left is the {{ form }}
template variable, which we passed to the template in the context dictionary. Perhaps unsurprisingly, when used as shown this provides the default rendering of all the form fields, including their labels, widgets, and help text — the rendering is as shown below:
<tr > <th > <characterization for = "id_renewal_date" > Renewal date: </label > </thursday > <td > <input id = "id_renewal_date" name = "renewal_date" type = "text" value = "2016-11-08" required > <br > <bridge class = "helptext" > Enter engagement between now and iv weeks (default iii weeks). </span > </td > </tr >
Notation: It is perhaps non obvious considering nosotros only have one field, only, by default, every field is divers in its ain table row. This same rendering is provided if you reference the template variable {{ grade.as_table }}
.
If you were to enter an invalid date, you'd additionally get a list of the errors rendered on the page (see errorlist
below).
<tr > <thursday > <characterization for = "id_renewal_date" > Renewal date: </label > </th > <td > <ul class = "errorlist" > <li > Invalid date - renewal in past </li > </ul > <input id = "id_renewal_date" proper noun = "renewal_date" type = "text" value = "2015-11-08" required > <br > <span class = "helptext" > Enter engagement between now and iv weeks (default iii weeks). </span > </td > </tr >
Other ways of using form template variable
Using {{ form.as_table }}
as shown above, each field is rendered as a tabular array row. You tin also render each field as a list item (using {{ grade.as_ul }}
) or as a paragraph (using {{ grade.as_p }}
).
It is also possible to accept consummate control over the rendering of each part of the form, by indexing its properties using dot notation. And then, for instance, we can access a number of split items for our renewal_date
field:
-
{{ grade.renewal_date }}:
The whole field. -
{{ form.renewal_date.errors }}
: The list of errors. -
{{ form.renewal_date.id_for_label }}
: The id of the label. -
{{ form.renewal_date.help_text }}
: The field help text.
For more examples of how to manually render forms in templates and dynamically loop over template fields, run into Working with forms > Rendering fields manually (Django docs).
Testing the page
If yous accustomed the "claiming" in Django Tutorial Part 8: User hallmark and permissions you'll have a list of all books on loan in the library, which is merely visible to library staff. We can add a link to our renew folio next to each item using the template code below.
{% if perms.catalog.can_mark_returned %}- <a href = "{% url 'renew-volume-librarian' bookinst.id %}" > Renew </a > {% endif %}
Note: Remember that your test login volition demand to accept the permission "catalog.can_mark_returned
" in order to access the renew book page (perhaps use your superuser account).
You tin can alternatively manually construct a exam URL similar this — http://127.0.0.1:8000/catalog/book/<bookinstance_id>/renew/
(a valid bookinstance_id
can be obtained past navigating to a volume detail folio in your library, and copying the id
field).
What does information technology look like?
If yous are successful, the default grade volition look similar this:
The grade with an invalid value entered will expect like this:
The list of all books with renew links will look like this:
ModelForms
Creating a Form
class using the approach described above is very flexible, allowing you to create whatever sort of form folio you like and acquaintance information technology with any model or models.
However, if you just need a form to map the fields of a single model then your model volition already define nearly of the information that you lot need in your form: fields, labels, aid text and so on. Rather than recreating the model definitions in your form, it is easier to employ the ModelForm helper form to create the course from your model. This ModelForm
can and then be used inside your views in exactly the aforementioned manner as an ordinary Course
.
A basic ModelForm
containing the same field every bit our original RenewBookForm
is shown below. All you need to exercise to create the form is add class Meta
with the associated model
(BookInstance
) and a list of the model fields
to include in the form.
from django.forms import ModelForm from catalog.models import BookInstance class RenewBookModelForm (ModelForm) : class Meta : model = BookInstance fields = [ 'due_back' ]
Notation: You can besides include all fields in the form using fields = '__all__'
, or you can utilize exclude
(instead of fields
) to specify the fields not to include from the model).
Neither arroyo is recommended because new fields added to the model are then automatically included in the form (without the developer necessarily considering possible security implications).
Annotation: This might not look all that much simpler than but using a Course
(and information technology isn't in this case, because we just have one field). Still, if you have a lot of fields, it can reduce the amount of code quite significantly!
The residue of the information comes from the model field definitions (e.g. labels, widgets, assistance text, error messages). If these aren't quite right, and so we can override them in our form Meta
, specifying a dictionary containing the field to modify and its new value. For case, in this form, we might want a label for our field of "Renewal date" (rather than the default based on the field name: Due Back), and we likewise want our help text to be specific to this use case. The Meta
below shows y'all how to override these fields, and yous tin can similarly set widgets
and error_messages
if the defaults aren't sufficient.
class Meta : model = BookInstance fields = [ 'due_back' ] labels = { 'due_back' : _( 'New renewal date' ) } help_texts = { 'due_back' : _( 'Enter a date betwixt now and 4 weeks (default 3).' ) }
To add together validation you can employ the aforementioned approach every bit for a normal Class
— you define a office named clean_<field_name>()
and heighten ValidationError
exceptions for invalid values. The only difference with respect to our original course is that the model field is named due_back
and not "renewal_date
". This alter is necessary since the corresponding field in BookInstance
is called due_back
.
from django.forms import ModelForm from catalog.models import BookInstance course RenewBookModelForm (ModelForm) : def clean_due_back (cocky) : data = self.cleaned_data[ 'due_back' ] # Check if a engagement is not in the past. if data < datetime.date.today( ) : raise ValidationError(_( 'Invalid date - renewal in past' ) ) # Bank check if a date is in the allowed range (+iv weeks from today). if data > datetime.appointment.today( ) + datetime.timedelta(weeks= 4 ) : raise ValidationError(_( 'Invalid date - renewal more than four weeks ahead' ) ) # Remember to always return the cleaned data. render data course Meta : model = BookInstance fields = [ 'due_back' ] labels = { 'due_back' : _( 'Renewal date' ) } help_texts = { 'due_back' : _( 'Enter a date betwixt now and 4 weeks (default 3).' ) }
The form RenewBookModelForm
above is now functionally equivalent to our original RenewBookForm
. You could import and employ information technology wherever you lot currently use RenewBookForm
every bit long every bit yous also update the respective form variable proper noun from renewal_date
to due_back
as in the 2d form declaration: RenewBookModelForm(initial={'due_back': proposed_renewal_date}
.
Generic editing views
The grade handling algorithm we used in our office view example above represents an extremely common pattern in form editing views. Django abstracts much of this "boilerplate" for you, by creating generic editing views for creating, editing, and deleting views based on models. Non merely practise these handle the "view" behavior, but they automatically create the form class (a ModelForm
) for you from the model.
Note: In addition to the editing views described here, there is besides a FormView class, which lies somewhere between our role view and the other generic views in terms of "flexibility" vs "coding effort". Using FormView
, you notwithstanding need to create your Form
, but you don't have to implement all of the standard course-handling patterns. Instead, you just have to provide an implementation of the function that will exist chosen once the submission is known to be valid.
In this department, nosotros're going to employ generic editing views to create pages to add functionality to create, edit, and delete Writer
records from our library — effectively providing a basic reimplementation of parts of the Admin site (this could be useful if yous need to offering admin functionality in a more flexible style than can be provided by the admin site).
Views
Open up the views file (locallibrary/catalog/views.py) and append the following code cake to the lesser of information technology:
from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.urls import reverse_lazy from catalog.models import Author class AuthorCreate (CreateView) : model = Writer fields = [ 'first_name' , 'last_name' , 'date_of_birth' , 'date_of_death' ] initial = { 'date_of_death' : '11/06/2020' } course AuthorUpdate (UpdateView) : model = Writer fields = '__all__' # Not recommended (potential security result if more than fields added) grade AuthorDelete (DeleteView) : model = Author success_url = reverse_lazy( 'authors' )
Every bit you lot can see, to create, update, or delete the views you need to derive from CreateView
, UpdateView
, and DeleteView
(respectively) and then define the associated model.
For the "create" and "update" cases you too need to specify the fields to display in the form (using the same syntax as for ModelForm
). In this instance, nosotros show how to list them individually and the syntax to listing "all" fields. Yous tin also specify initial values for each of the fields using a dictionary of field_name/value pairs (hither we arbitrarily set the date of expiry for sit-in purposes — yous might want to remove that). By default, these views volition redirect on success to a folio displaying the newly created/edited model detail, which in our case will exist the author detail view nosotros created in a previous tutorial. You tin specify an alternative redirect location by explicitly declaring parameter success_url
(every bit done for the AuthorDelete
class).
The AuthorDelete
class doesn't need to display any of the fields, so these don't need to be specified. You practise however demand to specify the success_url
, because there is no obvious default value for Django to employ. In this case, we use the reverse_lazy()
function to redirect to our author list later an writer has been deleted — reverse_lazy()
is a lazily executed version of reverse()
, used here because nosotros're providing a URL to a class-based view attribute.
Templates
The "create" and "update" views use the same template by default, which volition be named later on your model: model_name_form.html (you can alter the suffix to something other than _form using the template_name_suffix
field in your view, for instance template_name_suffix = '_other_suffix'
)
Create the template file locallibrary/catalog/templates/itemize/author_form.html and copy in the text beneath.
{% extends "base_generic.html" %} {% block content %} <form action = " " method = "post" > {% csrf_token %} <table > {{ form.as_table }} </table > <input type = "submit" value = "Submit" > </course > {% endblock %}
This is similar to our previous forms and renders the fields using a tabular array. Note also how once again nosotros declare the {% csrf_token %}
to ensure that our forms are resistant to CSRF attacks.
The "delete" view expects to discover a template named with the format model_name_confirm_delete.html (again, y'all can change the suffix using template_name_suffix
in your view). Create the template file locallibrary/catalog/templates/catalog/author_confirm_delete.html and copy in the text below.
{% extends "base_generic.html" %} {% block content %} <h1 > Delete Author </h1 > <p > Are yous sure you want to delete the author: {{ author }}? </p > <form action = " " method = "Postal service" > {% csrf_token %} <input blazon = "submit" value = "Yes, delete." > </form > {% endblock %}
URL configurations
Open your URL configuration file (locallibrary/itemize/urls.py) and add the following configuration to the bottom of the file:
urlpatterns += [ path( 'writer/create/' , views.AuthorCreate.as_view( ) , proper name= 'author-create' ) , path( 'author/<int:pk>/update/' , views.AuthorUpdate.as_view( ) , name= 'writer-update' ) , path( 'author/<int:pk>/delete/' , views.AuthorDelete.as_view( ) , proper name= 'writer-delete' ) , ]
In that location is nothing particularly new here! You tin run across that the views are classes, and must hence be called via .as_view()
, and you should be able to recognize the URL patterns in each instance. Nosotros must use pk
as the proper noun for our captured main key value, as this is the parameter name expected past the view classes.
The author create, update, and delete pages are now prepare to test (we won't bother hooking them into the site sidebar in this case, although you lot can do so if you wish).
Note: Observant users will have noticed that we didn't do anything to prevent unauthorized users from accessing the pages! We leave that as an do for you (hint: y'all could use the PermissionRequiredMixin
and either create a new permission or reuse our can_mark_returned
permission).
Testing the page
Commencement, log in to the site with an account that has whatever permissions you decided are needed to access the author editing pages.
So navigate to the author create page, http://127.0.0.1:8000/catalog/author/create/
, which should expect like the screenshot below.
Enter values for the fields and then printing Submit to salvage the author tape. You should now be taken to a detail view for your new author, with a URL of something like http://127.0.0.one:8000/catalog/author/10
.
You can examination editing records by appending /update/ to the end of the item view URL (e.thousand. http://127.0.0.1:8000/catalog/author/10/update/) — we don't show a screenshot considering information technology looks only similar the "create" page!
Finally, we tin can delete the page by appending delete to the finish of the author detail-view URL (e.1000. http://127.0.0.1:8000/catalog/writer/ten/delete/). Django should display the delete page shown beneath. Printing "Yes, delete." to remove the tape and be taken to the list of all authors.
Challenge yourself
Create some forms to create, edit, and delete Volume
records. Y'all can use exactly the same structure as for Authors
. If your book_form.html template is but a copy-renamed version of the author_form.html template, then the new "create book" page will await like the screenshot below:
Summary
Creating and treatment forms tin can be a complicated process! Django makes it much easier by providing programmatic mechanisms to declare, render, and validate forms. Furthermore, Django provides generic form editing views that can do almost all the piece of work to define pages that can create, edit, and delete records associated with a unmarried model case.
At that place is a lot more that tin can be done with forms (check out our Encounter also list below), but you should at present empathize how to add bones forms and form-treatment code to your own websites.
See also
In this module
Source: https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms
0 Response to "Django Template to Render Again When Swiping Back on Phone"
ارسال یک نظر