学习Tango with django(9)

用户认证

这本书接下来的部分就是让你熟悉django的用户认证机制。我们将使用django标准安装包里的应用auth,位于django.contrib.auth。根据django官方文档,这个应用提供了一下概念和功能。

  • 用户和用户模型的概念。
  • 权限,一串二进制标志位(比如是/否)用来决定一个用户能不能做什么。
  • 组,运用权限的一种方法而不仅仅是使用用户。
  • 一个可配置的密码哈希系统,确保数据安全。
  • 提供用户登录或限制内容的表单和视图工具。

django可以做很多与用户认证的功能。这个章节,我们从基础开始讲解,使用简单的工具和概念帮助你建立信心。

配置认证

在你先开始学习django认证应用之前,要确保在django的项目settings.py文件里进行了相应的配置。
在settings.py文件里找到INSTALLED_APPS列表,检查django.contrib.auth和django.contrib.contenttypes是否在列表中,代码类似如下:

INSTALLED_APPS =[
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.statiefiles',
    'rango',
]

django.contrib.auth模块用来访问认证系统,而django.contrib.contenttypes用来跟踪安装到数据库中的模型。

如果有必要,要进行迁移
如果你在INSTALLED_APPS列表中增加了django.contrib.auth和django.contrib.contenttypes,那你需要运行命令python manage.py migrate来更新你的数据库,这样你的数据库就会增加了一些表,如为user模型添加了user表。
无论你是否增加了新的应用到你的django项目中,你都最好运行一下migrate命令,也许有的应用包含一些模块需要同步到数据库中去。

密码哈希

在任何环境下都不应该将密码作为明文存储到数据库中,如果有人恶意访问数据库获取到你应用所有的帐户信息,那他就可以为所欲为了。幸运的是,django的auth应用默认使用PBKDF2算法加密用户密码,为用户的数据提供了高等级的保护。但是如果你想控制密码的哈希算法,你可以修改settings.py文件,在PASSWORD_HASHERS元组中添加新的哈希算法,如下所示:

PASSWORD_HASHERS =(
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
)

元组中指定的算法顺序是很重要的,django会根据顺序选择第一个密码哈希算法(比如PASSWORD_HASHERS[0])。如果在元组里还指定了其它算法,django会在第一个算法无效的时候再使用它们。
如果你想使用更加安全的哈希算法,使用命令pip install bcrypt安装Bcrypt,然后设置PASSWORD_HASHERS如下:

PASSWORD_HASHERS =[
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHAlPasswordHasher',
]

如之前提到的,django默认使用PBKDF2算法加密密码,如果你在settings.py文件中未指定PASSWORD_HASHERS元组,django就会默认使用PBKDF2PasswordHasher加密算法。更多密码加密内容请查阅django官方文档。

密码验证

由于人们喜欢设置一些相对比较容易猜到的密码,django1.9中就引进了一个新的手欢迎的功能,密码验证。在django项目的settings.py文件中,有一个嵌套着字典的名叫AUTH_PASSWORD_VALIDATORS的列表。从这个嵌套的字典里,你可以清楚的看到django1.9提供了数个密码验证器,用来检查简单密码,如长度。每个验证器里可以指定一个OPTIONS字典,允许你进行一些简单的配置。比如说,如果你想确保可接受的密码最少要六位长,你可以在MinimumLengthValidator密码验证器里将min_length设置为6。例子如下所示:

AUTH_PASSWORD_VALIDATORS =[
    ...
    {
    'NAME': 'django.contrib.auth.password_va1idation.MinimumLengthValidator',
    'OPTIONS': { 'min_length': 6, }
    },
    ...
]

你也可以创建一个自己的密码验证器。这本书我们并没有涉及到创建自定义密码验证器,请查看官方文档获取更多信息。

User模型

User对象(位于django.contrib.auth.models.User)被认为是django认证系统的核心。一个User对象就表示一个与django应用交互的个体。django官方文档关于User对象这块描述了它们是用于认证系统方面比如访问限制,新用户注册,作者与作者内容之间的关联。
用户模型有以下五个关键属性:

  • 用户帐号的username;
  • 帐号的password;
  • 用户的email address;
  • 用户的first name;
  • 用户的surname。

用户模型还有其他属性,如is_activeis_staffis_super。它们都是布尔值类型,用来表示帐号是否激活,是否属于工作人员,是否有管理员权限。查看一下django官方文档了解一下User基类提供了哪些属性。

添加额外User属性

如果你想导入其它的与用户相关的属性而不仅仅是User模型提供的,那你需要创建一个模型与User模型关联。对于我们的rango应用,我们希望为每个用户帐户导入两个额外的属性。具体来说,我们希望导入:

  • 一个URLField,让rango用户能够指定他们自己的网页;
  • 一个ImageField,让用户为它们的头像指定一个图像。

这个可以通过在models.py文件里创建一个额外的模型来实现。让我们添加一个新的模型叫UserProfile:

class UserProfile(models.Model):
    #   This line is required. Links UserProfile to a User model instance. 
    user = models.OneToOneField(User)

    # The additional attributes we wish to include.
    website = models.URLField(blank=True)
    picture = models.ImageField(upload_to='profile_images', blank=True)

    # Override the __Unicode__() method to return out something meaningful!
    # Remember if you use Python 2.7.x, define __unicode__ too!
    def __str__(self):
        return self.user.username

注意我们使用一对一的关系类型引用了User模型。因为我们引用了默认的User模型,我们就得在models.py文件中导入它。

from django.contrib.auth.models import User

对于rango,我们添加了两个数据项来完善我们的用户模型,并提供了一个__str__()方法当请求一个UserProfile模型实例的字符串表达值时返回一个有意义的值。记住如果你使用python2的话,你还需要提供一个__unicode__()方法返回用户的用户名的字符串。
对于website和picture这两项,我们都为它们设置了blank=True。这样就允许它们可以为空,也就是说用户可以不用为这个属性提供值。
此外,我们还会注意到ImageField项有一个upload_to属性。这个属性的值与项目的MDEIA_ROOT设置值结合起来,提供了一个上传图像存储的路径。比如说,MEDIA_ROOT的值为 <workspace>/tango_with_django_project/media/,upload_to值为profile_images,两者结合起来形成所有图像的存储目录路径<workspace>/tango_with_django_project/media/profile_images/。回顾一下在模板和媒体文件章节我们是如何设置媒体根目录的。

通过继承来扩展User怎么样
我们也可以尝试通过直接继承User模型来添加额外的数据项。但是因为其它的应用也可能会访问User模型,因此我们不推荐使用继承,而是用数据库中的一对一关系模型。

使用PIL
django的ImageFile项用到了python图像库(PIL)。如果你还没安装PIL,那么使用命令pip install pillow。如果你不想启用jpeg支持功能,你可以使用如下命令:

pip install pillow --global-option="build_ext" --global-option="--disable-jpeg"

你可以用命令pip list检查你的环境中安装了哪些包。

要让UserProfile模型数据能通过管理界面访问,你需要将UserProfile模型导入到rango的admin.py文件中。

from rango.models import UserProfile

现在你可以通过如下代码将新模型注册到管理界面了:

admin.site.register(UserProfile)

再迁移一次
记住当你创建了新的模型后,数据库必须要同步更新。运行命令python manage.py makemigration rango创建一个迁移脚本。然后运行命令Python manage.py migrate执行迁移,在数据库中创建相关的表。

创建用户注册视图和模板

在设计好应用认证功能的结构后,我们现在需要开发模块,实现让应用的用户能够创建用户帐号。因此,我们需要创建一个新的视图、模板和URL映射来处理用户注册。

django用户注册应用
要着重注意下现在已经有几个现成的用户注册应用可以直接使用,它们可以减少大量的在创建自己的注册和登录表单时产生的麻烦。
但是在使用这些应用之前你最好搞清楚它们的内在运行机制。这样你就能理解它们底层是如何运行的。没有付出就没有收获。这样做还会让你加深对表单如何运行、User模型如何扩展以及媒体文件如何上传等方法的理解。

要实现用户注册功能,我们需要完成下面这几个步骤:

  • 创建UserForm和UserProfileForm;
  • 增加一个视图处理新用户的创建;
  • 创建模板显示UserForm和UserProfileForm;
  • 将创建的视图映射到一个URL上。

最后一步就是将我们的注册功能增加到原来的应用上去:

  • 将index主页链接到注册页面。

创建UserForm和UserProfileForm

在rango/forms.py中,我们需要创建两个继承forms.ModelForm的类。我们不但要为基类User创建一个类,还要为UserProfile类创建一个类。这两个继承ModelForm的类为我们展示了某些特殊模型展现数据项的HTML表单,为我们减少了很多工作量。
在rango/forms.py中,让我们先创建两个类并继承forms.ModelForm类,如下代码所示:

class UserForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput())

    class Meta:
        model = User
        fields = ('username', 'email、' password')

class UserProfileForm(forms.ModelForm): 
    class Meta:
        model = UserProfile
        fields = ('website', 'picture')

你应该注意到在这两个类中我们都增加了一个嵌套Meta类。正如这嵌套类所表达的名字一样,Meta类中任何项都可以用来描述它所在类的额外属性。每个Meta类都必须要提供一个model项,UserForm类这个例子中model对应的模型就是User模型。你还需要指定一个fields或者exclude属性来表示模型中相关的数据项应不应该展现在渲染过的表单里。
这里我们仅想展现User模型的username,email和password三项,UserProfile模型中website和picture两项。对于UserProfile模型中的user项,我们还需要在注册一个用户时为其做一个关联。因为当我们创建了一个UserProfile的实例时,我们还没有User实例可引用。
UserForm模型中还导入了password属性的定义。尽管User模型实例中默认包含了password属性,但是渲染后的HTML表单无法隐藏密码。用户输入密码时,密码是可见的。通过修改了password属性,定义了一个CharField实例并使用PasswordInput()控件来隐藏用户的密码输入避免被偷看。
最后,记住在forms.py模型中导入需要用到的类。为了方便我们也把它们列了出来。

from django import forms
from django.contrib.auth.models import User
from rango.models import Category, Page, UserProfile

创建register()视图

下一步,我们需要需然表单并处理表单输入数据。在rango的views.py文件中,为新类UserForm和UserProfileForm增加导入语句。

from rango.forms import UserForm, UserProfileForm

做完之后按照如下代码添加register()函数。

def register(request):
    # A boolean value for telling the template
    # whether the registration was successful.
    # Set to False initially. Code changes value to
    # True when registration succeeds. 
    registered = False

    # If it's a HTTP POST, we're interested in processing form data. 
    if request,method == 'POST':
        # Attempt to grab information from the raw form information.
        # Note that we make use of both UserForm and UserProfileForm.
        user_form = UserForm(data=request,POST)
        profile_form = UserProfileForm(data=request.POST)

        # If the two forms are valid...
        if user_form.is_valid() and profile_form.is_valid():
            #   Save the user's form data to the database. 
            user = user_form.save()

            #   Now we hash the password with the set_password method.
            #   Once hashed, ive can update the user object.
            user.set_password{user.password)
            user.save()

            #   Now sort out the UserProfile instance.
            #   Since we need to set the user attribute ourselves,
            #   we set commit=False. This delays saving the model
            #   until we're ready to avoid integrity problems.
            profile = profile_form.save(commit=False)
            profile.user = user

            # Did the user provide a profile picture?
            # If so,we need to get it from the input form and
            # put it in the UserProfile model. 
            if 'picture' in request.FILES:
                profile.picture = request.FILES['picture']

            #   Now we save the UserProfile model instance,
            profile.save()

            #   Update our variable to indicate that the template
            #   registration tvas successful. 
            registered = True
        else:
            #   Invalid form or forms - mistakes or something else?
            #   Print problems to the terminal.
            print(user_form.errors, profile_form.errors)
    else#   Not a HTTP POST, so we render our form using two Model Form instances.
    #   These forms will be blank, ready for user input. 
        user_form = UserForm()
        profile_form = UserProfileForm()

    # Render the template dep&nding on the context. 
    return render(request,
                'rango/register.html',
                {'user_form' : user_form,
                'profile_form': profile_form,
                'registered': registered})

尽管这个视图函数看起来很复杂,但是实际上和实现add category与add page视图函数差不多。但是在这里我们需要处理两个不同的ModelForm实例,一个是User模型,一个是UserProfile模型。我们还需要处理用户的图像信息,如果他们上传了话。
此外,我们需要在创建的两个模型实例之间建立一个联系。新的User模型实例创建之后,我们在UserProfile实例里使用profile.user = user来引用了它。我们在这里填充了UserProfileFomr表单的user属性,这个属性我们在users隐藏了。

创建注册模板

现在我们需要建立一个模板供register视图使用。创建一个新的模板文件rango/register.html,并添加如下代码:

{% extends 'rango/base.html' %}
{% load staticfiles %}

{% block title_block %}
    REGISTER
{% endblock %}

{% block body_block %}
    <h1>about page</h1>
    {% if registered %}
        Rango says: <strong>thank you for registering!</strong>
        <a href="{% url 'index' %}">return to homepage</a><br />
    {% else %}
        Rango says: <strong>register here!</strong>
        <form id="user_form" method="post" action="{% url 'register' %}" enctype="multipart/form-data">
            {% csrf_token %}
            {{ user_form.as_p }}
            {{ userprofile_form.as_p }}
            <input type="submit" name="submit" value = "Register" />
        </form>
    {% endif %}
{% endblock %}

使用url模板标签
注意我们在上面的模板里使用了url模板标签,如{% url 'register' %}。因此我们需要保证映射了URL并命名为register。

这里第一个要注意的就是这个模板使用了在视图中定义的registered变量,来表示注册是否成功。注意为了让模板能展示注册表单,registered必须要被设置为False,要不然就直接显示成功注册信息了。
接下来,我们使用了user_form和profile_form的模板函数as_p。这个函数会把表单中的每个元素封装成一段(用<p>显示)。这样就保证了每个元素显示在每一行。
最后,在<form>元素中,我们导入了属性enctype。这是因为如果用户想上传图片,表单的响应包会包含二进制数据,而且可能比较大。因此响应包有可能被拆分成多个传回到服务器。所以我们需要声明enctype="multipart/form-data"。要不然,服务器就不会全部接收用户上传的数据。

多部分消息和二进制文件
你应该注意到了<form>元素里的enctype属性。当你想让用户从表单中上传文件时,你一定要设置enctype为multipart/form-data。这个属性值表示你的浏览器使用一种特殊的方式将表单数据传回服务器。从根本上来说,组成文件的数据会被分割成一系列的块然后传送。想查看更多信息,请访问stack overflow网站。

此外,你要记住在表单中导入CSRF令牌,即{% csrf_token %}。如果你不这样做,django的跨站伪造保护中间件会拒绝接受表单内容并返回错误。

register()视图的URL映射

在创建好新的视图和相关的模板后,我们现在需要为其田间一个URL映射。在rango/urls.py里,修改urlparrerns元组,如下所示:

urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^about/$', views.about, name='about'),
    url(r'^register/$', views.register, name = 'register'),  # new pattern

    url(r'^restricted/$',views.restricted, name='restricted'),
    url(r'^add_category/$',views.add_category,name='add_category'),
    url(r'^add_page/(?P<category_name_slug>[\w\-]+)/$',views.add_page,name='add_page'),
    url(r'^category/(?P<category_name_slug>[\w\-]+)/$',views.show_category,name='show_category'),
]

新增加的模式(注释了)将URL/rango/register指向了register()视图。同时还要注意我们新URL模式里包含了name属性,赋值为register,通过模板里的url标签使用,如{% url 'register' %}

将所有链接放在一起

最后,我们修改一下base.html文件,添加一行链接用来指向注册的URL。这样每个页面的无序列表里都会产生一个链接指向注册页面。

<ul>
                <li><a href="{% url 'add_category' %}">Add New Category</a> </li>
                <li><a href="{% url 'about' %}">About</a></li>
                <li><a href="{% url 'index' %}">Index</a></li>
                <li><a href="{% url 'register' %}">sign up</a></li>
            </ul>

示例

现在所有工作都已完成,运行开发服务器尝试注册一个新用户,你也可以上传一个图像。你的注册表单应该如下面的截图所示:
这里写图片描述

一个截图,演示了你根据这本书学习创建的注册表单

填写信息点击注册返回成功信息后表示你注册成功了。数据库中应该有了一条新的User和UserProfile数据。你可以在django管理界面上查看是否成功创建。

实现登录功能

现在注册用户的功能已经完成,我们现在开始为用户提供登录功能。要实现这个功能,我们就需要完成如下的工作:

  • 创建一个登录视图处理用户认证过程
  • 创建登录模板展现登录表单
  • 将登录视图映射到URL
  • 将登录链接插入到index页面

创建login()视图

首先打开rango/views.py,创建新视图user_login()。这个视图将会处理之后的登录表单数据,然后尝试通过上传的用户数据登录。

def user_login(request):
    if request.method == 'POST':
        username = request.POST.get('username','')
        password = request.POST.get('password','')

        user = authenticate(username=username, password=password)
        if user:
            if user.is_active:
                login(request,user)
                return HttpResponseRedirect(reverse('index'))
            else:
                return HttpResponse('Your account is not disable!')
        else:
            print('invalid login details:{0},{1}'.format(username,password))
            return HttpResponse('invalid login details supplied')
    else:
        return render(request,'rango/login.html')

这个视图看起来相当复杂,因为它处理了好几个场景。跟之前的例子一样,user_login()视图处理表单渲染和数据处理,上传的数据此时应该包含username和password两项。
首先,如果视图是通过HTTP的GET方式访问的,那么登录表单就会显示。而如果是通过HTTP的POST方式提交表单的,那么我们就需要处理这个表单了。
如果通过POST发送的表单数据是正确的,就从表单里抽取用户名和密码。然后用这些信息进行用户认证。django函数authenticate()会检查提供的用户名和密码是否匹配一个合法的用户帐号。如果合法用户存在,那么就返回一个User对象,要不然就返回None。
当我们获取到User对象后,我们就来检查这个帐号是否被激活,如果激活了,我们就运行django函数login(),这个函数是django官方指定的用来登录用户的。
但是,如果发送的表单是不正确的,比如说这个用户可能没有用户名或密码,那么登录表单就返显用户登录的错误信息(提供了非法的用户名密码)。
你应该注意到我们使用了一个新类,HttpResponseRedirct。顾名思义,通过HttpResponseRedirct类的实例生成的响应包告诉客户端浏览器重定向到你参数指定的URL。注意这里会返回一个HTTP状态码302,表示是一个重定向,与状态码200(成功)不一样。查看django官方文档查看更多信息。
最后,我们使用了django的另外一个方法reverse,获取rango应用的URL。这个函数会在rango的urls.py文件中搜索URL模式去找一个叫index的URL,然后用相应的模式替代。这表示如果我们以后修改了URL映射,视图代码是不用动的。
django提供了以上所有的函数和类,你要做的就是导入它们。下面的导入语句必须要添加到rango/views.py文件的上方。

from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth import authenticate, login, logout
from django.core.urlresolvers import reverse

创建一个登录模板

新的视图创建后,我们需要创建一个新的模板来让用户登录。尽管我们知道模板都是放在templates/rango目录里的,但是这次我们把命名模板文件名称的问题留给你。看一下上面的代码例子,根据新的视图函数user_login()来获取模板的名称。在新的模板文件,添加如下代码:

{% extends 'rango/base.html' %}
{% load staticfiles %}

{% block title_block %}
login
{% endblock %}

{% block body_block %}
    <h1>login to rango</h1>
    <form id="login_form" method="post" action="{% url 'login' %}">
        {% csrf_token %}
        Username: <input type="text" name="username" value="" size="50" />
        <br />
        Password: <input type="password" name="password" value="" size="50" />
        <br />
        <input type="submit" value="submit" />
    </form>
{% endblock %}

确保input元素里的name属性要和user_login()视图里指定的匹配。比如说,input元素里定义了name属性为username和password,那么在视图函数里用request.POST获取字典数据时,键值也要用username和password。还有别忘了{% csrf_token %}

将登录视图映射到URL

登录模板创建好后,我们就可以给user_login()视图映射一个URL了。修改rango的urls.py文件让urlpatterns列表包含下面的映射:

url(r'^login/$',views.user_login, name = 'login'),

把链接放在一起

最后一步就是为rango用户提供一个方便的链接去访问登录页面。因此我们需要编辑templates/rango/目录里的base.html模板,添加如下代码到无序列表里。

<ul>
...
<li><a href="{% url 'login' %}">login</a></li>
</ul>

你也可以修改主页的头信息,为登录的用户展现个性化的信息,在未登录状态展示通用信息。在index.html模板里找到如下的信息。

hey there partner!

这一行可以替换成如下的代码。

{% if user.is_authenticated %}
    hello {{ user.username }}!
{% else %}
    hey there partner!
{% endif %}

正如你所见,我们使用了 django的模板语言{% if user.is_authenticated %}检查了用户是否登录。如果用户登录了,django就能让我们访问user对象。我们可以通过这个对象辨别用户是否登录。如果他登录了,我们就可以获取他的详细信息。在上面的例子中,如果用户登录了,他的用户名就会显示出来,否则就显示通用词语信息hey there partner!

示例

启动django开发服务器,尝试登录应用,下面的截图展示了登录和index页面。
这里写图片描述

一个截图,未登录时显示默认信息,登录显示用户名

上面都完成后,用户登录功能就实现了。要测试这些功能,尝试打开django开发服务器并注册一个新用户。成功注册后,你应该能够用你刚才注册的信息登录成功。

限制访问

既然用户已经可以登录rango了,现在我们要根据需求对应用的特殊部分进行限制访问,也就是说只有注册用户才能增加分类和页面。在django中有几种方法可以实现这个目标。

  • 在模板中,我们可以用{% if user.is_authenticated %}模板标签来决定页面如何渲染(上面的模板代码中已实现)。
  • 在视图函数中,我们可以直接检查request对象中的user是否已认证。
  • 或者,我们可以使用一个装饰器函数@login_required来检查用户是否认证。

最直接的方法检查用户是否登录,就是通过user.is_authenticated()方法。传递给视图函数的request对象包括user对象。下面的例子演示了这个方法:

def some_view(request):
    if not request.user.is_authenticated():
        return HttpResponse("You are logged in.")
    else:
        return HttpResponse("You are not logged in.")

第三个方法就是使用python装饰器。装饰器与一个软件设计模式的名称一样。它们可以动态改变函数、方法或者类的功能而不需要直接编辑函数、方法或类的源代码。
django提供了一个装饰器叫login_required(),我们可以把它放在任何需要用户登录才能访问的视图函数上。如果用户未登录并且尝试带有login_required()装饰器的视图,他们就会被重定向到另外一个页面(你可以设置),通常是登录页面。

使用装饰器限制访问

首先让我们在views.py视图里创建一个叫restricted()的视图函数,并添加如下代码:

@login_required
def restricted(request):
    return HttpResponse("Since you're logged in, you can see this text!")

注意要使用装饰器,就把它直接放在函数声明的上方并在装饰器名称前加一个@。python将会先执行这个装饰器后,再执行函数/方法的代码。因为装饰器也是一个函数,如果这个装饰器在第三方包中定义的,你需要先导入它。由于login_required()实在其它地方定义的,你需要在views.py的上方进行导入。

from django.contrib.auth.decorators import login_required

我们还需要在urls.py文件中的urlpatterns列表里添加另外一个模式。添加如下代码:

url(r'^restricted/$',views.restricted, name='restricted'),

然后我们还需要处理用户尝试访问restricted视图但未登录的场景。对于这样的用户我们该怎么做?最简单的方法就是将他们重定向到一个能访问的页面上,比如注册页面。django允许我们在项目的settings.py设置一个重定向页面。在settings.py里,定义一个变量LOGIN_URL并赋值一个你希望未登录用户访问时重定向的页面URL,比如可以定义为/rango/login/:

LOGIN_URL = '/raongo/login/'

这样就能保证login_required()装饰器让未登录的用户访问重定向到/rango/login/。

用户退出

要让用户能简单的退出,最好提供一个退出选项。django有一个方便的函数logout(),能够确保用户可以正确并且安全的退出。logout()函数确保会话终止,之后再想访问一个需要认证的视图时就不成功了,除非再次登录。
要在提供用户退出功能,我们需要在rango/views.py里定义一个user_logout()视图并添加如下代码:

@login_required
def user_logout(request):
    logout(request)
    return HttpResponseRedirect(reverse('index'))

当然你需要将logout函数在views.py的上方导入进来。

from django.contrib.auth import logout

视图创建好后,我们就需要在urls.py里修改urlpatterns列表,将/rango/logout/映射到user_logout()视图里。

url(r'^logout/$',views.user_logout, name='logout')

现在用户退出功能已经完成了,接下里我们进行一些收尾工作。在主页上添加一个链接让用户简单的点击链接就能退出,这对用户来说很方便。但是让我们再深入思考一下:需要为未登录的用户提供一个退出的链接吗?或许不需要,为一个未登录的用户提供注册的链接或许更有帮助。
就像上一节一样,我们要修改index.html模板,通过模板上下文字典中的user对象来决定展示什么链接。找到页面最下方不断增长的链接,然后替换成如下代码。注意我们需要添加一个/rango/restriected/的限制访问页面。

<ul>
                <li><a href="{% url 'add_category' %}">Add New Category</a> </li>
                <li><a href="{% url 'about' %}">About</a></li>
                <li><a href="{% url 'index' %}">Index</a></li>
                {% if user.is_authenticated %}
                <li><a href="{% url 'restricted' %}">Restriected Page</a></li>
                <li><a href="{% url 'logout' %}">Logout</a></li>
                {% else %}
                <li><a href="{% url 'register' %}">sign up</a></li>
                <li><a href="{% url 'login' %}">login</a></li>
                {% endif %}
            </ul>

这段代码表示当一个用户进行认证并登录后,他可以看到一个限制访问和退出链接。如果未登录,就显示注册和登录链接。About和Add a New Category 这两个链接并不在模板中的条件块中,因为它们对匿名用户和登录用户都可见。

进一步充实

在这一章中,我们讲解了django中几个重要的用户管理认证的模块。我们还讲解了安装django中django.contrib.auth应用到我们项目的基础。除此之外,我们还演示了在django.contrib.models.User模型的基础上如何实现一个添加额外项的用户模型。同时还详细描述了如何实现用户注册、登录、退出和控制访问等功能。要获取更多关于用户认证和注册的信息请咨询django官方文档。
但是许多web应用还会进一步加强用户认证功能。比如说,注册用户时,你需要定义不同的安全等级,还要确保提供一个合法的email地址。虽然我们可以实现这个功能,但是当这个功能已经存在的时候,我们为什么还要重复发明轮子呢?django-registeration-redux应用已经开发了相当简单的功能用来实现与用户认证相关的其它有用的功能。在下一章节,我们将会讲解如何使用这个包。

练习
完成下面的练习来巩固下你在这一章中所学习的内容。

  • 修改应用,实现只有注册用户才能添加或编辑分类和页面,未注册用户只能访问分类和页面。你还需要确保只用用户登录之后页面才能显示增加或编辑分类页面的链接。
  • 当用户输入了不正确的用户名或密码,返显一个有效的信息。
  • 让你的模板不断的更新,将限制访问的页面视图转换为用一个模板实现。将这个模板命名为restricted.html,并确保它也是从base.html模板继承的。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值