Because the form has no idea an error occurred.
When you construct the form here:
form=LoginForm()
You’re constructing it without passing it any information. It doesn’t know anything about the POST the user just did, or that the the login failed, or that the password was missing, or whatever the error was.
Here’s what my login forms look like:
class LoginForm(forms.Form):
username = forms.CharField(max_length=255, required=True)
password = forms.CharField(widget=forms.PasswordInput, required=True)
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if not user or not user.is_active:
raise forms.ValidationError("Sorry, that login was invalid. Please try again.")
return self.cleaned_data
def login(self, request):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
user = authenticate(username=username, password=password)
return user
We override the form’s clean
method, so that if the form passes validation we can check the user’s credentials. We also put a login method on the form object itself, to make our view cleaner.
Next, in our view, we want to do this:
def login_view(request):
form = LoginForm(request.POST or None)
if request.POST and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return HttpResponseRedirect("/n1.html")# Redirect to a success page.
return render(request, 'enter.html', {'login_form': form })
We instantiate the form and hand it the request.POST to check against.
If we have a POST, we check if the form is valid. The form’s «clean» method will get called, and check the user credentials for us.
If the credentials fail, we raise an error, which we need to show in our template.
Errors raised by the form (but not attached to a field) are stored in non_field_errors, which can be displayed like so:
{% if form.non_field_errors %}
<ul class='form-errors'>
{% for error in form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
Working with forms¶
About this document
This document provides an introduction to the basics of web forms and how
they are handled in Django. For a more detailed look at specific areas of
the forms API, see The Forms API, Form fields, and
Form and field validation.
Unless you’re planning to build websites and applications that do nothing but
publish content, and don’t accept input from your visitors, you’re going to
need to understand and use forms.
Django provides a range of tools and libraries to help you build forms to
accept input from site visitors, and then process and respond to the input.
HTML forms¶
In HTML, a form is a collection of elements inside <form>...</form>
that
allow a visitor to do things like enter text, select options, manipulate
objects or controls, and so on, and then send that information back to the
server.
Some of these form interface elements — text input or checkboxes — are built
into HTML itself. Others are much more complex; an interface that pops up a
date picker or allows you to move a slider or manipulate controls will
typically use JavaScript and CSS as well as HTML form <input>
elements to
achieve these effects.
As well as its <input>
elements, a form must specify two things:
- where: the URL to which the data corresponding to the user’s input should
be returned - how: the HTTP method the data should be returned by
As an example, the login form for the Django admin contains several
<input>
elements: one of type="text"
for the username, one of
type="password"
for the password, and one of type="submit"
for the
“Log in” button. It also contains some hidden text fields that the user
doesn’t see, which Django uses to determine what to do next.
It also tells the browser that the form data should be sent to the URL
specified in the <form>
’s action
attribute — /admin/
— and that it
should be sent using the HTTP mechanism specified by the method
attribute —
post
.
When the <input type="submit" value="Log in">
element is triggered, the
data is returned to /admin/
.
GET
and POST
¶
GET
and POST
are the only HTTP methods to use when dealing with forms.
Django’s login form is returned using the POST
method, in which the browser
bundles up the form data, encodes it for transmission, sends it to the server,
and then receives back its response.
GET
, by contrast, bundles the submitted data into a string, and uses this
to compose a URL. The URL contains the address where the data must be sent, as
well as the data keys and values. You can see this in action if you do a search
in the Django documentation, which will produce a URL of the form
https://docs.djangoproject.com/search/?q=forms&release=1
.
GET
and POST
are typically used for different purposes.
Any request that could be used to change the state of the system — for example,
a request that makes changes in the database — should use POST
. GET
should be used only for requests that do not affect the state of the system.
GET
would also be unsuitable for a password form, because the password
would appear in the URL, and thus, also in browser history and server logs,
all in plain text. Neither would it be suitable for large quantities of data,
or for binary data, such as an image. A web application that uses GET
requests for admin forms is a security risk: it can be easy for an attacker to
mimic a form’s request to gain access to sensitive parts of the system.
POST
, coupled with other protections like Django’s CSRF protection offers more control over access.
On the other hand, GET
is suitable for things like a web search form,
because the URLs that represent a GET
request can easily be bookmarked,
shared, or resubmitted.
Django’s role in forms¶
Handling forms is a complex business. Consider Django’s admin, where numerous
items of data of several different types may need to be prepared for display in
a form, rendered as HTML, edited using a convenient interface, returned to the
server, validated and cleaned up, and then saved or passed on for further
processing.
Django’s form functionality can simplify and automate vast portions of this
work, and can also do it more securely than most programmers would be able to
do in code they wrote themselves.
Django handles three distinct parts of the work involved in forms:
- preparing and restructuring data to make it ready for rendering
- creating HTML forms for the data
- receiving and processing submitted forms and data from the client
It is possible to write code that does all of this manually, but Django can
take care of it all for you.
Forms in Django¶
We’ve described HTML forms briefly, but an HTML <form>
is just one part of
the machinery required.
In the context of a web application, ‘form’ might refer to that HTML
<form>
, or to the Django Form
that produces it, or to the
structured data returned when it is submitted, or to the end-to-end working
collection of these parts.
The Django Form
class¶
At the heart of this system of components is Django’s Form
class. In
much the same way that a Django model describes the logical structure of an
object, its behavior, and the way its parts are represented to us, a
Form
class describes a form and determines how it works and appears.
In a similar way that a model class’s fields map to database fields, a form
class’s fields map to HTML form <input>
elements. (A ModelForm
maps a model class’s fields to HTML form <input>
elements via a
Form
; this is what the Django admin is based upon.)
A form’s fields are themselves classes; they manage form data and perform
validation when a form is submitted. A DateField
and a
FileField
handle very different kinds of data and have to do
different things with it.
A form field is represented to a user in the browser as an HTML “widget” — a
piece of user interface machinery. Each field type has an appropriate default
Widget class, but these can be overridden as
required.
Instantiating, processing, and rendering forms¶
When rendering an object in Django, we generally:
- get hold of it in the view (fetch it from the database, for example)
- pass it to the template context
- expand it to HTML markup using template variables
Rendering a form in a template involves nearly the same work as rendering any
other kind of object, but there are some key differences.
In the case of a model instance that contained no data, it would rarely if ever
be useful to do anything with it in a template. On the other hand, it makes
perfect sense to render an unpopulated form — that’s what we do when we want
the user to populate it.
So when we handle a model instance in a view, we typically retrieve it from the
database. When we’re dealing with a form we typically instantiate it in the
view.
When we instantiate a form, we can opt to leave it empty or prepopulate it, for
example with:
- data from a saved model instance (as in the case of admin forms for editing)
- data that we have collated from other sources
- data received from a previous HTML form submission
The last of these cases is the most interesting, because it’s what makes it
possible for users not just to read a website, but to send information back
to it too.
Building a form¶
The work that needs to be done¶
Suppose you want to create a simple form on your website, in order to obtain
the user’s name. You’d need something like this in your template:
<form action="/your-name/" method="post"> <label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" value="{{ current_name }}"> <input type="submit" value="OK"> </form>
This tells the browser to return the form data to the URL /your-name/
, using
the POST
method. It will display a text field, labeled “Your name:”, and a
button marked “OK”. If the template context contains a current_name
variable, that will be used to pre-fill the your_name
field.
You’ll need a view that renders the template containing the HTML form, and
that can supply the current_name
field as appropriate.
When the form is submitted, the POST
request which is sent to the server
will contain the form data.
Now you’ll also need a view corresponding to that /your-name/
URL which will
find the appropriate key/value pairs in the request, and then process them.
This is a very simple form. In practice, a form might contain dozens or
hundreds of fields, many of which might need to be prepopulated, and we might
expect the user to work through the edit-submit cycle several times before
concluding the operation.
We might require some validation to occur in the browser, even before the form
is submitted; we might want to use much more complex fields, that allow the
user to do things like pick dates from a calendar and so on.
At this point it’s much easier to get Django to do most of this work for us.
Building a form in Django¶
The Form
class¶
We already know what we want our HTML form to look like. Our starting point for
it in Django is this:
forms.py
¶
from django import forms class NameForm(forms.Form): your_name = forms.CharField(label='Your name', max_length=100)
This defines a Form
class with a single field (your_name
). We’ve
applied a human-friendly label to the field, which will appear in the
<label>
when it’s rendered (although in this case, the label
we specified is actually the same one that would be generated automatically if
we had omitted it).
The field’s maximum allowable length is defined by
max_length
. This does two things. It puts a
maxlength="100"
on the HTML <input>
(so the browser should prevent the
user from entering more than that number of characters in the first place). It
also means that when Django receives the form back from the browser, it will
validate the length of the data.
A Form
instance has an is_valid()
method, which runs
validation routines for all its fields. When this method is called, if all
fields contain valid data, it will:
- return
True
- place the form’s data in its
cleaned_data
attribute.
The whole form, when rendered for the first time, will look like:
<label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" maxlength="100" required>
Note that it does not include the <form>
tags, or a submit button.
We’ll have to provide those ourselves in the template.
The view¶
Form data sent back to a Django website is processed by a view, generally the
same view which published the form. This allows us to reuse some of the same
logic.
To handle the form we need to instantiate it in the view for the URL where we
want it to be published:
views.py
¶
from django.http import HttpResponseRedirect from django.shortcuts import render 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': # create a form instance and populate it with data from the request: form = NameForm(request.POST) # check whether it's valid: if form.is_valid(): # process the data in form.cleaned_data as required # ... # redirect to a new URL: 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})
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
can expect to happen the first time we visit the URL.
If the form is submitted using a POST
request, the view will once again
create a form instance and populate it with data from the request: form =
This is called “binding data to the form” (it is now
NameForm(request.POST)
a bound form).
We call the form’s is_valid()
method; if it’s not True
, we go back to
the template with the form. This time the form is no longer empty (unbound)
so the HTML form will be populated with the data previously submitted, where it
can be edited and corrected as required.
If is_valid()
is True
, we’ll now be able to find all the validated form
data in its cleaned_data
attribute. We can use this data to update the
database or do other processing before sending an HTTP redirect to the browser
telling it where to go next.
The template¶
We don’t need to do much in our name.html
template:
<form action="/your-name/" method="post"> {% csrf_token %} {{ form }} <input type="submit" value="Submit"> </form>
All the form’s fields and their attributes will be unpacked into HTML markup
from that {{ form }}
by Django’s template language.
Forms and Cross Site Request Forgery protection
Django ships with an easy-to-use protection against Cross Site Request
Forgeries. When submitting a form via POST
with
CSRF protection enabled you must use the csrf_token
template tag
as in the preceding example. However, since CSRF protection is not
directly tied to forms in templates, this tag is omitted from the
following examples in this document.
HTML5 input types and browser validation
If your form includes a URLField
, an
EmailField
or any integer field type, Django will
use the url
, email
and number
HTML5 input types. By default,
browsers may apply their own validation on these fields, which may be
stricter than Django’s validation. If you would like to disable this
behavior, set the novalidate
attribute on the form
tag, or specify
a different widget on the field, like TextInput
.
We now have a working web form, described by a Django Form
, processed
by a view, and rendered as an HTML <form>
.
That’s all you need to get started, but the forms framework puts a lot more at
your fingertips. Once you understand the basics of the process described above,
you should be prepared to understand other features of the forms system and
ready to learn a bit more about the underlying machinery.
More about Django Form
classes¶
All form classes are created as subclasses of either django.forms.Form
or django.forms.ModelForm
. You can think of ModelForm
as a
subclass of Form
. Form
and ModelForm
actually inherit common
functionality from a (private) BaseForm
class, but this implementation
detail is rarely important.
Models and Forms
In fact if your form is going to be used to directly add or edit a Django
model, a ModelForm can save you a great
deal of time, effort, and code, because it will build a form, along with the
appropriate fields and their attributes, from a Model
class.
Bound and unbound form instances¶
The distinction between Bound and unbound forms is important:
- An unbound form has no data associated with it. When rendered to the user,
it will be empty or will contain default values. - A bound form has submitted data, and hence can be used to tell if that data
is valid. If an invalid bound form is rendered, it can include inline error
messages telling the user what data to correct.
The form’s is_bound
attribute will tell you whether a form has
data bound to it or not.
More on fields¶
Consider a more useful form than our minimal example above, which we could use
to implement “contact me” functionality on a personal website:
forms.py
¶
from django import forms class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField(widget=forms.Textarea) sender = forms.EmailField() cc_myself = forms.BooleanField(required=False)
Our earlier form used a single field, your_name
, a CharField
. In
this case, our form has four fields: subject
, message
, sender
and
cc_myself
. CharField
, EmailField
and
BooleanField
are just three of the available field types; a full list
can be found in Form fields.
Field data¶
Whatever the data submitted with a form, once it has been successfully
validated by calling is_valid()
(and is_valid()
has returned True
),
the validated form data will be in the form.cleaned_data
dictionary. This
data will have been nicely converted into Python types for you.
Note
You can still access the unvalidated data directly from request.POST
at
this point, but the validated data is better.
In the contact form example above, cc_myself
will be a boolean value.
Likewise, fields such as IntegerField
and FloatField
convert
values to a Python int
and float
respectively.
Here’s how the form data could be processed in the view that handles this form:
views.py
¶
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'] recipients = ['info@example.com'] if cc_myself: recipients.append(sender) send_mail(subject, message, sender, recipients) return HttpResponseRedirect('/thanks/')
Some field types need some extra handling. For example, files that are uploaded
using a form need to be handled differently (they can be retrieved from
request.FILES
, rather than request.POST
). For details of how to handle
file uploads with your form, see Binding uploaded files to a form.
Working with form templates¶
All you need to do to get your form into a template is to place the form
instance into the template context. So if your form is called form
in the
context, {{ form }}
will render its <label>
and <input>
elements
appropriately.
Additional form template furniture
Don’t forget that a form’s output does not include the surrounding
<form>
tags, or the form’s submit
control. You will have to provide
these yourself.
Reusable form templates¶
The HTML output when rendering a form is itself generated via a template. You
can control this by creating an appropriate template file and setting a custom
FORM_RENDERER
to use that
form_template_name
site-wide. You
can also customize per-form by overriding the form’s
template_name
attribute to render the form using the
custom template, or by passing the template name directly to
Form.render()
.
The example below will result in {{ form }}
being rendered as the output of
the form_snippet.html
template.
In your templates:
# In your template: {{ form }} # In form_snippet.html: {% for field in form %} <div class="fieldWrapper"> {{ field.errors }} {{ field.label_tag }} {{ field }} </div> {% endfor %}
Then you can configure the FORM_RENDERER
setting:
settings.py
¶
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:
class MyForm(forms.Form): template_name = "form_snippet.html" ...
… or for a single render of a form instance, passing in the template name to
the 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)
See Outputting forms as HTML for more details.
Changed in Django 4.0:
Template rendering of forms was added.
Changed in Django 4.1:
The ability to set the default form_template_name
on the form renderer
was added.
Form rendering options¶
There are other output options though for the <label>
/<input>
pairs:
{{ form.as_div }}
will render them wrapped in<div>
tags.{{ form.as_table }}
will render them as table cells wrapped in<tr>
tags.{{ form.as_p }}
will render them wrapped in<p>
tags.{{ form.as_ul }}
will render them wrapped in<li>
tags.
Note that you’ll have to provide the surrounding <table>
or <ul>
elements yourself.
Here’s the output of {{ form.as_p }}
for our ContactForm
instance:
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p> <p><label for="id_message">Message:</label> <textarea name="message" id="id_message" required></textarea></p> <p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></p> <p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>
Note that each form field has an ID attribute set to id_<field-name>
, which
is referenced by the accompanying label tag. This is important in ensuring that
forms are accessible to assistive technology such as screen reader software.
You can also customize the way in which labels and ids are generated.
See Outputting forms as HTML for more on this.
Rendering fields manually¶
We don’t have to let Django unpack the form’s fields; we can do it manually if
we like (allowing us to reorder the fields, for example). Each field is
available as an attribute of the form using {{ form.name_of_field }}
, and
in a Django template, will be rendered appropriately. For example:
{{ form.non_field_errors }} <div class="fieldWrapper"> {{ form.subject.errors }} <label for="{{ form.subject.id_for_label }}">Email subject:</label> {{ form.subject }} </div> <div class="fieldWrapper"> {{ form.message.errors }} <label for="{{ form.message.id_for_label }}">Your message:</label> {{ form.message }} </div> <div class="fieldWrapper"> {{ form.sender.errors }} <label for="{{ form.sender.id_for_label }}">Your email address:</label> {{ form.sender }} </div> <div class="fieldWrapper"> {{ form.cc_myself.errors }} <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label> {{ form.cc_myself }} </div>
Complete <label>
elements can also be generated using the
label_tag()
. For example:
<div class="fieldWrapper"> {{ form.subject.errors }} {{ form.subject.label_tag }} {{ form.subject }} </div>
Rendering form error messages¶
The price of this flexibility is a bit more work. Until now we haven’t had to
worry about how to display form errors, because that’s taken care of for us. In
this example we have had to make sure we take care of any errors for each field
and any errors for the form as a whole. Note {{ form.non_field_errors }}
at
the top of the form and the template lookup for errors on each field.
Using {{ form.name_of_field.errors }}
displays a list of form errors,
rendered as an unordered list. This might look like:
<ul class="errorlist"> <li>Sender is required.</li> </ul>
The list has a CSS class of errorlist
to allow you to style its appearance.
If you wish to further customize the display of errors you can do so by looping
over them:
{% if form.subject.errors %} <ol> {% for error in form.subject.errors %} <li><strong>{{ error|escape }}</strong></li> {% endfor %} </ol> {% endif %}
Non-field errors (and/or hidden field errors that are rendered at the top of
the form when using helpers like form.as_p()
) will be rendered with an
additional class of nonfield
to help distinguish them from field-specific
errors. For example, {{ form.non_field_errors }}
would look like:
<ul class="errorlist nonfield"> <li>Generic validation error</li> </ul>
See The Forms API for more on errors, styling, and working with form
attributes in templates.
Looping over the form’s fields¶
If you’re using the same HTML for each of your form fields, you can reduce
duplicate code by looping through each field in turn using a {% for %}
loop:
{% for field in form %} <div class="fieldWrapper"> {{ field.errors }} {{ field.label_tag }} {{ field }} {% if field.help_text %} <p class="help">{{ field.help_text|safe }}</p> {% endif %} </div> {% endfor %}
Useful attributes on {{ field }}
include:
{{ field.errors }}
- Outputs a
<ul class="errorlist">
containing any validation errors
corresponding to this field. You can customize the presentation of
the errors with a{% for error in field.errors %}
loop. In this
case, each object in the loop is a string containing the error message. {{ field.field }}
- The
Field
instance from the form class that
thisBoundField
wraps. You can use it to access
Field
attributes, e.g.
{{ char_field.field.max_length }}
. {{ field.help_text }}
- Any help text that has been associated with the field.
{{ field.html_name }}
- The name of the field that will be used in the input element’s name
field. This takes the form prefix into account, if it has been set. {{ field.id_for_label }}
- The ID that will be used for this field (
id_email
in the example
above). If you are constructing the label manually, you may want to use
this in lieu oflabel_tag
. It’s also useful, for example, if you have
some inline JavaScript and want to avoid hardcoding the field’s ID. {{ field.is_hidden }}
- This attribute is
True
if the form field is a hidden field and
False
otherwise. It’s not particularly useful as a template
variable, but could be useful in conditional tests such as:
{% if field.is_hidden %} {# Do something special #} {% endif %}
{{ field.label }}
- The label of the field, e.g.
Email address
. {{ field.label_tag }}
-
The field’s label wrapped in the appropriate HTML
<label>
tag. This
includes the form’slabel_suffix
. For example,
the defaultlabel_suffix
is a colon:<label for="id_email">Email address:</label>
{{ field.legend_tag }}
New in Django 4.1.
Similar to
field.label_tag
but uses a<legend>
tag in place of
<label>
, for widgets with multiple inputs wrapped in a<fieldset>
.
{{ field.use_fieldset }}
New in Django 4.1.
This attribute is
True
if the form field’s widget contains multiple
inputs that should be semantically grouped in a<fieldset>
with a
<legend>
to improve accessibility. An example use in a template:
{% if field.use_fieldset %} <fieldset> {% if field.label %}{{ field.legend_tag }}{% endif %} {% else %} {% if field.label %}{{ field.label_tag }}{% endif %} {% endif %} {{ field }} {% if field.use_fieldset %}</fieldset>{% endif %}
{{ field.value }}
- The value of the field. e.g
someone@example.com
.
See also
For a complete list of attributes and methods, see
BoundField
.
Further topics¶
This covers the basics, but forms can do a whole lot more:
- Formsets
- Using initial data with a formset
- Limiting the maximum number of forms
- Limiting the maximum number of instantiated forms
- Formset validation
- Validating the number of forms in a formset
- Dealing with ordering and deletion of forms
- Adding additional fields to a formset
- Passing custom parameters to formset forms
- Customizing a formset’s prefix
- Using a formset in views and templates
- Creating forms from models
ModelForm
- Model formsets
- Inline formsets
- Form Assets (the
Media
class)- Assets as a static definition
Media
as a dynamic property- Paths in asset definitions
Media
objectsMedia
on Forms
See also
- The Forms Reference
- Covers the full API reference, including form fields, form widgets,
and form and field validation.
Built-in Form Field Validations in Django Forms are the default validations that come predefined to all fields. Every field comes in with some built-in validations from Django validators. Each Field class constructor takes some fixed arguments.
The error_messages
argument lets you specify manual error messages for attributes of the field. 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. For example, here is the default error message:
>>> from django import forms >>> generic = forms.CharField() >>> generic.clean('') Traceback (most recent call last): ... ValidationError: ['This field is required.']
And here is a custom error message:
>>> name = forms.CharField( error_messages={ 'required': 'Please enter your name' }) >>> name.clean('') Traceback (most recent call last): ... ValidationError: ['Please enter your name']
Syntax
field_name = models.Field(option = value)
Django Form Field Validation error_messages
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 forms.py
file of geeks app. We will be using CharField for experimenting for all field options.
from
django
import
forms
class
GeeksForm(forms.Form):
geeks_field
=
forms.CharField(
error_messages
=
{
'required'
:
"Please Enter your Name"
})
Add the geeks app to INSTALLED_APPS
INSTALLED_APPS
=
[
'django.contrib.admin'
,
'django.contrib.auth'
,
'django.contrib.contenttypes'
,
'django.contrib.sessions'
,
'django.contrib.messages'
,
'django.contrib.staticfiles'
,
'geeks'
,
]
Now to render this form into a view we need a view and a URL mapped to that view. Let’s create a view first in views.py
of geeks app,
from
django.shortcuts
import
render
from
.forms
import
GeeksForm
def
home_view(request):
context
=
{}
form
=
GeeksForm(request.POST
or
None
)
context[
'form'
]
=
form
if
request.POST:
if
form.is_valid():
temp
=
form.cleaned_data.get(
"geeks_field"
)
print
(temp)
return
render(request,
"home.html"
, context)
Here we are importing that particular form from forms.py and creating an object of it in the view so that it can be rendered in a template.
Now, to initiate a Django form you need to create home.html where one would be designing the stuff as they like. Let’s create a form in home.html
.
<
form
method
=
"POST"
>
{% csrf_token %}
{{ form }}
<
input
type
=
"submit"
value
=
"Submit"
>
</
form
>
Finally, a URL to map to this view in urls.py
from
django.urls
import
path
from
.views
import
home_view
URLpatterns
=
[
path('', home_view ),
]
Let’s run the server and check what has actually happened, Run
Python manage.py runserver
Now let’s try to submit it empty and check if required error_message
has been overridden.
Thus the field is displaying a custom error message for required
attribute of Charfield.
More Built-in Form Validations
Field Options | Description |
---|---|
required | By default, each Field class assumes the value is required, so to make it not required you need to set required=False |
label | The label argument lets you specify the “human-friendly” label for this field. This is used when the Field is displayed in a Form. |
label_suffix | The label_suffix argument lets you override the form’s label_suffix on a per-field basis. |
widget | The widget argument lets you specify a Widget class to use when rendering this Field. See Widgets for more information. |
help_text | The help_text argument lets you specify descriptive text for this Field. If you provide help_text, it will be displayed next to the Field when the Field is rendered by one of the convenience Form methods. |
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. |
validators | The validators argument lets you provide a list of validation functions for this field. |
localize | The localize argument enables the localization of form data input, as well as the rendered output. |
disabled. | The disabled boolean argument, when set to True, disables a form field using the disabled HTML attribute so that it won’t be editable by users. |
In this post we’ll learn to create user-defined functions, displaying validation errors in the template for Django Form Validations.
Table Of Contents
- Introduction
- Creating Form
- Rendering Form
- Saving Form
- Form Validation User-Defined Functions
- Conclusion
Introduction
The forms are a Django Module which deals with all form-related functions from binding POST data to form, Validating form and rendering HTML field in the template.
We’ll be using below models.py file as an example to demonstrate form validations.
from django.db import models from django.contrib.auth.models import User from datetime import datetime class AuthUserProfile(models.Model): user_profile_id = models.AutoField(primary_key=True) user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='auth_user_profile') dob = models.DateField(blank=True, null=True) is_deleted = models.PositiveSmallIntegerField(default=0) created_at = models.DateTimeField(auto_now=datetime.now(), null=True) updated_at = models.DateTimeField(auto_now=datetime.now(), null=True) class Meta(): db_table = 'auth_user_profile' verbose_name = 'User Profile' verbose_name_plural = 'User Profiles' def __str__(self): return self.user
Create a form which has these fields (first_name, last_name, username, password, email) from User
models and field (dob) in AuthUserProfile
Model and also will add custom field and non-field level validation.
Creating Form
In forms.py file import forms from Django. Inherit forms.Form
to UserForm
and add attributes to the field.
from django import forms from datetime import datetime from django.contrib.auth.models import User class UserForm(forms.Form): first_name = forms.CharField(label="First Name*",widget=forms.TextInput(attrs={'required':True,'class':"form-control"})) last_name = forms.CharField(label="Last Name*",widget=forms.TextInput(attrs={'required':True,'class':"form-control"})) username = forms.CharField(label="User Name*",widget=forms.TextInput(attrs={'required':True,'class':"form-control"})) email = forms.CharField(label="Email",widget=forms.TextInput(attrs={'type':'email','required':False,'class':"form-control"})) date_of_birth = forms.CharField(label="Date of Birth",widget=forms.TextInput(attrs={'type':'date','required':True,'class':"form-control"})) password = forms.CharField(label="Password*",widget=forms.TextInput(attrs={'required':True,'class':"form-control", 'type' : "password"})) confirm_password = forms.CharField(label="Confirm Password*",widget=forms.TextInput(attrs={'required':True,'class':"form-control", 'type' : "password"})) def clean(self): # user age must be above 18 to register if self.cleaned_data.get('date_of_birth'): dob = datetime.strptime(self.cleaned_data.get('date_of_birth'),"%Y-%m-%d") now = datetime.now() diff = now.year-dob.year if diff < 18: msg="User must be atleast 18 years old" self.add_error(None, msg) #check if user name is unique username_count = User.objects.filter(username=self.cleaned_data.get('username')).count() if username_count>0: msg="Username '{}' has already been used.".format(self.cleaned_data.get('username')) self.add_error(None, msg) def clean_confirm_password(self): password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') if confirm_password!=password: msg = "Password and Confirm Passwords must match." self.add_error('confirm_password', msg)
You may notice clean()
and clean_confirm_password()
methods in UserForm
the form they are validation methods.
The clean()
the method is form level validation this can also be used to perform field-level validation.
And the clean_confirm_password()
is a field-level validation for confirm_password
the field it checks if confirm_password!=password
then adds error to a then particular field.
Rendering Form
Rendering of forms is an easy part we must pass the form object as an argument to render function.
In views.py create a function user_profile_create
which will display rendered form.
from django.contrib.auth.models import User from users.models import AuthUserProfile from forms.forms import UserForm from django.contrib.auth.hashers import make_password from django.contrib import messages def user_profile_create(request): form = UserForm() template="forms/user_profile_create_form.html" return render(request,template,{"form":form})
form = UserForm()
creates form object of UserForm and is passed as an argument to the render()
function.
In urls.py
file add routes to view.
urlpatterns = [ path('user/profile/create', views.user_profile_create, name='user-profile-create'), ]
Create an HTML file in your apps template folder naming user_profile_create_form.html.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Django Form Validation</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script> <style> .error{ color:red; } </style> </head> <body> <div class="container"> {% if messages %} {% for message in messages %} {% if message.level == DEFAULT_MESSAGE_LEVELS.SUCCESS %} <div class="alert alert-success" role="alert"> <div id="primary-notification-div"> {{ message }} </div> </div> {% endif %} {% endfor %} {% endif %} <h1>User Profile</h1> <form action="{% url 'forms:user-profile-save' %}" method="post"> {% csrf_token %} {% if form.errors %} {% for error in form.non_field_errors %} <div class="alert alert-danger"> <strong>{{ error|escape }}</strong> </div> {% endfor %} {% endif %} <div class="row"> <div class="col-md-3"> {{form.first_name.label}} {{form.first_name}} {% if form.errors.first_name %} <label for="" class="error">{{form.errors.first_name|striptags}}</label> {% endif %} </div> <div class="col-md-3"> {{form.last_name.label}} {{form.last_name}} {% if form.errors.last_name %} <label for="" class="error">{{form.errors.last_name|striptags}}</label> {% endif %} </div> <div class="col-md-3"> {{form.username.label}} {{form.username}} {% if form.errors.username %} <label for="" class="error">{{form.errors.username|striptags}}</label> {% endif %} </div> <div class="col-md-3"> {{form.email.label}} {{form.email}} {% if form.errors.email %} <label for="" class="error">{{form.errors.email|striptags}}</label> {% endif %} </div> <div class="col-md-3"> {{form.date_of_birth.label}} {{form.date_of_birth}} {% if form.errors.date_of_birth %} <label for="" class="error">{{form.errors.date_of_birth|striptags}}</label> {% endif %} </div> </div> <div class="row" style="margin-top: 25px;"> <div class="col-md-3"> {{form.password.label}} {{form.password}} {% if form.errors.password %} <label for="" class="error">{{form.errors.password|striptags}}</label> {% endif %} </div> </div> <div class="row" style="margin-top: 25px;"> <div class="col-md-3"> {{form.confirm_password.label}} {{form.confirm_password}} {% if form.errors.confirm_password %} <label for="" class="error">{{form.errors.confirm_password|striptags}}</label> {% endif %} </div> <div class="col-md-12" style="margin-top: 25px;"> <input type="submit" class="btn btn-sm btn-primary" value="submit"> </div> </div> </form> </div> </body> </html>
This is how our form will look when we go to route user/profile/create.
- The
message
displays success message oncemessages.add_message(request, messages.SUCCESS, ".....")
is called it is just like the flash message. - The
form.errors
is called on validation has failed. - The
form.non_field_errors
display errors on top of form this errors are not associated to a particular field. - The
form.errors.
displays the error of the particular field.
This is how errors are displayed in the form.
Saving Form
In urls.py file add routes to save the form.
urlpatterns = [ path('user/profile/save', views.user_profile_create, name='user-profile-save'), ]
In views.py file add function user_profile_save()
to save form data.
def user_profile_save(request): form = UserForm(request.POST) if form.is_valid(): query = { "first_name" : form.cleaned_data.get('first_name'), "last_name" : form.cleaned_data.get('last_name'), "username" : form.cleaned_data.get('username'), "password" : make_password(form.cleaned_data.get('password')), "email" : form.cleaned_data.get('email'), "is_superuser" : 0, "is_staff" : 1, "is_active" : 1, } user = User.objects.create(**query) query={ "user_id" : user.id, "dob" : form.cleaned_data.get('dob'), } AuthUserProfile.objects.create(**query) messages.add_message(request, messages.SUCCESS, "User Profile created successfully.") return HttpResponseRedirect(reverse('forms:user-profile-create')) template="forms/user_profile_create_form.html" return render(request,template,{"form":form})
The request.POST
is passed to UserForm(request.POST)
this binds the submitted data to Form Class.
The form.is_valid()
returns a Boolean value if True
then the form is clean and if False
then there may be validation error.
To view validation errors after .is_valid()
method we can print form.errors
to view validation errors.
Calling form.cleaned_data.get('')
gives use of the sanitized value to that field. Inside .is_valid()
we have called model methods to save form data.
Form Validation User-Defined functions
To defined a custom validation function in Form Class name function with prefix clean followed by underscore and field name which must be validated.
Example
def clean_first_name(self): pass #this validates field first_name def clean_username(self): pass #this validates field username
If the value of the field is not as expected that you can raise validation error or add error by mentioning field name self.add_error('field_name', "Error Message")
.
If you want to raise non-field error than set the first argument of add_error()
method None followed by the message you want to be displayed.
self.add_error(None, msg) #this creates a non-field error
Conclusion
We have come to the end of our post on Django Form Validation.
If you have any doubts or suggestions please mention in the comments section and we’ll reach you soon and we would also love to hear requests and your recommendations for new tutorials/posts.
Related Posts
- Python Django Forms | Creating, Rendering, Validating and Saving Forms
- Django – Multiple Files Validation and Uploads
Summary
Review Date
2020-06-15
Reviewed Item
Django Forms | Custom Form Validations
Author Rating
5
Software Name
Django Web Framework
Software Name
Windows Os, Mac Os, Ubuntu Os
Software Category
Web Development
Этот документ объясняет использование системы аутентификации Django в конфигурации по умолчанию. Эта конфигурация развивалась для удовлетворения наиболее распространенных потребностей проекта, обрабатывая достаточно широкий спектр задач, и имеет тщательную реализацию паролей и разрешений. Для проектов, где потребности в аутентификации отличаются от конфигурации по умолчанию, Django поддерживает расширенные extension and customization> аутентификации.
Django authentication обеспечивает аутентификацию и авторизацию вместе и обычно называется системой аутентификации, поскольку эти функции в некоторой степени связаны между собой.
User
объекты¶
Объекты User
являются ядром системы аутентификации. Они обычно представляют людей, взаимодействующих с вашим сайтом, и используются для таких вещей, как ограничение доступа, регистрация профилей пользователей, ассоциирование контента с создателями и т.д. В системе аутентификации Django существует только один класс пользователей, т.е. пользователи 'superusers'
или admin 'staff'
— это просто объекты пользователей с установленными специальными атрибутами, а не разные классы объектов пользователей.
Основными атрибутами пользователя по умолчанию являются:
username
password
email
first_name
last_name
См. full API documentation
для полной справки, следующая документация больше ориентирована на задачи.
Создание пользователей¶
Самый прямой способ создания пользователей — использовать включенную вспомогательную функцию create_user()
:
>>> from django.contrib.auth.models import User >>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword') # At this point, user is a User object that has already been saved # to the database. You can continue to change its attributes # if you want to change other fields. >>> user.last_name = 'Lennon' >>> user.save()
Если у вас установлен админ Django, вы также можете create users interactively.
Создание суперпользователей¶
Создайте суперпользователей с помощью команды createsuperuser
:
$ python manage.py createsuperuser --username=joe --email=joe@example.com
Вам будет предложено ввести пароль. После ввода пароля пользователь будет создан немедленно. Если вы не указали параметры --username
или --email
, будет предложено ввести эти значения.
Изменение паролей¶
Django не хранит необработанные (открытый текст) пароли в модели пользователя, а только хэш (см. documentation of how passwords are managed для полной информации). В связи с этим не пытайтесь манипулировать атрибутом password пользователя напрямую. Поэтому при создании пользователя используется вспомогательная функция.
Чтобы изменить пароль пользователя, у вас есть несколько вариантов:
manage.py changepassword *username*
предлагает метод изменения пароля пользователя из командной строки. Он предлагает вам изменить пароль данного пользователя, который вы должны ввести дважды. Если они оба совпадают, то новый пароль будет немедленно изменен. Если вы не укажете пользователя, команда попытается изменить пароль пользователя, чье имя пользователя совпадает с именем текущего пользователя системы.
Вы также можете изменить пароль программно, используя set_password()
:
>>> from django.contrib.auth.models import User >>> u = User.objects.get(username='john') >>> u.set_password('new password') >>> u.save()
Если у вас установлен админ Django, вы также можете изменить пароли пользователей на странице authentication system’s admin pages.
Django также предоставляет views и forms, которые могут быть использованы для того, чтобы позволить пользователям изменять свои собственные пароли.
Изменение пароля пользователя приведет к выходу из всех его сессий. Подробнее см. в разделе Аннулирование сессии при смене пароля.
Аутентификация пользователей¶
-
authenticate
(request=None, **credentials)[исходный код]¶ -
Используйте
authenticate()
для проверки набора учетных данных. Она принимает учетные данные в качестве аргументов ключевых слов,username
иpassword
для случая по умолчанию, сверяет их с каждым authentication backend и возвращает объектUser
, если учетные данные действительны для бэкенда. Если учетные данные не действительны ни для одного бэкенда или если бэкенд поднимаетPermissionDenied
, возвращаетсяNone
. Например:from django.contrib.auth import authenticate user = authenticate(username='john', password='secret') if user is not None: # A backend authenticated the credentials else: # No backend authenticated the credentials
request
— необязательныйHttpRequest
, который передается в методеauthenticate()
бэкендов аутентификации.Примечание
Это низкоуровневый способ аутентификации набора учетных данных; например, его использует
RemoteUserMiddleware
. Если вы не пишете свою собственную систему аутентификации, вы, вероятно, не будете ее использовать. Скорее, если вы ищете способ входа пользователя в систему, используйтеLoginView
.
Разрешения и авторизация¶
Django поставляется со встроенной системой разрешений. Она предоставляет возможность назначать разрешения определенным пользователям и группам пользователей.
Он используется администратором сайта Django, но вы можете использовать его в своем собственном коде.
Административный сайт Django использует разрешения следующим образом:
- Доступ к объектам просмотра ограничен пользователями, имеющими разрешение «просмотр» или «изменение» для данного типа объекта.
- Доступ к просмотру формы «Добавить» и добавлению объекта ограничен пользователями с правом «Добавить» для данного типа объекта.
- Доступ к просмотру списка изменений, форме «изменения» и изменению объекта ограничен пользователями с правом «изменения» для данного типа объекта.
- Доступ к удалению объекта ограничен пользователями, имеющими разрешение «удалить» для данного типа объекта.
Разрешения могут быть установлены не только для типа объекта, но и для конкретного экземпляра объекта. Используя методы has_view_permission()
, has_add_permission()
, has_change_permission()
и has_delete_permission()
, предоставляемые классом ModelAdmin
, можно настроить разрешения для различных экземпляров объектов одного типа.
Объекты User
имеют два поля типа «многие-ко-многим»: groups
и user_permissions
. Объекты User
могут обращаться к связанным с ними объектам так же, как и к любым другим Django model:
myuser.groups.set([group_list]) myuser.groups.add(group, group, ...) myuser.groups.remove(group, group, ...) myuser.groups.clear() myuser.user_permissions.set([permission_list]) myuser.user_permissions.add(permission, permission, ...) myuser.user_permissions.remove(permission, permission, ...) myuser.user_permissions.clear()
Разрешения по умолчанию¶
Когда django.contrib.auth
указано в настройках INSTALLED_APPS
, это гарантирует, что четыре разрешения по умолчанию — добавление, изменение, удаление и просмотр — будут созданы для каждой модели Django, определенной в одном из ваших установленных приложений.
Эти разрешения будут созданы при запуске manage.py migrate
; при первом запуске migrate
после добавления django.contrib.auth
к INSTALLED_APPS
разрешения по умолчанию будут созданы для всех ранее установленных моделей, а также для любых новых моделей, устанавливаемых в это время. После этого он будет создавать разрешения по умолчанию для новых моделей каждый раз, когда вы запускаете manage.py migrate
(функция, создающая разрешения, связана с сигналом post_migrate
).
Предположим, что у вас есть приложение с app_label
foo
и моделью с именем Bar
, для проверки основных разрешений вы должны использовать:
- добавить:
user.has_perm('foo.add_bar')
- изменение:
user.has_perm('foo.change_bar')
- удалить:
user.has_perm('foo.delete_bar')
- вид:
user.has_perm('foo.view_bar')
К модели Permission
редко обращаются напрямую.
Групи¶
django.contrib.auth.models.Group
Модели — это общий способ категоризации пользователей, чтобы вы могли применять к ним разрешения или другие метки. Пользователь может принадлежать к любому количеству групп.
Пользователь в группе автоматически имеет разрешения, предоставленные этой группе. Например, если группа Site editors
имеет разрешение can_edit_home_page
, то любой пользователь в этой группе будет иметь это разрешение.
Помимо прав доступа, группы — это удобный способ разделить пользователей на категории, чтобы дать им определенный ярлык или расширенную функциональность. Например, вы можете создать группу 'Special users'
, и написать код, который может, скажем, дать им доступ к части вашего сайта, предназначенной только для членов клуба, или отправлять им сообщения по электронной почте, предназначенные только для членов клуба.
Программное создание разрешений¶
Хотя custom permissions может быть определено в классе Meta
модели, вы также можете создавать разрешения напрямую. Например, вы можете создать разрешение can_publish
для модели BlogPost
в myapp
:
from myapp.models import BlogPost from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType content_type = ContentType.objects.get_for_model(BlogPost) permission = Permission.objects.create( codename='can_publish', name='Can Publish Posts', content_type=content_type, )
Затем разрешение может быть назначено на User
через атрибут user_permissions
или на Group
через атрибут permissions
.
Прокси-модели нуждаются в собственном типе содержимого
Если вы хотите создать permissions for a proxy model, передайте for_concrete_model=False
в ContentTypeManager.get_for_model()
, чтобы получить соответствующее ContentType
:
content_type = ContentType.objects.get_for_model(BlogPostProxy, for_concrete_model=False)
Кэширование разрешений¶
ModelBackend
кэширует разрешения на объект пользователя после первого раза, когда они должны быть получены для проверки разрешений. Обычно это подходит для цикла запрос-ответ, поскольку разрешения обычно не проверяются сразу после их добавления (например, в админке). Если вы добавляете разрешения и проверяете их сразу после этого, например, в тесте или представлении, самым простым решением будет повторная выборка пользователя из базы данных. Например:
from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType from django.shortcuts import get_object_or_404 from myapp.models import BlogPost def user_gains_perms(request, user_id): user = get_object_or_404(User, pk=user_id) # any permission check will cache the current set of permissions user.has_perm('myapp.change_blogpost') content_type = ContentType.objects.get_for_model(BlogPost) permission = Permission.objects.get( codename='change_blogpost', content_type=content_type, ) user.user_permissions.add(permission) # Checking the cached permission set user.has_perm('myapp.change_blogpost') # False # Request new instance of User # Be aware that user.refresh_from_db() won't clear the cache. user = get_object_or_404(User, pk=user_id) # Permission cache is repopulated from the database user.has_perm('myapp.change_blogpost') # True ...
Модели прокси¶
Прокси-модели работают точно так же, как и конкретные модели. Разрешения создаются с использованием собственного типа содержимого прокси-модели. Прокси-модели не наследуют разрешения конкретной модели, которую они подклассифицируют:
class Person(models.Model): class Meta: permissions = [('can_eat_pizzas', 'Can eat pizzas')] class Student(Person): class Meta: proxy = True permissions = [('can_deliver_pizzas', 'Can deliver pizzas')] >>> # Fetch the content type for the proxy model. >>> content_type = ContentType.objects.get_for_model(Student, for_concrete_model=False) >>> student_permissions = Permission.objects.filter(content_type=content_type) >>> [p.codename for p in student_permissions] ['add_student', 'change_student', 'delete_student', 'view_student', 'can_deliver_pizzas'] >>> for permission in student_permissions: ... user.user_permissions.add(permission) >>> user.has_perm('app.add_person') False >>> user.has_perm('app.can_eat_pizzas') False >>> user.has_perms(('app.add_student', 'app.can_deliver_pizzas')) True
Аутентификация в веб-запросах¶
Django использует sessions и промежуточное ПО для подключения системы аутентификации к request objects
.
Они обеспечивают атрибут request.user
в каждом запросе, который представляет текущего пользователя. Если текущий пользователь не вошел в систему, этот атрибут будет установлен в экземпляр AnonymousUser
, в противном случае это будет экземпляр User
.
Вы можете различать их с помощью is_authenticated
, например, так:
if request.user.is_authenticated: # Do something for authenticated users. ... else: # Do something for anonymous users. ...
Как войти в систему пользователя¶
Если у вас есть аутентифицированный пользователь, которого вы хотите присоединить к текущей сессии — это делается с помощью функции login()
.
-
login
(request, user, backend=None)[исходный код]¶ -
Чтобы зарегистрировать пользователя в системе из представления, используйте
login()
. Он принимает объектHttpRequest
и объектUser
.login()
сохраняет идентификатор пользователя в сессии, используя фреймворк сессий Django.Обратите внимание, что любые данные, установленные во время анонимной сессии, сохраняются в сессии после входа пользователя в систему.
Этот пример показывает, как можно использовать
authenticate()
иlogin()
:from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(request, username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. ... else: # Return an 'invalid login' error message. ...
Выбор бэкенда аутентификации¶
Когда пользователь входит в систему, его ID и бэкенд, который использовался для аутентификации, сохраняются в сессии пользователя. Это позволяет тому же authentication backend получить данные пользователя при последующем запросе. Бэкэнд аутентификации для сохранения в сессии выбирается следующим образом:
- Используйте значение необязательного аргумента
backend
, если он предоставлен. - Используйте значение атрибута
user.backend
, если он присутствует. Это позволяет использовать парыauthenticate()
иlogin()
:authenticate()
устанавливает атрибутuser.backend
на возвращаемом объекте пользователя. - Используйте
backend
вAUTHENTICATION_BACKENDS
, если есть только один. - В противном случае вызовите исключение.
В случаях 1 и 2 значением аргумента backend
или атрибута user.backend
должна быть строка пути импорта с точкой (как в AUTHENTICATION_BACKENDS
), а не реальный класс бэкенда.
Как выйти из системы¶
-
logout
(request)[исходный код]¶ -
Чтобы выйти из пользователя, который вошел в систему через
django.contrib.auth.login()
, используйтеdjango.contrib.auth.logout()
в вашем представлении. Она принимает объектHttpRequest
и не имеет возвращаемого значения. Пример:from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
Обратите внимание, что
logout()
не выдает никаких ошибок, если пользователь не вошел в систему.Когда вы вызываете
logout()
, данные сессии для текущего запроса полностью очищаются. Все существующие данные удаляются. Это делается для того, чтобы другой человек не смог использовать тот же веб-браузер для входа в систему и получить доступ к данным сессии предыдущего пользователя. Если вы хотите поместить в сессию что-либо, что будет доступно пользователю сразу после выхода из нее, сделайте это после вызоваdjango.contrib.auth.logout()
.
Ограничение доступа для вошедших в систему пользователей¶
Сырой способ¶
Необработанный способ ограничения доступа к страницам заключается в проверке request.user.is_authenticated
и либо перенаправлении на страницу входа:
from django.conf import settings from django.shortcuts import redirect def my_view(request): if not request.user.is_authenticated: return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path)) # ...
…или вывести сообщение об ошибке:
from django.shortcuts import render def my_view(request): if not request.user.is_authenticated: return render(request, 'myapp/login_error.html') # ...
Декоратор login_required
¶
-
login_required
(redirect_field_name=‘next’, login_url=None)[исходный код]¶ -
В качестве сокращения можно использовать удобный декоратор
login_required()
:from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
login_required()
делает следующее:- Если пользователь не вошел в систему, перенаправьте его на
settings.LOGIN_URL
, передав текущий абсолютный путь в строке запроса. Пример:/accounts/login/?next=/polls/3/
. - Если пользователь вошел в систему, выполните представление нормально. Код представления может считать, что пользователь вошел в систему.
По умолчанию путь, на который пользователь должен быть перенаправлен после успешной аутентификации, хранится в параметре строки запроса под названием
"next"
. Если вы предпочитаете использовать другое имя для этого параметра,login_required()
принимает необязательный параметрredirect_field_name
:from django.contrib.auth.decorators import login_required @login_required(redirect_field_name='my_redirect_field') def my_view(request): ...
Обратите внимание, что если вы зададите значение
redirect_field_name
, то, скорее всего, вам также придется настроить шаблон входа в систему, так как контекстная переменная шаблона, хранящая путь перенаправления, будет использовать в качестве ключа значениеredirect_field_name
, а не"next"
(по умолчанию).login_required()
также принимает необязательный параметрlogin_url
. Пример:from django.contrib.auth.decorators import login_required @login_required(login_url='/accounts/login/') def my_view(request): ...
Обратите внимание, что если вы не указываете параметр
login_url
, вам нужно убедиться, что параметрsettings.LOGIN_URL
и ваше представление входа правильно связаны. Например, используя значения по умолчанию, добавьте следующие строки в URLconf:from django.contrib.auth import views as auth_views path('accounts/login/', auth_views.LoginView.as_view()),
settings.LOGIN_URL
также принимает имена функций представления и named URL patterns. Это позволяет вам свободно переназначать представление входа в URLconf без необходимости обновлять настройки. - Если пользователь не вошел в систему, перенаправьте его на
Примечание
Декоратор login_required
НЕ проверяет флаг is_active
у пользователя, но по умолчанию AUTHENTICATION_BACKENDS
отвергает неактивных пользователей.
The LoginRequiredMixin
mixin¶
При использовании class-based views можно добиться того же поведения, что и при login_required
, используя LoginRequiredMixin
. Этот миксин должен находиться на самой левой позиции в списке наследования.
-
class
LoginRequiredMixin
¶ -
Если представление использует этот миксин, все запросы неаутентифицированных пользователей будут перенаправляться на страницу входа или отображаться ошибка HTTP 403 Forbidden, в зависимости от параметра
raise_exception
.Вы можете установить любой из параметров
AccessMixin
для настройки обработки неавторизованных пользователей:from django.contrib.auth.mixins import LoginRequiredMixin class MyView(LoginRequiredMixin, View): login_url = '/login/' redirect_field_name = 'redirect_to'
Примечание
Как и декоратор login_required
, этот миксин НЕ проверяет флаг is_active
на пользователе, но по умолчанию AUTHENTICATION_BACKENDS
отклоняет неактивных пользователей.
Ограничение доступа для вошедших в систему пользователей, которые прошли тест¶
Чтобы ограничить доступ на основе определенных разрешений или какого-либо другого теста, вы сделаете практически то же самое, что описано в предыдущем разделе.
Вы можете запустить свой тест на request.user
непосредственно в представлении. Например, это представление проверяет, есть ли у пользователя email в нужном домене, и если нет, перенаправляет на страницу входа:
from django.shortcuts import redirect def my_view(request): if not request.user.email.endswith('@example.com'): return redirect('/login/?next=%s' % request.path) # ...
-
user_passes_test
(test_func, login_url=None, redirect_field_name=‘next’)[исходный код]¶ -
В качестве сокращения можно использовать удобный декоратор
user_passes_test
, который выполняет перенаправление, когда вызываемый объект возвращаетFalse
:from django.contrib.auth.decorators import user_passes_test def email_check(user): return user.email.endswith('@example.com') @user_passes_test(email_check) def my_view(request): ...
user_passes_test()
принимает обязательный аргумент: вызываемый объект, который принимает объектUser
и возвращаетTrue
, если пользователю разрешено просматривать страницу. Обратите внимание, чтоuser_passes_test()
не проверяет автоматически, чтоUser
не является анонимным.user_passes_test()
принимает два необязательных аргумента:login_url
- Позволяет указать URL, на который будут перенаправлены пользователи, не прошедшие тест. Это может быть страница входа в систему и по умолчанию имеет значение
settings.LOGIN_URL
, если вы его не укажете. redirect_field_name
- То же самое, что и для
login_required()
. Установка значенияNone
удаляет его из URL, что может понадобиться, если вы перенаправляете пользователей, не прошедших тест, на страницу без входа в систему, где нет «следующей страницы».
Например:
@user_passes_test(email_check, login_url='/login/') def my_view(request): ...
-
class
UserPassesTestMixin
¶ -
При использовании class-based views, вы можете использовать
UserPassesTestMixin
для этого.-
test_func
()¶ -
Вы должны переопределить метод
test_func()
класса, чтобы указать выполняемый тест. Кроме того, вы можете установить любой из параметровAccessMixin
для настройки обработки неавторизованных пользователей:from django.contrib.auth.mixins import UserPassesTestMixin class MyView(UserPassesTestMixin, View): def test_func(self): return self.request.user.email.endswith('@example.com')
-
get_test_func
()¶ -
Вы также можете переопределить метод
get_test_func()
, чтобы миксин использовал для своих проверок функцию с другим именем (вместоtest_func()
).
Укладка
UserPassesTestMixin
Из-за того, как реализовано
UserPassesTestMixin
, вы не можете складывать их в список наследования. Следующее НЕ работает:class TestMixin1(UserPassesTestMixin): def test_func(self): return self.request.user.email.endswith('@example.com') class TestMixin2(UserPassesTestMixin): def test_func(self): return self.request.user.username.startswith('django') class MyView(TestMixin1, TestMixin2, View): ...
Если бы
TestMixin1
вызывалsuper()
и учитывал этот результат,TestMixin1
уже не работал бы автономно. -
Декоратор permission_required
¶
-
permission_required
(perm, login_url=None, raise_exception=False)[исходный код]¶ -
Это довольно распространенная задача — проверить, есть ли у пользователя определенное разрешение. По этой причине Django предоставляет ярлык для этого случая: декоратор
permission_required()
.:from django.contrib.auth.decorators import permission_required @permission_required('polls.add_choice') def my_view(request): ...
Как и в методе
has_perm()
, имена разрешений принимают форму"<app label>.<permission codename>"
(например,polls.add_choice
для разрешения на модель в приложенииpolls
).Декоратор также может принимать итерацию разрешений, в этом случае пользователь должен иметь все разрешения, чтобы получить доступ к представлению.
Обратите внимание, что
permission_required()
также принимает необязательный параметрlogin_url
:from django.contrib.auth.decorators import permission_required @permission_required('polls.add_choice', login_url='/loginpage/') def my_view(request): ...
Как и в декораторе
login_required()
,login_url
по умолчанию равенsettings.LOGIN_URL
.Если задан параметр
raise_exception
, декоратор подниметPermissionDenied
, предлагая the 403 (HTTP Forbidden) view вместо перенаправления на страницу входа.Если вы хотите использовать
raise_exception
, но при этом дать пользователям возможность сначала войти в систему, вы можете добавить декораторlogin_required()
:from django.contrib.auth.decorators import login_required, permission_required @login_required @permission_required('polls.add_choice', raise_exception=True) def my_view(request): ...
Это также позволяет избежать цикла перенаправления, когда
LoginView
становитсяredirect_authenticated_user=True
, а у вошедшего пользователя нет всех необходимых прав.
Миксин PermissionRequiredMixin
¶
Чтобы применить проверку разрешений к class-based views, вы можете использовать PermissionRequiredMixin
:
-
class
PermissionRequiredMixin
¶ -
Этот миксин, как и декоратор
permission_required
, проверяет, имеет ли пользователь, обращающийся к представлению, все заданные разрешения. Вы должны указать разрешение (или итерацию разрешений) с помощью параметраpermission_required
:from django.contrib.auth.mixins import PermissionRequiredMixin class MyView(PermissionRequiredMixin, View): permission_required = 'polls.add_choice' # Or multiple of permissions: permission_required = ('polls.view_choice', 'polls.change_choice')
Вы можете установить любой из параметров
AccessMixin
, чтобы настроить обработку неавторизованных пользователей.Вы также можете переопределить эти методы:
-
get_permission_required
()¶ -
Возвращает итерабель имен разрешений, используемых данным микшином. По умолчанию используется атрибут
permission_required
, при необходимости преобразуется в кортеж.
-
has_permission
()¶ -
Возвращает булево значение, обозначающее, имеет ли текущий пользователь разрешение на выполнение декорированного представления. По умолчанию возвращается результат вызова
has_perms()
со списком разрешений, возвращаемымget_permission_required()
.
-
Перенаправление несанкционированных запросов в представлениях на основе классов¶
Чтобы облегчить обработку ограничений доступа в class-based views, AccessMixin
можно использовать для настройки поведения представления при отказе в доступе. Аутентифицированным пользователям отказывается в доступе с ответом HTTP 403 Forbidden. Анонимные пользователи перенаправляются на страницу входа или показывают ответ HTTP 403 Forbidden, в зависимости от атрибута raise_exception
.
-
class
AccessMixin
¶ -
-
login_url
¶ -
Возвращаемое значение по умолчанию для
get_login_url()
. По умолчаниюNone
, в этом случаеget_login_url()
возвращается кsettings.LOGIN_URL
.
-
permission_denied_message
¶ -
Возвращаемое значение по умолчанию для
get_permission_denied_message()
. По умолчанию это пустая строка.
-
redirect_field_name
¶ -
Возвращаемое значение по умолчанию для
get_redirect_field_name()
. По умолчанию возвращается значение"next"
.
-
raise_exception
¶ -
Если этот атрибут установлен в
True
, то при невыполнении условий возникает исключениеPermissionDenied
. ЕслиFalse
(по умолчанию), анонимные пользователи перенаправляются на страницу входа в систему.
-
get_login_url
()¶ -
Возвращает URL, на который будут перенаправлены пользователи, не прошедшие тест. Возвращает
login_url
, если установлено, илиsettings.LOGIN_URL
в противном случае.
-
get_permission_denied_message
()¶ -
Когда
raise_exception
равноTrue
, этот метод можно использовать для управления сообщением об ошибке, передаваемым в обработчик ошибок для отображения пользователю. По умолчанию возвращает атрибутpermission_denied_message
.
-
get_redirect_field_name
()¶ -
Возвращает имя параметра запроса, который будет содержать URL, на который пользователь должен быть перенаправлен после успешного входа в систему. Если вы установите значение
None
, параметр запроса не будет добавлен. По умолчанию возвращает атрибутredirect_field_name
.
-
handle_no_permission
()¶ -
В зависимости от значения
raise_exception
, метод либо вызывает исключениеPermissionDenied
, либо перенаправляет пользователя наlogin_url
, по желанию включаяredirect_field_name
, если оно установлено.
-
Аннулирование сессии при смене пароля¶
Если ваш AUTH_USER_MODEL
наследует от AbstractBaseUser
или реализует свой собственный метод get_session_auth_hash()
, аутентифицированные сессии будут включать хэш, возвращаемый этой функцией. В случае AbstractBaseUser
это HMAC поля пароля. Django проверяет, что хэш в сессии для каждого запроса совпадает с тем, который вычисляется во время запроса. Это позволяет пользователю выйти из всех своих сессий, изменив пароль.
Представления смены пароля по умолчанию, входящие в Django, PasswordChangeView
и представление user_change_password
в админке django.contrib.auth
, обновляют сессию новым хэшем пароля, чтобы пользователь, меняющий свой пароль, не вышел из системы. Если у вас есть пользовательское представление смены пароля и вы хотите иметь подобное поведение, используйте функцию update_session_auth_hash()
.
-
update_session_auth_hash
(request, user)[исходный код]¶ -
Эта функция принимает текущий запрос и обновленный объект пользователя, из которого будет получен новый хэш сессии, и соответствующим образом обновляет хэш сессии. Она также поворачивает ключ сессии, чтобы украденная сессионная cookie была недействительна.
Пример использования:
from django.contrib.auth import update_session_auth_hash def password_change(request): if request.method == 'POST': form = PasswordChangeForm(user=request.user, data=request.POST) if form.is_valid(): form.save() update_session_auth_hash(request, form.user) else: ...
Представления аутентификации¶
Django предоставляет несколько представлений, которые вы можете использовать для обработки входа, выхода и управления паролями. В них используется stock auth forms, но вы можете передавать и свои собственные формы.
Django не предоставляет шаблонов по умолчанию для представлений аутентификации. Вы должны создать свои собственные шаблоны для представлений, которые вы хотите использовать. Контекст шаблона документирован в каждом представлении, см. Все виды аутентификации.
Использование представлений¶
Существуют различные методы реализации этих представлений в вашем проекте. Самый простой способ — включить предоставленный URLconf в django.contrib.auth.urls
в ваш собственный URLconf, например:
urlpatterns = [ path('accounts/', include('django.contrib.auth.urls')), ]
Это будет включать следующие шаблоны URL:
accounts/login/ [name='login'] accounts/logout/ [name='logout'] accounts/password_change/ [name='password_change'] accounts/password_change/done/ [name='password_change_done'] accounts/password_reset/ [name='password_reset'] accounts/password_reset/done/ [name='password_reset_done'] accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm'] accounts/reset/done/ [name='password_reset_complete']
Представления предоставляют имя URL для более удобного использования. Подробнее об использовании именованных шаблонов URL см. в the URL documentation.
Если вы хотите получить больший контроль над своими URL-адресами, вы можете сослаться на определенное представление в URLconf:
from django.contrib.auth import views as auth_views urlpatterns = [ path('change-password/', auth_views.PasswordChangeView.as_view()), ]
Представления имеют необязательные аргументы, которые можно использовать для изменения поведения представления. Например, если вы хотите изменить имя шаблона, которое использует представление, вы можете указать аргумент template_name
. Для этого в URLconf можно указать аргументы в виде ключевых слов, которые будут переданы представлению. Например:
urlpatterns = [ path( 'change-password/', auth_views.PasswordChangeView.as_view(template_name='change-password.html'), ), ]
Все представления являются class-based, что позволяет легко настраивать их с помощью подклассов.
Все виды аутентификации¶
Это список со всеми представлениями, которые предоставляет django.contrib.auth
. Подробности реализации смотрите в Использование представлений.
-
class
LoginView
¶ -
Имя URL:
login
Подробнее об использовании именованных шаблонов URL см. в разделе the URL documentation.
Методы и атрибуты
-
template_name
¶ -
Имя шаблона, отображаемого для представления, используемого для входа пользователя в систему. По умолчанию имеет значение
registration/login.html
.
-
next_page
¶ -
New in Django 4.0.
URL для перенаправления после входа в систему. По умолчанию
LOGIN_REDIRECT_URL
.
-
redirect_field_name
¶ -
Имя поля
GET
, содержащего URL для перенаправления после входа в систему. По умолчанию используетсяnext
. Переопределяет URLget_default_redirect_url()
, если передан заданный параметрGET
.
-
authentication_form
¶ -
Вызываемый объект (обычно класс формы), который будет использоваться для аутентификации. По умолчанию
AuthenticationForm
.
-
Словарь контекстных данных, которые будут добавлены к контекстным данным по умолчанию, переданным в шаблон.
-
redirect_authenticated_user
¶ -
Булево значение, определяющее, будут ли аутентифицированные пользователи, зашедшие на страницу входа, перенаправлены так, как будто они только что успешно вошли в систему. По умолчанию имеет значение
False
.Предупреждение
Если вы включите опцию
redirect_authenticated_user
, другие сайты смогут определить, авторизованы ли их посетители на вашем сайте, запрашивая URL перенаправления на файлы изображений на вашем сайте. Чтобы избежать такой утечки информации social media fingerprinting», размещайте все изображения и ваш favicon на отдельном домене.Включение
redirect_authenticated_user
также может привести к циклу перенаправления при использовании декоратораpermission_required()
, если не используется параметрraise_exception
.
-
success_url_allowed_hosts
¶ -
set
хостов, в дополнение кrequest.get_host()
, которые безопасны для перенаправления после входа в систему. По умолчанию используется пустойset
.
-
get_default_redirect_url
()¶ -
New in Django 4.0.
Возвращает URL для перенаправления после входа в систему. Реализация по умолчанию разрешает и возвращает
next_page
, если установлен, илиLOGIN_REDIRECT_URL
в противном случае.
Вот что делает
LoginView
:- При вызове через
GET
отображается форма входа в систему, которая POSTs на тот же URL. Подробнее об этом чуть позже. - При вызове через
POST
с учетными данными, предоставленными пользователем, он пытается войти в систему. В случае успешного входа представление перенаправляется на URL, указанный вnext
. Еслиnext
не указан, он перенаправляется наsettings.LOGIN_REDIRECT_URL
(по умолчанию на/accounts/profile/
). Если вход не был успешным, отображается форма входа.
Вы обязаны предоставить html для шаблона входа в систему, который по умолчанию называется
registration/login.html
. Этому шаблону передаются четыре контекстные переменные шаблона:form
: ОбъектForm
, представляющийAuthenticationForm
.next
: URL для перенаправления после успешного входа в систему. Он также может содержать строку запроса.site
: ТекущийSite
, в соответствии с настройкойSITE_ID
. Если у вас не установлен фреймворк сайта, это будет установлено в экземплярRequestSite
, который берет имя сайта и домен из текущегоHttpRequest
.site_name
: Псевдоним дляsite.name
. Если у вас не установлен фреймворк сайта, это значение будет установлено в значениеrequest.META['SERVER_NAME']
. Подробнее о сайтах смотрите Структура «сайтов».
Если вы предпочитаете не вызывать шаблон
registration/login.html
, вы можете передать параметрtemplate_name
через дополнительные аргументы методуas_view
в вашем URLconf. Например, эта строка URLconf будет использоватьmyapp/login.html
вместо:path('accounts/login/', auth_views.LoginView.as_view(template_name='myapp/login.html')),
Вы также можете указать имя поля
GET
, которое содержит URL-адрес для перенаправления на него после входа в систему с помощьюredirect_field_name
. По умолчанию поле называетсяnext
.Вот пример шаблона
registration/login.html
, который вы можете использовать в качестве отправной точки. Он предполагает, что у вас есть шаблонbase.html
, определяющий блокcontent
:{% extends "base.html" %} {% block content %} {% if form.errors %} <p>Your username and password didn't match. Please try again.</p> {% endif %} {% if next %} {% if user.is_authenticated %} <p>Your account doesn't have access to this page. To proceed, please login with an account that has access.</p> {% else %} <p>Please login to see this page.</p> {% endif %} {% endif %} <form method="post" action="{% url 'login' %}"> {% csrf_token %} <table> <tr> <td>{{ form.username.label_tag }}</td> <td>{{ form.username }}</td> </tr> <tr> <td>{{ form.password.label_tag }}</td> <td>{{ form.password }}</td> </tr> </table> <input type="submit" value="login"> <input type="hidden" name="next" value="{{ next }}"> </form> {# Assumes you set up the password_reset view in your URLconf #} <p><a href="{% url 'password_reset' %}">Lost password?</a></p> {% endblock %}
Если вы настроили аутентификацию (см. Customizing Authentication), вы можете использовать пользовательскую форму аутентификации, установив атрибут
authentication_form
. Эта форма должна принимать аргумент с ключевым словомrequest
в своем методе__init__()
и предоставлять методget_user()
, который возвращает объект аутентифицированного пользователя (этот метод вызывается только после успешной проверки формы). -
-
class
LogoutView
¶ -
Выводит пользователя из системы при запросе
POST
.Не рекомендуется, начиная с версии 4.1: Поддержка выхода из системы при запросах
GET
устарела и будет удалена в Django 5.0.Имя URL:
logout
Атрибуты:
-
next_page
¶ -
URL для перенаправления после выхода из системы. По умолчанию
LOGOUT_REDIRECT_URL
.
-
template_name
¶ -
Полное имя шаблона для отображения после выхода пользователя из системы. По умолчанию имеет значение
registration/logged_out.html
.
-
redirect_field_name
¶ -
Имя поля
GET
, содержащего URL для перенаправления после выхода из системы. По умолчанию используется значение'next'
. Переопределяет URLnext_page
, если передан заданный параметрGET
.
-
Словарь контекстных данных, которые будут добавлены к контекстным данным по умолчанию, переданным в шаблон.
-
success_url_allowed_hosts
¶ -
set
хостов, в дополнение кrequest.get_host()
, которые безопасны для перенаправления после выхода из системы. По умолчанию используется пустое значениеset
.
Контекст шаблона:
title
: Строка «Logged out», локализована.site
: ТекущийSite
, в соответствии с настройкойSITE_ID
. Если у вас не установлен фреймворк сайта, это будет установлено в экземплярRequestSite
, который берет имя сайта и домен из текущегоHttpRequest
.site_name
: Псевдоним дляsite.name
. Если у вас не установлен фреймворк сайта, это значение будет установлено в значениеrequest.META['SERVER_NAME']
. Подробнее о сайтах смотрите Структура «сайтов».
-
-
logout_then_login
(request, login_url=None)¶ -
Выводит пользователя из системы при запросе
POST
, затем перенаправляет на страницу входа в систему.Имя URL: URL по умолчанию не предоставляется
Дополнительные аргументы:
login_url
: URL страницы входа в систему для перенаправления. По умолчаниюsettings.LOGIN_URL
, если не указан.
Не рекомендуется, начиная с версии 4.1: Поддержка выхода из системы при запросах
GET
устарела и будет удалена в Django 5.0.
-
class
PasswordChangeView
¶ -
Имя URL:
password_change
Позволяет пользователю изменить свой пароль.
Атрибуты:
-
template_name
¶ -
Полное имя шаблона, который будет использоваться для отображения формы смены пароля. По умолчанию
registration/password_change_form.html
, если не указано.
-
success_url
¶ -
URL для перенаправления после успешной смены пароля. По умолчанию
'password_change_done'
.
-
form_class
¶ -
Пользовательская форма «Смена пароля», которая должна принимать аргумент в виде ключевого слова
user
. Форма отвечает за фактическое изменение пароля пользователя. По умолчанию используетсяPasswordChangeForm
.
-
Словарь контекстных данных, которые будут добавлены к контекстным данным по умолчанию, переданным в шаблон.
Контекст шаблона:
form
: Форма смены пароля (см.form_class
выше).
-
-
class
PasswordChangeDoneView
¶ -
Имя URL:
password_change_done
Страница, отображаемая после того, как пользователь изменил свой пароль.
Атрибуты:
-
template_name
¶ -
Полное имя шаблона для использования. По умолчанию
registration/password_change_done.html
, если не указано.
-
Словарь контекстных данных, которые будут добавлены к контекстным данным по умолчанию, переданным в шаблон.
-
-
class
PasswordResetView
¶ -
Имя URL:
password_reset
Позволяет пользователю сбросить пароль путем генерации ссылки одноразового использования, которая может быть использована для сброса пароля, и отправки этой ссылки на зарегистрированный адрес электронной почты пользователя.
Это представление будет отправлять сообщение электронной почты, если выполняются следующие условия:
- Указанный адрес электронной почты существует в системе.
- Запрашиваемый пользователь активен (
User.is_active
равноTrue
). - У запрашиваемого пользователя есть пароль, который можно использовать. Пользователям, отмеченным непригодным паролем (см.
set_unusable_password()
), не разрешается запрашивать сброс пароля, чтобы предотвратить злоупотребления при использовании внешнего источника аутентификации, например LDAP.
Если какое-либо из этих условий не выполняется, письмо не будет отправлено, но пользователь также не получит никакого сообщения об ошибке. Это предотвращает утечку информации к потенциальным злоумышленникам. Если вы хотите предоставить сообщение об ошибке в этом случае, вы можете подклассифицировать
PasswordResetForm
и использовать атрибутform_class
.Примечание
Имейте в виду, что отправка электронного письма требует дополнительного времени, поэтому вы можете быть уязвимы к атаке перечисления адресов электронной почты по времени из-за разницы между длительностью запроса сброса для существующего адреса электронной почты и длительностью запроса сброса для несуществующего адреса электронной почты. Чтобы уменьшить накладные расходы, вы можете использовать сторонний пакет, позволяющий отправлять электронные письма асинхронно, например django-mailer.
Атрибуты:
-
template_name
¶ -
Полное имя шаблона, который будет использоваться для отображения формы сброса пароля. По умолчанию имеет значение
registration/password_reset_form.html
, если не указано.
-
form_class
¶ -
Форма, которая будет использоваться для получения email пользователя для сброса пароля. По умолчанию имеет значение
PasswordResetForm
.
-
email_template_name
¶ -
Полное имя шаблона, который будет использоваться для создания письма со ссылкой на сброс пароля. По умолчанию имеет значение
registration/password_reset_email.html
, если не указано.
-
subject_template_name
¶ -
Полное имя шаблона, который будет использоваться для темы письма со ссылкой на сброс пароля. По умолчанию имеет значение
registration/password_reset_subject.txt
, если не указано.
-
token_generator
¶ -
Экземпляр класса для проверки одноразовой ссылки. По умолчанию это будет
default_token_generator
, это экземплярdjango.contrib.auth.tokens.PasswordResetTokenGenerator
.
-
success_url
¶ -
URL для перенаправления после успешного запроса на сброс пароля. По умолчанию
'password_reset_done'
.
-
from_email
¶ -
Действительный адрес электронной почты. По умолчанию Django использует
DEFAULT_FROM_EMAIL
.
-
Словарь контекстных данных, которые будут добавлены к контекстным данным по умолчанию, переданным в шаблон.
-
html_email_template_name
¶ -
Полное имя шаблона, используемого для генерации многокомпонентного письма text/html со ссылкой для сброса пароля. По умолчанию письмо в формате HTML не отправляется.
-
Словарь контекстных данных, которые будут доступны в шаблоне письма. Его можно использовать для переопределения значений контекста шаблона по умолчанию, перечисленных ниже, например
domain
.
Контекст шаблона:
form
: Форма (см.form_class
выше) для сброса пароля пользователя.
Контекст шаблона электронной почты:
email
: Псевдоним дляuser.email
user
: ТекущийUser
, в соответствии с полем формыemail
. Только активные пользователи могут сбрасывать свои пароли (User.is_active is True
).site_name
: Псевдоним дляsite.name
. Если у вас не установлен фреймворк сайта, это значение будет установлено в значениеrequest.META['SERVER_NAME']
. Подробнее о сайтах смотрите Структура «сайтов».domain
: Псевдоним дляsite.domain
. Если у вас не установлен фреймворк сайта, то будет установлено значениеrequest.get_host()
.protocol
: http или httpsuid
: Первичный ключ пользователя, закодированный в base 64.token
: Токен для проверки того, что ссылка сброса действительна.
Образец
registration/password_reset_email.html
(шаблон тела письма):Someone asked for password reset for email {{ email }}. Follow the link below: {{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
Тот же контекст шаблона используется для шаблона темы. Тема должна быть однострочной строкой обычного текста.
-
class
PasswordResetDoneView
¶ -
Имя URL:
password_reset_done
Страница, отображаемая после того, как пользователь получил по электронной почте ссылку для сброса пароля. Это представление вызывается по умолчанию, если для
PasswordResetView
не задан явный URLsuccess_url
.Примечание
Если указанный адрес электронной почты не существует в системе, пользователь неактивен или имеет недействительный пароль, пользователь все равно будет перенаправлен на этот вид, но письмо отправлено не будет.
Атрибуты:
-
template_name
¶ -
Полное имя шаблона для использования. По умолчанию
registration/password_reset_done.html
, если не указано.
-
Словарь контекстных данных, которые будут добавлены к контекстным данным по умолчанию, переданным в шаблон.
-
-
class
PasswordResetConfirmView
¶ -
Имя URL:
password_reset_confirm
Представляет форму для ввода нового пароля.
Ключевые аргументы из URL:
uidb64
: Идентификатор пользователя, закодированный в base 64.token
: Токен для проверки правильности пароля.
Атрибуты:
-
template_name
¶ -
Полное имя шаблона для отображения представления подтверждения пароля. Значение по умолчанию —
registration/password_reset_confirm.html
.
-
token_generator
¶ -
Экземпляр класса для проверки пароля. По умолчанию это будет
default_token_generator
, это экземплярdjango.contrib.auth.tokens.PasswordResetTokenGenerator
.
-
post_reset_login
¶ -
Булево значение, указывающее, следует ли автоматически аутентифицировать пользователя после успешного сброса пароля. По умолчанию имеет значение
False
.
-
post_reset_login_backend
¶ -
Пунктирный путь к бэкенду аутентификации, который будет использоваться при аутентификации пользователя, если
post_reset_login
являетсяTrue
. Требуется, только если у вас настроено несколькоAUTHENTICATION_BACKENDS
. По умолчанию используетсяNone
.
-
form_class
¶ -
Форма, которая будет использоваться для установки пароля. По умолчанию имеет значение
SetPasswordForm
.
-
success_url
¶ -
URL для перенаправления после сброса пароля. По умолчанию
'password_reset_complete'
.
-
Словарь контекстных данных, которые будут добавлены к контекстным данным по умолчанию, переданным в шаблон.
-
reset_url_token
¶ -
Параметр токена, отображаемый как компонент URL-адресов сброса пароля. По умолчанию имеет значение
'set-password'
.
Контекст шаблона:
form
: Форма (см.form_class
выше) для установки пароля нового пользователя.validlink
: Булево, истинно, если ссылка (комбинацияuidb64
иtoken
) действительна или еще не использована.
-
class
PasswordResetCompleteView
¶ -
Имя URL:
password_reset_complete
Представляет представление, которое информирует пользователя о том, что пароль был успешно изменен.
Атрибуты:
-
template_name
¶ -
Полное имя шаблона для отображения представления. По умолчанию имеет значение
registration/password_reset_complete.html
.
-
Словарь контекстных данных, которые будут добавлены к контекстным данным по умолчанию, переданным в шаблон.
-
Вспомогательные функции¶
-
redirect_to_login
(next, login_url=None, redirect_field_name=‘next’)¶ -
Перенаправляет на страницу входа в систему, а затем обратно на другой URL после успешного входа.
Требуемые аргументы:
next
: URL для перенаправления после успешного входа в систему.
Дополнительные аргументы:
login_url
: URL страницы входа в систему для перенаправления. По умолчаниюsettings.LOGIN_URL
, если не указан.redirect_field_name
: Имя поляGET
, содержащего URL для перенаправления после выхода из системы. Переопределяетnext
, если передан заданныйGET
параметр.
Встроенные формы¶
Если вы не хотите использовать встроенные представления, но желаете избежать необходимости писать формы для этой функциональности, система аутентификации предоставляет несколько встроенных форм, расположенных в django.contrib.auth.forms
:
-
class
AdminPasswordChangeForm
¶ -
Форма, используемая в интерфейсе администратора для изменения пароля пользователя.
Принимает
user
в качестве первого позиционного аргумента.
-
class
AuthenticationForm
¶ -
Форма для входа пользователя в систему.
Принимает
request
в качестве первого позиционного аргумента, который сохраняется в экземпляре формы для использования подклассами.-
confirm_login_allowed
(user)¶ -
По умолчанию
AuthenticationForm
отклоняет пользователей, чей флагis_active
установлен вFalse
. Вы можете отменить это поведение с помощью пользовательской политики, чтобы определить, какие пользователи могут войти в систему. Сделайте это с помощью пользовательской формы, которая является подклассомAuthenticationForm
и переопределяет методconfirm_login_allowed()
. Этот метод должен вызывать ошибкуValidationError
, если данный пользователь не может войти в систему.Например, чтобы разрешить всем пользователям входить в систему независимо от статуса «активный»:
from django.contrib.auth.forms import AuthenticationForm class AuthenticationFormWithInactiveUsersOkay(AuthenticationForm): def confirm_login_allowed(self, user): pass
(В этом случае вам также необходимо использовать бэкенд аутентификации, который позволяет неактивным пользователям, например
AllowAllUsersModelBackend
).Или разрешить вход только некоторым активным пользователям:
class PickyAuthenticationForm(AuthenticationForm): def confirm_login_allowed(self, user): if not user.is_active: raise ValidationError( _("This account is inactive."), code='inactive', ) if user.username.startswith('b'): raise ValidationError( _("Sorry, accounts starting with 'b' aren't welcome here."), code='no_b_users', )
-
-
class
PasswordChangeForm
¶ -
Форма, позволяющая пользователю изменить свой пароль.
-
class
PasswordResetForm
¶ -
Форма для создания и отправки по электронной почте ссылки одноразового использования для сброса пароля пользователя.
-
send_mail
(subject_template_name, email_template_name, context, from_email, to_email, html_email_template_name=None)¶ -
Использует аргументы для отправки сообщения
EmailMultiAlternatives
. Может быть переопределено, чтобы настроить способ отправки письма пользователю.Параметры: - subject_template_name – шаблон для темы.
- email_template_name – шаблон для тела письма.
- context – контекст, передаваемый в
subject_template
,email_template
иhtml_email_template
(если это неNone
). - from_email – электронная почта отправителя.
- to_email – электронная почта подателя запроса.
- html_email_template_name – шаблон для HTML-тела; по умолчанию
None
, в этом случае отправляется обычное текстовое письмо.
По умолчанию
save()
заполняетcontext
теми же переменными, которыеPasswordResetView
передает своему почтовому контексту.
-
-
class
SetPasswordForm
¶ -
Форма, позволяющая пользователю изменить свой пароль без ввода старого пароля.
-
class
UserChangeForm
¶ -
Форма, используемая в интерфейсе администратора для изменения информации и разрешений пользователя.
-
class
UserCreationForm
¶ -
A
ModelForm
для создания нового пользователя.Он имеет три поля:
username
(из модели пользователя),password1
иpassword2
. Он проверяет соответствиеpassword1
иpassword2
, проверяет пароль с помощьюvalidate_password()
и устанавливает пароль пользователя с помощьюset_password()
.
Данные аутентификации в шаблонах¶
Текущий зарегистрированный пользователь и его разрешения доступны в template context при использовании RequestContext
.
Техничность
Технически, эти переменные становятся доступными в контексте шаблона, только если вы используете RequestContext
и включен контекстный процессор 'django.contrib.auth.context_processors.auth'
. Он находится в сгенерированном по умолчанию файле настроек. Подробнее см. в RequestContext docs.
Пользователи¶
При отображении шаблона RequestContext
в переменной шаблона User
хранится текущий зарегистрированный пользователь, либо экземпляр AnonymousUser
, либо экземпляр {{ user }}
:
{% if user.is_authenticated %} <p>Welcome, {{ user.username }}. Thanks for logging in.</p> {% else %} <p>Welcome, new user. Please log in.</p> {% endif %}
Эта контекстная переменная шаблона недоступна, если не используется RequestContext
.
Права¶
Разрешения вошедшего в систему пользователя хранятся в переменной шаблона {{ perms }}
. Это экземпляр django.contrib.auth.context_processors.PermWrapper
, который является дружественным шаблону прокси разрешения.
Оценка одноатрибутного поиска {{ perms }}
как булева является прокси для User.has_module_perms()
. Например, чтобы проверить, есть ли у вошедшего пользователя какие-либо разрешения в приложении foo
:
Оценка двухуровневого атрибута поиска как булева является прокси для User.has_perm()
. Например, чтобы проверить, есть ли у вошедшего пользователя разрешение foo.add_vote
:
{% if perms.foo.add_vote %}
Вот более полный пример проверки разрешений в шаблоне:
{% if perms.foo %} <p>You have permission to do something in the foo app.</p> {% if perms.foo.add_vote %} <p>You can vote!</p> {% endif %} {% if perms.foo.add_driving %} <p>You can drive!</p> {% endif %} {% else %} <p>You don't have permission to do anything in the foo app.</p> {% endif %}
Можно также искать разрешения по операторам {% if in %}
. Например:
{% if 'foo' in perms %} {% if 'foo.add_vote' in perms %} <p>In lookup works, too.</p> {% endif %} {% endif %}
Управление пользователями в админке¶
Если у вас установлены и django.contrib.admin
, и django.contrib.auth
, администратор предоставляет удобный способ просмотра и управления пользователями, группами и разрешениями. Пользователи могут быть созданы и удалены, как и любая модель Django. Группы могут быть созданы, а разрешения могут быть назначены пользователям или группам. Журнал пользовательских правок моделей, сделанных в админке, также сохраняется и отображается.
Создание пользователей¶
Вы должны увидеть ссылку на «Пользователи» в разделе «Auth» на главной странице индекса администратора. Страница администратора «Добавить пользователя» отличается от стандартных страниц администратора тем, что она требует выбора имени пользователя и пароля, прежде чем вы сможете редактировать остальные поля пользователя.
Также обратите внимание: если вы хотите, чтобы учетная запись пользователя могла создавать пользователей с помощью сайта Django admin, вам нужно дать ей разрешение на добавление пользователей и изменение пользователей (т.е. разрешения «Добавить пользователя» и «Изменить пользователя»). Если у учетной записи есть разрешение на добавление пользователей, но нет разрешения на их изменение, эта учетная запись не сможет добавлять пользователей. Почему? Потому что если у вас есть разрешение на добавление пользователей, у вас есть возможность создавать суперпользователей, которые, в свою очередь, могут изменять других пользователей. Поэтому Django требует разрешения на добавление и изменение в качестве небольшой меры безопасности.
Вдумчиво отнеситесь к тому, как вы позволяете пользователям управлять разрешениями. Если вы дадите не суперпользователю возможность редактировать пользователей, это в конечном итоге будет равносильно предоставлению ему статуса суперпользователя, поскольку он сможет повышать разрешения пользователей, включая себя!
Изменение паролей¶
Пароли пользователей не отображаются в админке (и не хранятся в базе данных), но отображаются password storage details. В отображение этой информации включена ссылка на форму смены пароля, которая позволяет администраторам изменять пароли пользователей.
- Назад
- Обзор: Django
- Далее
В данном руководстве мы продемонстрируем вам систему входа пользователя на ваш сайт используя его собственный аккаунт. Кроме того, мы покажем как реализовать контроль того, что может видеть и делать пользователь, в зависимости от того, залогинен он, или нет, а также имеет ли он соответствующий уровень прав доступа (permissions). Для того чтобы продемонстрировать все это, мы расширим LocalLibrary, добавив страницы для входа/выхода, а также страницы просмотра/редактирования книг, специфические для пользователя и персонала.
Требования: | Завершить изучение предыдущих тем руководства, включая Руководство Django Часть 7: Работа с сессиями. |
---|---|
Цель: | Понимать как настроить и использовать механизм аутентификации пользователя и разграничений прав доступа. |
Обзор
Django предоставляет систему аутентификации и авторизации («permission») пользователя, реализованную на основе фреймворка работы с сессиями, который мы рассматривали в предыдущей части. Система аутентификации и авторизации позволяет вам проверять учётные данные пользователей и определять какие действия какой пользователь может выполнять. Данный фреймворк включает в себя встроенные модели для Пользователей
и Групп
(основной способ применения прав доступа для более чем одного пользователя), непосредственно саму систему прав доступа (permissions)/флаги, которые определяют может ли пользователь выполнить задачу, с какой формой и отображением для авторизованных пользователей, а так же получить доступ к контенту с ограниченным доступом.
Примечание: В соответствии с идеологией Django система аутентификации является очень общей и, таким образом, не предоставляет некоторые возможности, которые присутствуют в других системах веб-аутентификации. Решениями некоторых общих задач занимаются пакеты сторонних разработчиков, например, защита от подбора пароля (через стороннюю библиотеку OAuth).
В данном разделе руководства мы покажем вам реализацию аутентификации пользователя на сайте LocalLibrary, создание страниц входа/выхода, добавления разграничения доступа (permissions) к вашим моделям, а также продемонстрируем контроль за доступом к некоторым страницам. Мы будем использовать аутентификацию/авторизацию для показа пользователям и сотрудникам библиотеки, списков книг, которые были взяты на прокат.
Система аутентификации является очень гибкой и позволяет вам формировать свои собственные URL-адреса, формы, отображения, а также шаблоны страниц, если вы пожелаете, с нуля, через простой вызов функций соответствующего API для авторизации пользователя. Тем не менее, в данной статье мы будем использовать «встроенные» в Django методы отображений и форм аутентификации, а также методы построения страниц входа и выхода. Нам все ещё необходимо создавать шаблоны страниц, но это будет достаточно несложно.
Мы покажем вам как реализовать разграничение доступа (permissions), а также выполнять соответствующую проверку статусов авторизации и прав доступа, в отображениях, и в шаблонах страниц.
Подключение аутентификации
Аутентификация была подключена автоматически когда мы создали скелет сайта (в части 2), таким образом на данный момент вам ничего не надо делать.
Примечание: Необходимые настройки были выполнены для нас, когда мы создали приложение при помощи команды django-admin startproject
. Таблицы базы данных для пользователей и модели авторизации были созданы, когда в первый раз выполнили команду python manage.py migrate
.
Соответствующие настройки сделаны в параметрах INSTALLED_APPS
и MIDDLEWARE
файла проекта (locallibrary/locallibrary/settings.py), как показано ниже:
INSTALLED_APPS = [
...
'django.contrib.auth', # Фреймворк аутентификации и моделей по умолчанию.
'django.contrib.contenttypes', # Django контент-типовая система (даёт разрешения, связанные с моделями).
....
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware', # Управление сессиями между запросами
...
'django.contrib.auth.middleware.AuthenticationMiddleware', # Связывает пользователей, использующих сессии, запросами.
....
Создание пользователей и групп
Вы уже создали своего первого пользователя когда мы рассматривали Административная панель сайта Django в части 4 (это был суперпользователь, созданный при помощи команды python manage.py createsuperuser
). Наш суперпользователь уже авторизован и имеет все необходимые уровни доступа к данным и функциям, таким образом нам необходимо создать тестового пользователя для отработки соответствующей работы сайта. В качестве наиболее быстрого способа, мы будем использовать административную панель сайта для создания соответствующих групп и аккаунтов locallibrary.
Примечание: вы можете создавать пользователей программно, как показано ниже. Например, вам мог бы подойти данный способ в том случае, если вы разрабатываете интерфейс, который позволяет пользователям создавать их собственные аккаунты (вы не должны предоставлять доступ пользователям к административной панели вашего сайта).
from django.contrib.auth.models import User
# Создайте пользователя и сохраните его в базе данных
user = User.objects.create_user('myusername', 'myemail@crazymail.com', 'mypassword')
# Обновите поля и сохраните их снова
user.first_name = 'John'
user.last_name = 'Citizen'
user.save()
Ниже мы создадим группу, а затем пользователя. Несмотря на то, что у нас пока нет никаких разрешений для добавления к нашей библиотеке каких-либо членов, если мы захотим это сделать в будущем, то будет намного проще добавлять их к уже созданной группе, с заданной аутентификацией.
Запустите сервер разработки и перейдите к административной панели вашего сайта (http://127.0.0.1:8000/admin/
). Залогиньтесь на сайте при помощи параметров (имя пользователя и пароля) аккаунта суперпользователя. Самая «верхняя» страница панели Администратора показывает все наши модели. Для того, чтобы увидеть записи в разделе Authentication and Authorisation вы можете нажать на ссылку Users, или Groups.
В первую очередь, в качестве нового члена нашего сайта, давайте создадим новую группу.
- Нажмите на кнопку Add (Добавить) (рядом с Group) и создайте новую группу; для данной группы введите Name (Имя) «Library Members».
- Для данной группы не нужны какие-либо разрешения, поэтому мы просто нажимаем кнопку SAVE (Сохранить) (вы перейдёте к списку групп).
Теперь давайте создадим пользователя:
- Перейдите обратно на домашнюю страницу административной панели
- Для перехода к диалогу добавления пользователя нажмите на кнопку Add, соответствующую строке Users (Пользователи).
- Введите соответствующие Username (имя пользователя) и Password/Password confirmation (пароль/подтверждение пароля) для вашего тестового пользователя
-
Нажмите SAVE для завершения процесса создания пользователя.
Административная часть сайта создаст нового пользователя и немедленно перенаправит вас на страницу Change user (Изменение параметров пользователя) где вы можете, соответственно, изменить ваш username, а кроме того добавить информацию для дополнительных полей модели User. Эти поля включают в себя имя пользователя, фамилию, адрес электронной почты, статус пользователя, а также соответствующие параметры доступа (может быть установлен только флаг Active). Ниже вы можете определить группу для пользователя и необходимые параметры доступа, а кроме того, вы можете увидеть важные даты, относящиеся к пользователю (дату подключения к сайту и дату последнего входа).
- В разделе Groups, из списка Доступные группы выберите группу Library Member, а затем переместите её в блок «Выбранные группы» (нажмите стрелку-«направо», находящуюся между блоками).
- Больше нам не нужно здесь нечего делать, просто нажмите «Save»(Сохранить), и вы вернётесь к списку созданных пользователей.
Вот и все! Теперь у вас есть учётная запись «обычного члена библиотеки», которую вы сможете использовать для тестирования (как только добавим страницы, чтобы пользователи могли войти в систему).
Примечание: Попробуйте создать другого пользователя, например «Библиотекаря». Так же создайте группу «Библиотекарей» и добавьте туда своего только что созданного библиотекаря
Настройка представлений проверки
Django предоставляет почти все, что нужно для создания страниц аутентификации входа, выхода из системы и управления паролями из коробки. Это включает в себя url-адреса, представления (views) и формы,но не включает шаблоны — мы должны создать свой собственный шаблон!
В этом разделе мы покажем, как интегрировать систему по умолчанию в Сайт LocalLibrary и создать шаблоны. Мы поместим их в основные URL проекта.
Примечание: вы не должны использовать этот код, но вполне вероятно, что вы хотите, потому что это делает вещи намного проще. Вам почти наверняка потребуется изменить код обработки формы, если вы измените свою модель пользователя (сложная тема!) но даже в этом случае вы всё равно сможете использовать функции просмотра запасов.
**Примечание:**В этом случае мы могли бы разумно поместить страницы аутентификации, включая URL-адреса и шаблоны, в наше приложение каталога. Однако, если бы у нас было несколько приложений, было бы лучше отделить это общее поведение входа в систему и иметь его доступным на всем сайте, так что это то, что мы показали здесь!
Проектирование URLs
Добавьте следующее в нижней части проекта urls.py файл (locallibrary/locallibrary/urls.py) файл:
#Add Django site authentication urls (for login, logout, password management)
urlpatterns += [
path('accounts/', include('django.contrib.auth.urls')),
]
Перейдите по http://127.0.0.1:8000/accounts/
URL (обратите внимание на косую черту!), Django покажет ошибку, что он не смог найти этот URL, и перечислить все URL, которые он пытался открыть. Из этого вы можете увидеть URL-адреса, которые будут работать, например:
Примечание: Примечание. Использование вышеуказанного метода добавляет следующие URL-адреса с именами в квадратных скобках, которые могут использоваться для изменения сопоставлений URL-адресов. Вам не нужно реализовывать что-либо ещё — приведённое выше сопоставление URL-адресов автоматически отображает указанные ниже URL-адреса.
Примечание:
accounts/ login/ [name='login']
accounts/ logout/ [name='logout']
accounts/ password_change/ [name='password_change']
accounts/ password_change/done/ [name='password_change_done']
accounts/ password_reset/ [name='password_reset']
accounts/ password_reset/done/ [name='password_reset_done']
accounts/ reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/ reset/done/ [name='password_reset_complete']
Теперь попробуйте перейти к URL-адресу входа (http://127.0.0.1:8000/accounts/login/
). Это приведёт к сбою снова, но с ошибкой, сообщающей вам, что нам не хватает требуемого шаблона (registration / login.html) в пути поиска шаблона. Вы увидите следующие строки, перечисленные в жёлтом разделе вверху:
Exception Type: TemplateDoesNotExist
Exception Value: registration/login.html
Следующий шаг — создать каталог регистрации в пути поиска, а затем добавить файл login.html.
Каталог шаблонов
URL-адреса (и неявные представления), которые мы только что добавили, ожидают найти связанные с ними шаблоны в каталоге / регистрации / где-то в пути поиска шаблонов.
Для этого сайта мы разместим наши HTML-страницы в каталоге templates / registration /. Этот каталог должен находиться в корневом каталоге проекта, то есть в том же каталоге, что и в каталоге и папках locallibrary). Создайте эти папки сейчас.
Примечание: ваша структура папок теперь должна выглядеть как показано внизу:
locallibrary (django project folder)
|_catalog
|_locallibrary
|_templates (new)
|_registration
Чтобы сделать эти директории видимыми для загрузчика шаблонов (т. е. помещать этот каталог в путь поиска шаблона) откройте настройки проекта (/locallibrary/locallibrary/settings.py), и обновите в секции TEMPLATES
строку 'DIRS'
как показано.
TEMPLATES = [
{
...
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
...
Шаблон аутентификации
Предупреждение: Важно: Шаблоны аутентификации, представленные в этой статье, являются очень простой / слегка изменённой версией шаблонов логина демонстрации Django. Возможно, вам придётся настроить их для собственного использования!
Создайте новый HTML файл, названный /locallibrary/templates/registration/login.html. дайте ему следующее содержание:
{% extends "base_generic.html" %}
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% endif %}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>
{% endblock %}
Этот шаблон имеет сходство с тем, что мы видели раньше — он расширяет наш базовый шаблон и переопределяет блок контента. Остальная часть кода — это довольно стандартный код обработки формы, о котором мы поговорим в следующем учебном пособии. Все, что вам нужно знать, это показ формы, в которой вы можете ввести своё имя пользователя и пароль, а если вы введёте недопустимые значения, вам будет предложено ввести правильные значения, когда страница обновится.
Перейдите на страницу входа (http://127.0.0.1:8000/accounts/login/
) когда вы сохраните свой шаблон, и вы должны увидеть что-то наподобие этого:
Если ваша попытка войти в систему будет успешной, вы будете перенаправлены на другую страницу (по умолчанию это будет http://127.0.0.1:8000/accounts/profile/
). Проблема здесь в том, что по умолчанию Django ожидает, что после входа в систему вы захотите перейти на страницу профиля, что может быть или не быть. Поскольку вы ещё не определили эту страницу, вы получите ещё одну ошибку!
Откройте настройки проекта (/locallibrary/locallibrary/settings.py) и добавьте текст ниже. Теперь, когда вы входите в систему, вы по умолчанию должны перенаправляться на домашнюю страницу сайта.
# Redirect to home URL after login (Default redirects to /accounts/profile/)
LOGIN_REDIRECT_URL = '/'
Шаблон выхода
Если вы перейдёте по URL-адресу выхода (http://127.0.0.1:8000/accounts/logout/
), то увидите странное поведение — ваш пользователь наверняка выйдет из системы, но вы попадёте на страницу выхода администратора. Это не то, что вам нужно, хотя бы потому, что ссылка для входа на этой странице приведёт вас к экрану входа в систему администратора. (и это доступно только для пользователей, у которых есть разрешение is_staff
).
Создайте и откройте /locallibrary/templates/registration/logged_out.html. Скопируйте текст ниже:
{% extends "base_generic.html" %}
{% block content %}
<p>Logged out!</p>
<a href="{% url 'login'%}">Click here to login again.</a>
{% endblock %}
Этот шаблон очень прост. Он просто отображает сообщение, информирующее вас о том, что вы вышли из системы, и предоставляет ссылку, которую вы можете нажать, чтобы вернуться на экран входа в систему. Если вы снова перейдёте на страницу выхода из системы, вы увидите эту страницу:
Шаблон сброса пароля
Система сброса пароля по умолчанию использует электронную почту, чтобы отправить пользователю ссылку на сброс. Вам необходимо создать формы, чтобы получить адрес электронной почты пользователя, отправить электронное письмо, разрешить им вводить новый пароль и отметить, когда весь процесс будет завершён.
В качестве отправной точки можно использовать следующие шаблоны.
Форма сброса пароля
Это форма, используемая для получения адреса электронной почты пользователя (для отправки пароля для сброса пароля). Создайте /locallibrary/templates/registration/password_reset_form.html и дайте ему следующее содержание:
{% extends "base_generic.html" %}
{% block content %}
<form action="" method="post">{% csrf_token %}
{% if form.email.errors %} {{ form.email.errors }} {% endif %}
<p>{{ form.email }}</p>
<input type="submit" class="btn btn-default btn-lg" value="Reset password" />
</form>
{% endblock %}
Сброс пароля
Эта форма отображается после того, как ваш адрес электронной почты будет собран. Создайте /locallibrary/templates/registration/password_reset_done.html, и дайте ему следующее содержание:
{% extends "base_generic.html" %}
{% block content %}
<p>We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.</p>
{% endblock %}
Сброс пароля по email
Этот шаблон предоставляет текст электронной почты HTML, содержащий ссылку на сброс, которую мы отправим пользователям. Создайте /locallibrary/templates/registration/password_reset_email.html и дайте ему следующее содержание:
Someone asked for password reset for email {{ email }}. Follow the link below:
{{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
Подтверждение на сброс пароля
На этой странице вы вводите новый пароль после нажатия ссылки в электронном письме с возвратом пароля. Создайте /locallibrary/templates/registration/password_reset_confirm.html и дайте ему следующее содержание:
{% extends "base_generic.html" %}
{% block content %}
{% if validlink %}
<p>Please enter (and confirm) your new password.</p>
<form action="" method="post">
{% csrf_token %}
<table>
<tr>
<td>{{ form.new_password1.errors }}
<label for="id_new_password1">New password:</label></td>
<td>{{ form.new_password1 }}</td>
</tr>
<tr>
<td>{{ form.new_password2.errors }}
<label for="id_new_password2">Confirm password:</label></td>
<td>{{ form.new_password2 }}</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Change my password" /></td>
</tr>
</table>
</form>
{% else %}
<h1>Password reset failed</h1>
<p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p>
{% endif %}
{% endblock %}
Сброс пароля завершён
Это последний шаблон сброса пароля, который отображается, чтобы уведомить вас о завершении сброса пароля. Создайте /locallibrary/templates/registration/password_reset_complete.html и дайте ему следующее содержание:
{% extends "base_generic.html" %}
{% block content %}
<h1>The password has been changed!</h1>
<p><a href="{% url 'login' %}">log in again?</a></p>
{% endblock %}
Тестирование новых страниц аутентификации
Теперь, когда вы добавили конфигурацию URL и создали все эти шаблоны, теперь страницы аутентификации должны работать! Вы можете протестировать новые страницы аутентификации, попытавшись войти в систему, а затем выйдите из учётной записи суперпользователя, используя эти URL-адреса:
http://127.0.0.1:8000/accounts/login/
http://127.0.0.1:8000/accounts/logout/
Вы сможете проверить функцию сброса пароля по ссылке на странице входа. Имейте в виду, что Django отправляет только сбросные электронные письма на адреса (пользователи), которые уже хранятся в его базе данных!
Примечание: Система сброса пароля требует, чтобы ваш сайт поддерживал электронную почту, что выходит за рамки этой статьи, поэтому эта часть ещё не будет работать. Чтобы разрешить тестирование, поместите следующую строку в конец файла settings.py. Это регистрирует любые письма, отправленные на консоль (чтобы вы могли скопировать ссылку на сброс пароля с консоли).
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Для получения дополнительной информации см. Отправка email (Django docs).
Тестирование проверки подлинности пользователей
В этом разделе мы рассмотрим, что мы можем сделать, чтобы выборочно контролировать контент, который видят пользователи, на основе того, вошли ли они в систему или нет.
Тестирование в шаблонах
Вы можете получить информацию о текущем зарегистрированном пользователе в шаблонах с переменной шаблона {{user}} (это добавляется в контекст шаблона по умолчанию при настройке проекта, как и в нашем скелете).
Обычно вы сначала проверяете переменную шаблона {{user.is_authenticated}}, чтобы определить, имеет ли пользователь право видеть конкретный контент. Чтобы продемонстрировать это, мы обновим нашу боковую панель, чтобы отобразить ссылку «Вход», если пользователь вышел из системы, и ссылку «Выход», если он вошёл в систему.
Откройте базовый шаблон (/locallibrary/catalog/templates/base_generic.html) и скопируйте следующий текст в sidebar блок непосредственно перед тегом шаблона endblock.
<ul class="sidebar-nav">
...
{% if user.is_authenticated %}
<li>User: {{ user.get_username }}</li>
<li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>
{% else %}
<li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>
{% endif %}
</ul>
Как вы можете видеть, мы используем теги шаблона if-else-endif для условного отображения текста на основе того, является ли {{user.is_authenticated}} истинным. Если пользователь аутентифицирован, мы знаем, что у нас есть действительный пользователь, поэтому мы вызываем {{user.get_username}}, чтобы отобразить их имя.
Мы создаём URL-адрес для входа и выхода из системы, используя тег шаблона URL-адреса и имена соответствующих конфигураций URLs. Также обратите внимание на то, как мы добавили ?next={{request.path}}
в конец URLs. Это означает, что следующий URL-адрес содержит адрес (URL) текущей страницы, в конце связанного URL-адреса. После того, как пользователь успешно выполнил вход в систему, представления будут использовать значение «next
» чтобы перенаправить пользователя обратно на страницу, где они сначала нажали ссылку входа / выхода из системы.
Примечание: Попробуйте! Если вы находитесь на главной странице и вы нажимаете «Вход / Выход» на боковой панели, то после завершения операции вы должны вернуться на ту же страницу.
Тестирование в представлениях
Если вы используете функциональные представления, самым простым способом ограничить доступ к вашим функциям является применение login_required
декоратор к вашей функции просмотра, как показано ниже. Если пользователь вошёл в систему, ваш код просмотра будет выполняться как обычно. Если пользователь не вошёл в систему, это перенаправит URL-адрес входа, определённый в настройках проекта. (settings.LOGIN_URL
), передав текущий абсолютный путь в качестве next параметра URL. Если пользователю удастся войти в систему, они будут возвращены на эту страницу, но на этот раз аутентифицированы.
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
Примечание: Вы можете сделать то же самое вручную, путём тестирования request.user.is_authenticated
, но декоратор намного удобнее!
Аналогичным образом, самый простой способ ограничить доступ к зарегистрированным пользователям в ваших представлениях на основе классов — это производные от LoginRequiredMixin
. Вы должны объявить этот mixin сначала в списке суперкласса, перед классом основного представления.
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
...
Это имеет такое же поведение при переадресации, что и login_required
декоратор. Вы также можете указать альтернативное местоположение для перенаправления пользователя, если он не аутентифицирован (login_url
), и имя параметра URL вместо «next
» , чтобы вставить текущий абсолютный путь (redirect_field_name
).
class MyView(LoginRequiredMixin, View):
login_url = '/login/'
redirect_field_name = 'redirect_to'
Для получения дополнительной информации ознакомьтесь с Django docs here.
Пример — перечисление книг текущего пользователя
Теперь, когда мы знаем, как ограничить страницу определённому пользователю, создайте представление о книгах, которые заимствовал текущий пользователь.
К сожалению, у нас пока нет возможности пользователям использовать книги! Поэтому, прежде чем мы сможем создать список книг, мы сначала расширим BookInstance
модель для поддержки концепции заимствования и использования приложения Django Admin для заимствования ряда книг нашему тестовому пользователю.
Модели
Прежде всего, мы должны предоставить пользователям возможность кредита на BookInstance
(у нас уже есть status
и due_back
дата, но у нас пока нет связи между этой моделью и пользователем. Мы создадим его с помощью поля ForeignKey
(один ко многим). Нам также нужен простой механизм для проверки того, просрочена ли заёмная книга.
Откройте catalog/models.py, и импортируйте модель User
из django.contrib.auth.models
(добавьте это чуть ниже предыдущей строки импорта в верхней части файла, так User
доступен для последующего кода, что позволяет использовать его):
from django.contrib.auth.models import User
Затем добавьте поле borrower
в модель BookInstance
:
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
Пока мы здесь, давайте добавим свойство, которое мы можем вызвать из наших шаблонов, чтобы указать, просрочен ли конкретный экземпляр книги. Хотя мы могли бы рассчитать это в самом шаблоне, использование свойства, как показано ниже, будет намного более эффективным. Добавьте это где-нибудь в верхней части файла:
from datetime import date
Теперь добавьте следующее определение свойства внутри класса BookInstance:
@property
def is_overdue(self):
if self.due_back and date.today() > self.due_back:
return True
return False
Примечание: Примечание. Сначала мы проверим, является ли due_back
пустым, прежде чем проводить сравнение. Пустое поле due_back
заставило Django выкидывать ошибку, а не показывать страницу: пустые значения не сопоставимы. Это не то, что мы хотели бы, чтобы наши пользователи испытывали!
Теперь, когда мы обновили наши модели, нам нужно будет внести новые изменения в проект, а затем применить эти миграции:
python3 manage.py makemigrations
python3 manage.py migrate
Admin
Теперь откройте каталог catalog/admin.py, и добавьте поле borrower
в класс BookInstanceAdmin
, как в list_display
, так и в полях fieldsets
, как показано ниже. Это сделает поле видимым в разделе Admin, так что мы можем при необходимости назначить User
в BookInstance
.
@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
list_display = ('book', 'status', 'borrower', 'due_back', 'id')
list_filter = ('status', 'due_back')
fieldsets = (
(None, {
'fields': ('book','imprint', 'id')
}),
('Availability', {
'fields': ('status', 'due_back','borrower')
}),
)
Займите несколько книг
Теперь, когда возможно кредитовать книги конкретному пользователю, зайдите и заработайте на нескольких записей в BookInstance
. Установите borrowed
поле вашему тестовому пользователю, сделайте status
«В займе» и установите сроки оплаты как в будущем, так и в прошлом.
Примечание: Мы не будем описывать процесс, так как вы уже знаете, как использовать Admin сайт!
Займ в представлении
Теперь мы добавим представление для получения списка всех книг, которые были предоставлены текущему пользователю. Мы будем использовать один и тот же общий класс, с которым мы знакомы, но на этот раз мы также будем импортировать и выводить из LoginRequiredMixin
, так что только вошедший пользователь сможет вызвать это представление. Мы также решили объявить template_name
, вместо того, чтобы использовать значение по умолчанию, потому что у нас может быть несколько разных списков записей BookInstance, с разными представлениями и шаблонами.
Добавьте следующее в catalog/views.py:
from django.contrib.auth.mixins import LoginRequiredMixin
class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
"""
Generic class-based view listing books on loan to current user.
"""
model = BookInstance
template_name ='catalog/bookinstance_list_borrowed_user.html'
paginate_by = 10
def get_queryset(self):
return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')
Чтобы ограничить наш запрос только объектами BookInstance для текущего пользователя, мы повторно реализуем get_queryset()
, как показано выше. Обратите внимание, что «o» это сохранённый код для «on loan» и мы сортируем по дате due_back
, чтобы сначала отображались самые старые элементы.
URL-адрес для заёмных книг
Теперь откройте /catalog/urls.py и добавьте url()
, указывая на приведённое выше представление (вы можете просто скопировать текст ниже в конец файла).
urlpatterns += [
url(r'^mybooks/$', views.LoanedBooksByUserListView.as_view(), name='my-borrowed'),
]
Шаблон для заёмных книг
Теперь все, что нам нужно сделать для этой страницы, — это добавить шаблон. Сначала создайте файл шаблона /catalog/templates/catalog/bookinstance_list_borrowed_user.html и дайте ему следующее содержание:
{% extends "base_generic.html" %}
{% block content %}
<h1>Borrowed books</h1>
{% if bookinstance_list %}
<ul>
{% for bookinst in bookinstance_list %}
<li class="{% if bookinst.is_overdue %}text-danger{% endif %}">
<a href="{% url 'book-detail' bookinst.book.pk %}">{{bookinst.book.title}}</a> ({{ bookinst.due_back }})
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no books borrowed.</p>
{% endif %}
{% endblock %}
Этот шаблон очень похож на тот, который мы создали ранее для объектов Book
и Author
. Единственное, что «новое» здесь, это то, что мы проверяем метод, который мы добавили в модель (bookinst.is_overdue
) с целью использовать его для изменения цвета просроченных предметов.
Когда сервер разработки запущен, вы должны теперь иметь возможность просматривать список для зарегистрированного пользователя в своём браузере по адресу http://127.0.0.1:8000/catalog/mybooks/
. Попробуйте это, когда ваш пользователь войдёт в систему и выйдет из системы (во втором случае вы должны быть перенаправлены на страницу входа в систему).
Добавить список на боковую панель
Последний шаг — добавить ссылку на эту новую страницу в sidebar. Мы поместим это в тот же раздел, где мы покажем другую информацию для зарегистрированного пользователя.
Откройте базовый шаблон (/locallibrary/catalog/templates/base_generic.html) и добавьте выделенную строку из sidebar, как показано на рисунке.
<ul class="sidebar-nav">
{% if user.is_authenticated %}
<li>User: {{ user.get_username }}</li>
<li><a href="{% url 'my-borrowed' %}">My Borrowed</a></li>
<li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>
{% else %}
<li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>
{% endif %}
</ul>
На что это похоже?
Когда любой пользователь войдёт в систему, он будет видеть ссылку «Мной позаимствовано (My Borrowed)» в боковой колонке, и список книг, показанных ниже (первая книга не имеет установленной даты, что является ошибкой, которую мы надеемся исправить в более позднем уроке!).
Права доступа
Права доступа связаны с моделями и определяют операции, которые могут выполняться на экземпляре модели самим пользователем, у которого есть разрешение. По умолчанию Django автоматически даёт добавить, изменить, и удалить разрешения у всех моделей, которые позволяют пользователям с правом доступа выполнять связанные действия через администратора сайта. Вы можете определить свои собственные разрешения для моделей и предоставить их конкретным пользователям. Вы также можете изменить разрешения, связанные с разными экземплярами одной и той же модели. Тестирование разрешений в представлениях и шаблонах очень похоже на тестирование по статусу аутентификации (фактически, тестирование прав доступа также проверяет аутентификацию).
Модели
Определение разрешений выполняется в разделе моделей «class Meta
» , используется permissions
поле. Вы можете указать столько разрешений, сколько необходимо в кортеже, причём каждое разрешение определяется во вложенном кортеже, содержащем имя разрешения и отображаемое значение разрешения. Например, мы можем определить разрешение, позволяющее пользователю отметить, что книга была возвращена, как показано здесь:
class BookInstance(models.Model):
...
class Meta:
...
permissions = (("can_mark_returned", "Set book as returned"),)
Затем мы могли бы назначить разрешение группе «Библиотекарь» (Librarian) на сайте администратора.
Откройте catalog/models.py, и добавьте разрешение, как показано выше. Вам нужно будет повторно выполнить миграцию (вызвав python3 manage.py makemigrations
и python3 manage.py migrate
) для надлежащего обновления базы данных.
Шаблоны
Разрешения текущего пользователя хранятся в переменной шаблона, называемой {{ perms }}
. Вы можете проверить, имеет ли текущий пользователь определённое разрешение, используя конкретное имя переменной в соответствующем приложении «Django» — например, {{ perms.catalog.can_mark_returned }}
будет True
если у пользователя есть это разрешение, а False
— в противном случае. Обычно мы проверяем разрешение с использованием шаблона {% if %}
, как показано в:
{% if perms.catalog.can_mark_returned %}
<!-- We can mark a BookInstance as returned. -->
<!-- Perhaps add code to link to a "book return" view here. -->
{% endif %}
Представления
Разрешения можно проверить в представлении функции, используя permission_required
декоратор или в представлении на основе классов, используя PermissionRequiredMixin
. шаблон и поведение такие же, как для аутентификации входа в систему, хотя, конечно, вы можете разумно добавить несколько разрешений.
Функция в представлении с декоратором:
from django.contrib.auth.decorators import permission_required
@permission_required('catalog.can_mark_returned')
@permission_required('catalog.can_edit')
def my_view(request):
...
Требуется разрешение mixin для представлений на основе классов.
from django.contrib.auth.mixins import PermissionRequiredMixin
class MyView(PermissionRequiredMixin, View):
permission_required = 'catalog.can_mark_returned'
# Or multiple permissions
permission_required = ('catalog.can_mark_returned', 'catalog.can_edit')
# Note that 'catalog.can_edit' is just an example
# the catalog application doesn't have such permission!
Пример
Мы не будем обновлять LocalLibrary здесь; возможно, в следующем уроке!
Испытайте себя
Ранее в этой статье мы показали вам, как создать страницу для текущего пользователя, в которой перечислены книги, которые они заимствовали. Теперь задача состоит в том, чтобы создать аналогичную страницу, которая видна только для библиотекарей, которая отображает все книги, которые были заимствованы, и которая показывает имя каждого заёмщика.
Вы должны следовать той же схеме, что и для другого представления. Главное отличие состоит в том, что вам нужно ограничить представление только библиотекарями. Вы можете сделать это на основе того, является ли пользователь сотрудником (декоратор функции: staff_member_required
, переменная шаблона: user.is_staff
) но мы рекомендуем вам вместо этого использовать can_mark_returned
разрешения и PermissionRequiredMixin
, как описано в предыдущем разделе.
Предупреждение: Важно: Не забудьте использовать вашего суперпользователя для тестирования на основе разрешений (проверки разрешений всегда возвращают true для суперпользователей, даже если разрешение ещё не определено!). Вместо этого создайте пользователя-библиотекаря и добавьте необходимые возможности.
Когда вы закончите, ваша страница должна выглядеть примерно, как на скриншоте ниже.
Подводим итоги
Отличная работа — теперь вы создали веб-сайт, на котором участники библиотеки могут входить в систему и просматривать собственный контент, и библиотекари (с правом доступа) могут просматривать все заёмные книги с их читателями. На данный момент мы все ещё просто просматриваем контент, но те же принципы и методы используются, когда вы хотите начать изменять и добавлять данные.
В следующей статье мы рассмотрим, как вы можете использовать формы Django для сбора пользовательского ввода, а затем начнём изменять некоторые из наших сохранённых данных.
Смотрите также
14.03.2012
Думаю, вы хотите, чтобы ваши сообщения об ошибках в заполняемых формах были на том же языке, что и сам сайт. Один из простых способов — это добавить следующий код в соответствующий forms.py. Затем формы надо будет наследовать не от forms.Form, а от MyForm (обратите внимание, ExampleForm, в примере ниже, наследуется от него).
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
for k, field in self.fields.items():
if 'required' in field.error_messages:
field.error_messages['required'] = u'Это поле обязательно!'
class ExampleForm(MyForm):
title = forms.CharField(max_length=100, required=True, label=u'Название')
Полный список error_messages для различных типов полей можно увидеть, если просмотреть этот раздел: https://docs.djangoproject.com/en/1.3/ref/forms/fields/#built-in-field-classes
Вот что есть на данный момент:
required — показывается, если данное поле обязательно;
max_length — если превышено максимальное количество символов в символьном поле / в случае с файлами — длина имени файла;
min_length — если символов меньше, чем должно быть, в символьном поле;
invalid_choice — если выбран невозможный choice;
invalid — при неправильном email’е и прочем неправильном вводе данных;
max_value — если превышено числовое значение;
min_value — если значение меньше минимального числового ограничения;
max_digits — если превышено количество цифр в числе;
max_decimal_places — если превышено количество цифр после запятой;
max_whole_digits — если превышено количество цифр до запятой;
missing — если файл не найден;
empty — если файл пустой;
invalid_image — если изображение повреждено;
invalid_list — если неправильный список choice’ов;
invalid_link — для URLField — вызывается, если данного url не существует.
Основано на примере с http://stackoverflow.com/questions/1481771/django-override-default-form-error-messages
django