首页 > 代码库 > 第 19 章 用户帐号

第 19 章 用户帐号

    Web应用程序的核心是让任何用户都能够注册并能够使用它,不管用户身处何方。在本章中,我们将创建一些表单,让用户能够添加主题和条目,以及编辑既有的条目。我们还将学习Django如何防范对基于表单的网页发起的常见攻击,这让我们无需花太多时间考虑确保应用程序安全的问题。

    然后,我们将实现一个用户身份验证系统。我们将创建一个注册页面,供用户创建账户,并让有些页面只能供已登录的用户访问。接下来,我们将修改一些视图函数,使得用户只能看到自己的数据。我们将学习如何确保用户数据的安全。

19.1  让用户能够输入数据

    建立用于创建用户账户的身份验证系统之前,我们先来添加几个页面,让用户能够输入数据。我们将让用户能够添加新主题、添加新条目以及编辑既有条目。

    当前,只有超级用户能够通过管理网站输入数据。我们不想让用户与管理网站交互,因此我们将使用Django的表单创建工具来创建让用户能够输入的页面。

19.1.1  添加新主题    首先来让用户能够添加新主题。创建基于表单的页面的方法几乎与前面创建网页一样:定义一个URL,编写一个视图函数并编写一个模板。一个主要差别是,需要导入包含表单的模块forms.py.

    1.用于添加主题的表单

    让用户输入并提交信息的页面都是表单,哪怕它看起来不像表单。用户输入信息时,我们需要进行验证,确认提供的信息是正确的数据类型,且不是恶意的信息,如中断服务器的代码。然后,我们再对这些有效信息进行处理,并将其保存到数据库的合适地方。这些工作很多都是Django自动完成的。

    在Django中,创建表单的最简单方式是使用ModelForm,它根据我们在第18章定义的模型中的信息自动创建表单创建一个名为forms.py的文件,将其存储到models.py所在的目录中,并在其中编写我们的第一个表单。

  forms.py

    from django import forms

  from .models import Topic

  class TopicForm(forms.ModelForm):                   --(1)

        class Meta:

      model = Topic                               --(2)

      fields = ["text"]                           --(3)

      labels = {"text":""}                        --(4)

    我们首先导入了模块forms以及要使用的模型Topic。在(1)处,我们定义了一个名为TopicForm的类,它继承了forms.ModelForm。

    最简单的ModelForm版本只包含一个内嵌的Meta类,它告诉Django根据哪个模型创建表单,以及在表单中包含哪些字段。在(2)处,我们根据模型Topic创建一个表单,该表单只包含字段text(见(3))。(4)处的代码让Django不要为字段text生成标签。

    2.URL模式new_topic

    这个新网页的URL应简短而具有描述行,因此当用户要添加新主题时,我们将切换到http://localhost:8000/new_topic/。下面是网页new_topic的URL模式,我们将其添加到learning_logs/urls.py中:

    urls.py

‘‘‘定义learning_logs的URL模式‘‘‘
from django.conf.urls import url
from . import views

urlpatterns = [
    #主页
    url(r‘^$‘,views.index,name=‘index‘),
        #显示所有的主题
    url(r‘^topics/$‘, views.topics,name=‘topics‘),
        #特定主题的详细页面
    url(r‘^topics/(?P<topic_id>\d+)/$‘,views.topic,name=‘topic‘),
    #用于添加新主题的网页
    url(r‘^new_topic/$‘,views.new_topic,name=‘new_topic‘),
]
    这个URL模式将请求交给视图函数new_topic(),接下来我们将编写这个函数。

    3.视图函数new_topic()

    函数new_topic()需要处理两种情形:刚进入new_topic网页(在这种情况下,它应显示一个空表单);对提交的表单数据进行处理,并将用户重定向到网页topics:

    views.py

from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from .models import Topic
from .forms import TopicForm

# Create your views here.
def index(request):
    """学习笔记的主页"""
    return render(request,‘learning_logs/index.html‘)

def topics(request):
    """显示所有的主题"""
    topics = Topic.objects.order_by("date_added")
    context = {‘topics‘:topics}
    return render(request,‘learning_logs/topics.html‘,context)
def topic(request,topic_id):
    """显示单个主题及其所有的条目"""
    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by(‘-date_added‘)
    context = {‘topic‘:topic,‘entries‘:entries}
    return render(request, ‘learning_logs/topic.html‘,context)

def new_topic(request):
    """添加新主题"""
    if request.method != "POST:                      (1)
        #未提交数据:创建一个新表单
    form = TopicForm()                                (2)
    else:
    """POST提交的数据,对数据进行处理"""
    form = TopicForm(request.POST)                     (3)
    if form.is_valid():                                (4)
        form.save()                                    (5)
            return HttpResponseRedirect(reverse(‘learning_logs:topics‘))            (6)

context = {‘form‘:form}                                                (7)
return render(request,"learning_logs/new_topic.html‘,context)

    我们导入了HttpResponseRedirect类,用户提交主题后我们将使用这个类将用户重定向到网页topics。函数reverse()根据指定的URL模型确定URL,这意味着Django将在页面被请求时生成URL.我们还导入了刚才创建的表单TopicForm。

    4.GET请求和POST请求

    创建Web应用程序时,将用到的两种主要请求类型是GET请求和POST请求。对于只是从服务器读取数据的页面,使用GET请求;在用户需要通过表单提交信息时,通常使用POST请求。处理所有表单时,我们都将指定使用POST方法。还有一些其他类型的请求,但这个项目没有使用。

    函数new_topic()将请求对象作为参数。用户初次请求该网页时,其浏览器将发送GET请求;用户填写并提交表单时,其浏览器将发送POST请求。根据请求的类型,我们可以确定用户请求的是空表单(GET请求)还是要求对填写好的表单进行处理(POST请求).

    (1)处的测试确定请求方法是GET还是POST。如果请求方法不是POST,请求就可能是GET,因此我们需要返回一个空表单(即便请求是其他类型的,返回一个空表单也不会有任何问题)。我们创建一个TopicForm实例(见2),将其存储在变量form中,再通过上下文字典将这个表单发送给模板(见(7))。由于实例化TopicForm时我们没有指定任何实参,Django将创建一个可供用户填写的空表单。

    如果请求方法是POST,将执行else代码块,对提交的表单进行数据处理。我们使用用户输入的数据(它们存储在request.POST中)创建一个TopicForm实例(见(3)),这样对象form将包含用户提交的信息。

    要将提交的信息保存到数据库,必须先通过检查确定它们是有效的(见(4))。函数is_valid()核实用户填写了所有必不可少的字段(表单字段默认都是必不可少的),且输入的数据与要求的字段类型一致(例如,字段text少于200个字符,这是我们在第18章中的models.py中指定的)。这种自动验证避免了我们去做大量的工作。如果所有字段都有效,我们就可调用save()(见(5)),将表单中的数据写入数据库。保存数据后,就可离开这个页面了。我们使用reverse()获取页面topics的URL,并将其传递给HttpResponseRedirect()(见(6)),后者将用户的浏览器重定向到页面topics。在页面topics中,用户将在主题列表中看到他刚输入的主题。

    5.模板new_topic

    下面来创建新模板new_topic.html,用于显示我们刚创建的表单:

    new_topic.html

    {% extends "learning_logs/base.html" %}

    {% block content %}

      <p>Add a new topic:</p>

      <form action="{% url ‘learning_logs:new_topic‘ %}" method="post">       --(1)

        {% csrf_token %}                                                   --(2)

    {{ form.as_p }}                                                       --(3)

    <button name="submit">add topic</button>                               --(4)

    </form>

    {% endblock content %}

    这个模板继承了base.html,因此其基本结构与项目"学习笔记"的其他页面相同。在(1)处,我们定义了一个HTML表单。实参action告诉服务器将提交的表单数据发送到哪里,这里我们将它发回给视图函数new_topic()。实参methon让浏览器以POST请求的方式提交数据。

    Django使用模板标签{% csrf_token %}(见2)来防止攻击者利用表单来获得对服务器未经授权的访问(这种攻击被称为跨站请求伪造).在(3)处,我们显示表单,从中可知Django使得完成显示表单等任务有多简单:我们只需包含模板变量{{ form.as_p }},就可让Django自动创建显示表单所需的全部字段。

修饰符as_p让Django以段落格式渲染所有表单元素,这是一种整洁地显示表单的简单方式。

    Django不会为并表单创建提交按钮,因此我们在(4)处定义了一个这样的按钮。

    6.链接到页面new_topic

    接下来,我们在页面topics中添加一个页面new_topic的链接:

    topics.html

 

 

   

   

   

   

 

技术分享

第 19 章 用户帐号