第 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

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”:
        #免提交数据:创建一个初表单
    form = TopicForm()
    else:
    “””POST提交的数据,对数据开展处理”””
    form = TopicForm(request.POST)
    if form.is_valid():
        form.save()
            return
HttpResponseRedirect(reverse(‘learning_logs:topics’))

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

19.1.2 
添加新条令

   
现在用户可互补加新主题了,但它还眷恋补充加新条令。我们拿另行定义URL,编写视图函数和模板,并链接到添加新条令的网页。但在此之前,我们需要在forms.py中更补充加一个近乎。

   
1.用于补充加新条令的表单

   
我们要创造一个和范Entry相关联的表单,但此表单的定制程度较TopicForm要高些:

forms.py

from django import
forms
from .models import Topic,Entry

class TopicForm(forms.ModelForm):
    class Meta:
    model = Topic
    fields = [“text”]
    labels = {“text”: “”}

 class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
    fields = [“text”]
    labels =
{“text”:””}                                                        
–(1)
    widgets =
{‘text’:forms.Textarea(attrs={‘cols’:80})}                        
–(2)
   
我们第一修改了import语句,使该除去导入Topic外,还导入Entry。新类EntryForm继承了forms.ModelForm,它富含的Meta类指出了表单基于的型和要当表单中涵盖如何字段。这里呢被字段’text’指定了一个空标签。

   
在(2)处,我们定义了属性widgets小部件(widget)凡一个HTML表单元素,如单行文本框、多行文本区域或下拉列表。通过安装属性widgets,可覆盖Django选择的默认小部件。通过叫Django使用forms.Textarea,我们定制了字段”text”的输入的有点部件,将文件区域的宽窄设置为80排,而未是默认的40排列。这为用户提供了足足的上空,可以编写有意义之条目。

   
2.URL模式
new_entry

   
在用于补充加新条令的页面的URL模式被,需要包含的参topic_id,因为条目必须与一定的主题相关联。该URL模式如下,我们以她上加至了learning_logs/urls.py中:

ruls.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(r’^new_entry/(?P<topic_id\d+/$’,views.new_entry,name=’new_entry’),

]

   
这个URL模式和形式吗heet://localhost:8000/new_entry/id/的URL匹配,其中id是一个与主题ID匹配的数字。代码(?P<topic_id>\d+)抓获一个数字值,并拿其储存在变量topic_id中。请求的URL与此模式匹配时,Django将请求与主题ID发送给函数new_entry().

   
3.视图函数new_entry()

   
视图函数new_entry()与函数new_topic()很像:

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,EntryForm

# 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”:
        #免提交数据:创建一个初表单
    form = TopicForm()
    else:
    “””POST提交的数量,对数据开展处理”””
    form = TopicForm(request.POST)
    if form.is_valid():
        form.save()
            return
HttpResponseRedirect(reverse(‘learning_logs:topics’))

def new_entry(request,topic_id):
    “””在特定的主题中上加新条目”””
    topic =
Topic.objects.get(id=topic_id)                                                    
(1)
    if request.method !=
“POST”:                                                              
(2)
    #未提交数据,创建一个空表单
    form =
EntryForm()                                                                         
(3)
    else:
    #POST提交的多寡,对数码进行拍卖
    form =
EntryForm(data=request.POST)                                                        
(4)
        if form.is_valid():
        new_entry =
form.save(commit=False)                                                    
(5)
        new_entry.topic =
topic                                                                 
(6)
        new_entry.save()
        return
HttpResponseRedirect(reverse(‘learning_logs:topic’,args=[topic_id]))            
(7)

context = {‘topic’:topic,’form’:form}
return render(request,”learning_logs/new_entry.html’,context)

   
我们修改了import语句,在中蕴含了刚创立的EntryForm。new_entry()的定义包含形参topic_id,用于存储于URL中得到的价。渲染页面和处理表单数据时,都亟需知道对的常谁主题,因此我们下topic_id来得到对的主题。

   
在(2)处,我们检查请求方法是POST还是GET.如果是GET请求,将实行if代码块:创建一个拖欠的EntryForm实例(见(3))。如果要的法门吗POST,我们尽管本着数据开展处理:创建一个EntryForm实例,使用request对象被的POST数据来填充它(见(4));在检讨是不是行得通,如果可行,就设置条目对象的特性topic,再将条目对象保存到数据库。

   
调用save()时,我们传递了实参commit=False(见(5)),让Django创建一个初的条目对象,并拿那储存到new_entry中,但未将它们保存至数据库被。我们以new_entry的习性topic设置也这函数开头从数据库被拿走的主题,然后调用save(),且非指定其他实参。这将将条目保存至数据库,并将该同是的主题相关联。

   
在(7)处,我们以用户重定向到展示相关主题的页面。调用reverse()时,需要提供个别独实参:要基于它们来生成URL的URL模式之名目;列表args,其中包含要含有在URL中之兼具实参。这里,列表args只来一个元素——topic_id。接下来,调用HttpResponseRedirect()将用户重定向到展示新增条款所属主题的页面,用户以以拖欠页面的条文列表中看看新添加之条文。

   
4.模板new_entry

   
从脚的代码可知,模板new_entry类似于模板new_topic:

new_entry.html

{% extends
‘learning_logs/base.html’ %}
{% block content %}

  <p><a href=”{% url ‘learning_logs:topic’ topic_id %}”>{{
topic }}</a></p>              –(1)
  <p>Add a new entry:</p>
  <form action=”{% url ‘learning_logs:new_entry’ topic.id %}
method=’post’>              —(2)
    {% csrf_token %}
    {{ form.as_p }}
    <buttom name=”submit”>add entry</button>
  </form>

{% endblock content %}

   
我们于页面顶端显示了主题(见(1)),让用户知道他是以哪个主题中补充加条目;该主题名也是一个链接,可用以返回到该主题的主页面。

   
表单的实参action包含URL中的topic_id值,让视图函数能够用新条令关联到正确的主题(见(2))。除此之外,这个模板与模板new_topic.html完全相同。

   
5.链接到页面
new_entry

   
接下去,我们需要在展示特定主题的页面中上加到页面new_entry的链接:

    topic.html

{% extends
“learning_logs/base.html” %}

{% block content %}

  <p>Topic:{{ topic }}</p>
  <p>Entries:</p>
  <p>
     <a href=”{% url ‘learning_logs:new_entry’ topic.id %}”>add
new entry</a>
  </p>

  <ul>
    {% for entry in entries %}
      <li>
        <p>{{ entry.date_added|date:’M d,Y H:i’}}</p>
        <p>{{ entry.text|linebreaks }}</p>
      </li>
    {% empty %}
      <li>
        There are no entries for this topic yet.
      </li>
    {% endfor %}
  </ul>

{% endblock content %}

   
我们在展示条目前补加链接,因为于这种页面中,执行的不过常见的操作是补偿加新条令。

19.1.3 
编辑条目

   
下面来创造一个页面,让用户能够编辑既有的条条框框。

   
1.URL模式edit_entry

   
这个页面的URL需要传递要编制的条目的ID.修改后的learning_logs/ruls.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(r’^new_entry/(?P<topic_id\d+/$’,views.new_entry,name=’new_entry’),
        #用来编辑条目的页面
  
 url(r’^edit_entry/(?P<entry_id>\d+)/$’,views.edit_entry,name=’edit_entry’),

]

   
在URL(如http://localhost:8000/edit\_entry/1/)中传递的ID存储在形参entry\_id中。这个URL模式将预期匹配的请求发送给视图函数edit\_entry().

   
2.视图函数edit_entry()

   
页面edit_entry收到GET请求时,edit_entry()将回来一个表单,让用户会针对条款进行编辑。该页面收取POST的求(条目文本通过修订)时,它将修改后底公文保存到数据库被:

   
“””显示单个主题及其具有的条款”””
    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”:
        #切莫提交数据:创建一个初表单
    form = TopicForm()
    else:
    “””POST提交的多寡,对数据开展处理”””
    form = TopicForm(request.POST)
    if form.is_valid():
        form.save()
            return
HttpResponseRedirect(reverse(‘learning_logs:topics’))

def new_entry(request,topic_id):
    “””在一定的主题中上加新条目”””
    topic = Topic.objects.get(id=topic_id)
    if request.method != “POST”:
    #莫提交数据,创建一个空表单
    form = EntryForm()
    else:
    #POST提交的数额,对数码开展处理
    form = EntryForm(data=request.POST)
        if form.is_valid():
        new_entry = form.save(commit=False)
        new_entry.topic = topic
        new_entry.save()
        return
HttpResponseRedirect(reverse(‘learning_logs:topic’,args=[topic_id]))

def edit_entry(request,entry_id):
    #修既出条款
    entry = Entry.objects.get(id=entry.id)                         
(1)

    topic = entry.topic
    if request.method != “POST”:
        #第一请求,使用时章填充表单
    form = EntryForm(instance=entry)                               
(2)

    else:
    #POST提交的数量,对数码进行拍卖
    form = EntryForm(instance=entry,data=request.POST)             
(3)

    if form.is_valid():
        form.save()                                                
(4)

        return
HttpResponseRedirect(reverse(‘learning_logs:topic’,args=[topic.id]))  
(5)

context = {‘entry’:entry,’topic’:topic,’form’:form}
return render(request,”learning_logs/edit_entry.html’,context)

   
我们率先要导入模型Entry。在(1)处,我们得用户如修改的条规对象,以及跟该条款相关联的主题。在呼吁方法吧GET时拿履行的if代码块被,我们运用实参instance=entry创建一个EntryForm实例。这个实参让Django创建一个表单,并下既出条文对象吃的音信填写它。用户用张既有的数目,并能够编辑它们。

   
处理POST请求时,我们传递实参instance=entry和data=request.POST(见(3)),让Django根据既来章对象创建一个表单实例,并冲request.POST中之相关数据对那个进行修改。然后,我们检查表单是否有效,如果中,就调用save(),且未指定其他实参(见(4))。接下来,我们重定向到展示条目所属主题的页面,用户将当其中顾其编写的条目的初本子。

   
3.模板edit_entry

   
下面是模板edit_entry.html,它同模板new_entry.html类似:

   
edit_entry.html

{% entends
“learning_logs/base.html” %}
{% block content %}
  <p><a href=”{% url ‘learning_logs:topic’ topic.id %}”>{{
topic }}</a></p>
  <p>Edit entry:</p>
  <form action=”{% url ‘learning_logs:edit_entry’ entry.id %}”
method=’post’>
    {% csrf_token %}
    {{ form.as_p }}
    <button name=”submit”>save changes</button>
  </form>

{% endblock content %}
    在(1)处,实参action将表单发回给函数edit_entry()进行拍卖。在标签{%
url
%}中,我们将条目ID作为一个实参,让视图对象会修改是的条条框框对象。我们拿提交按钮命名吧save
changes,以提示用户:单击该按钮将保留所举行的编辑,而不是开创一个初条令。

   
4.链接到页面
edit_entry

   
现在,在亮特定主题的页面被,需要被每个条目添加到页面edit_entry的链接:

topic.html

{% extends
“learning_logs/base.html” %}

{% block content %}

  <p>Topic:{{ topic }}</p>
  <p>Entries:</p>
  <p>
     <a href=”{% url ‘learning_logs:new_entry’ topic.id %}”>add
new entry</a>
  </p>

  <ul>
    {% for entry in entries %}
      <li>
        <p>{{ entry.date_added|date:’M d,Y H:i’}}</p>
        <p>{{ entry.text|linebreaks }}</p>
    <p>
      <a href=”{% url ‘learning_logs:edit_entry’ entry.id
%}”>edit entry</a>

    </p>
      </li>
    {% empty %}
      <li>
        There are no entries for this topic yet.
      </li>
    {% endfor %}
  </ul>

{% endblock content %}

   
我们将编辑链接放在每个条目的日期和文件后面。在循环中,我们用模板标签{%
url
%}根据URL模式edit_entry和脚下章的ID属性(entry.id)来确定URL.链接文本为”edit
entry”,它起在页面被每个条目的尾。

   
至此,“学习笔记”已备了特需之大部效果。用户可续加主题与条款,还而因需要查阅其他一样组条目。在生同样节,我们将促成一个用户注册系统,让任何人都可通往“学习笔记”申请账户,并创立和谐之主题与条款。

19.2 
创建用户账户

   
在马上同一节,我们将成立一个用户注册与身份验证系统,让用户能够报账户,进而登录以及撤回。我们用创设一个新的应用程序,其中包含与处理用户账户相关的有机能。我们还以针对范Topic稍作改,让每个主题都归为特定用户。

19.2.1  应用程序users

   
我们首先以命令startapp来创造一个称也users的应用程序:

(11_env)learning_log$
python3 manage.py startapp users
(11_env)learning_log$ ls
11_env  db.sqlite3  learning_log  learning_logs  manage.py 
users

(11_env)learning_log$ ls users
admin.py  apps.py  __init__.py  migrations  models.py  tests.py 
views.py

   
这个令新建一个称为也users的目录(见(1)),其组织与应用程序learning_logs相同。

   
1.用应用程序users添加到settings.py中

   
在settings.py中,我们得以此新的应用程序添加到INSTALLED_APPS中,如下所示:

    settings.py

–snip–

INSTALLED_APPS = [
    ‘django.contrib.admin’,
    ‘django.contrib.auth’,
    ‘django.contrib.contenttypes’,
    ‘django.contrib.sessions’,
    ‘django.contrib.messages’,
    ‘django.contrib.staticfiles’,
    #自身的应用程序
    “learning_logs”,
    ‘users’,
]

   
这样,Django将把应用程序users包含到路受到。

   
2.带有应用程序users的URL

   
接下去,我们用改项目根本目录中之urls.py,使其富含我们以为应用程序users定义之URL:

urls.py

from django.conf.urls
import include, url
from django.contrib import admin

urlpatterns = [
    url(r’^admin/’, include(admin.site.urls)),
    url(r’users/’,include(‘users.urls’,namespace=’users’)),
    url(r”,
include(‘learning_logs.urls’,namespace=’learning_logs’)),
]

   
我们补充加了一条龙代码,以富含应用程序users中的文件urls.py。这行代码和外以单词users打头的URL(如http://localhost:8000/users/login/)都匹配。我们还创建了命名空间'users',以便将应用程序learning\_logs的URL同应用程序users的URL区分开来。

19.2.2 
登录页面

   
我们首先来促成登录页面的成效。为这,我们用采用Django提供的默认登录视图,因此URL模式会微微有异。在目录learning_log/users/中,新建一个称作吧urls.py的公文,并以里面添加如下代码:

urls.py

“””为应用程序users定义URL模式”””
from django.conf.urls import url
from django.contrib.auth.views import login                  (1)

from . import views

urlpatterns = [
    #登录页面
   
url(r’^login/$’,login,{‘template_name’:’users/login.html’},name=’login’),       
(2)
]

   
我们率先导入了默认视图login(见(1))。登录页面的URL模式和URL
http://localhost:8000/users/login/匹配(见2).这个URL中的单词users让Django在users/urls.py中查找,而单词login让它将请求发送给Django默认视图login(请注意,视图参数为lonin,而不是views.login)。鉴于我们没有编写自己的视图函数,我们传递了一个字典,告诉Django去哪里查找我们将编写的模板。这个模板包含在应用程序users而不是learning\_logs中。

   
1.模板login.html

   
用户要登录页面时,Django将使用其默认视图login,但咱还是需要呢者页面提供模板。为夫,在目录learning_log/users/中,创建一个叫作也templates的目,并在里创建一个称也users的目。以下是模板login.html,我们将该储存到learning_log/users/templates/users/中:

login.html

{% extends
“learning_logs/base.html” %}

{% block content %}

  {% if form.errors
%}                                                             (1)
  <p>Your username and password didn’t match. Please try
again.</P>
  {% endif %}

  <form method=”post” action=”{% url ‘users:login’
%}>                              (2)
  {% csrf_token %}
  {{ form.as_p
}}                                                                  
(3)
 
  <button name=”submit”>log
in</button>                                              (4)
  <input type=”hidden” name=”next” value=”{% url
“learning_logs:index” %}” />        (5)
  </form>
{% endblock content %}

   
这个模板继承了base.html,旨在确保登录页面的外观和网站的其它页面相同。请留心,一个应用程序中之模板可能持续另一个应用程序中之模版。

   
如果表单的errors属性被装置,我们就显示同一长长的错误信息(见(1)),指出输入的用户名–密码对同数据库中贮存的别样用户名–密码对都未兼容。

   
我们而让报到视图处理表单,因此用实参action设置为报到页面的URL(见(2))。登录视图将一个表单发送给模板,在模板被,我们来得是表单(见(3))并丰富一个交按钮(见(4))。在(5)处,我们包含了一个藏匿的表单元素——’next’,其中的实参value告诉Django在用户成功登录后将该重定向到什么地方——在这里是主页。

   
2.链接到登录页面

   
下面在base.html中补充加至登录页面的链接,让有页面还饱含它。用户就登录时,我们无思展示这链接,因此将它嵌套在一个{%
if %}标签中:

base.html

<p>
  <a href=”{% url ‘learning_logs:index’ %}”>Learning
Log</a> –
  **<a href=”{% url ‘learning_logs:topics’ %}”>Topics</a>


  {% if user.is_authenticated
%}                                         (1)

    Hello,{{ user.username
}}.                                           (2)

  {% else %}
    <a href=”{% url ‘users:login’ %}”>log
in</a>                         (3)

  {% endif %}
</p>

{% block content %}{% endblock content %}

   
在Django身份验证系统遭到,每个模板都可采用user,这个变量有一个is_authenticated属性:如果用户已登录,该属性将为True,否则也False。这吃咱会为曾经过认证的用户展示同一长达消息,而向非经过身份验证的用户展示另外一样长条消息。

   
在这里,我们向已经报到的用户展示平条问候语(见(1))。对于早已透过身份验证的用户,还设置了性username,我们以这个特性来个性化问候语,让用户知道他现已登录(见(2))。在(3)处,对于尚非经过身份验证的用户,我们重新提拔一个交登录页面的链接。

   
3.以登录页面

   
前面建立了一个用户账户,下面来报到一下,看看登录页面是否中。请看http://localhost:8000/admin/,如果我们依然是以管理员的身份登录的,请在页眉上找到注销链接并单击它。

   
注销后,访问http://localhost:8000/users/login/,我们将看到类似于下图所示的登录页面。输入我们前面设置的用户名和密码,将进入页面index。在这个主页的页眉中,显示了一条个性化问候语,其中包含你的用户名。

19.2.3 
注销

   
现在内需提供一个叫用户注销的门径。我们无创造用于注销的页面,而为用户仅仅待单击一个链接就是可知收回并返回到主页。为是,我们用为收回链接定义一个URL模式,编写一个视图函数,并当base.html中上加一个撤销链接。

   
1.注销URL

   
下面的代码为收回定义了URL模式,该模式及URL
http://locallwst:8000/users/logout/匹配。修改后的users/urls.py如下:

urls.py

“””为应用程序users定义URL模式”””
from django.conf.urls import url
from django.contrib.auth.views import login

from . import views

urlpatterns = [
    #签到页面
   
url(r’^login/$’,login,{‘template_name’:’users/login.html’},name=’login’),
    #注销
    url(r’^logout/$’,views.logout_view,name=’logout’),
]

   
这个URL模式将请发送给函数logout_view()。这样受这函数命名,旨在将那个与我们用于其中调用的函数logout()区分开来(请保管我们修改的时users/urls.py,而不是learning_log/urls.py).

   
2.视图函数
logout_view()

   
函数logout_view()很粗略:只是导入Django函数logout(),并调用它,再重定向到主页。请打开users/views.py,并输入下面的代码:

views.py

from django.http import
HttpResponseRedirect
from django.core.urlresolvers import reverse
#从今Django核心解析器中导入模块
from django.contrib.auth import
logout                                          (1)
    def logout_view(request):
    “””注销用户”””
   
logout(request)                                                             
(2)
    return
HttpResponseRedirect(reverse(‘learning_logs:index’))                 
(3)

   
我们由django.contrib.auth中导入了函数logout()(见(1)).在(2)处,我们调用了函数logout(),它要求以request对象作为实参。然后,我们重定向到主页(见(3)).

   
3.链接到注销视图

   
现在我们要添加一个注销链接。我们于base.html中添加这种链接,让每个页面都富含它;我们将它们座落标签{%
if user.is_authenticated %}中,使得仅当用户登录后才能够看出其:

base.html

<p>
  <a href=”{% url ‘learning_logs:index’ %}”>Learning
Log</a> –
  <a href=”{% url ‘learning_logs:topics’ %}”>Topics</a> –
  {% if user.is_authenticated %}
    Hello,{{ user.username }}.
    <a href=”{% url ‘users:logout’ %}”>log out</a>
  {% else %}
    <a href=”{% url ‘users:login’ %}”>log in</a>
  {% endif %}
</p>

{% block content %}{% endblock content %}

   
图19-5显得了用户登录后见到的主页。这里的要是创办能够正确工作的网站,因此尚未设置任何样式。确定所急需的功用还能够是运行后,我们拿设置是网站的体制,使其扣留起还专业。

19.2.4 
注册页面

   
下面来创造一个让初用户能够报的页面。我们以应用Django提供的表单UserCreationForm,但编制好之视图函数和模板。

   
1.登记页面的URL模式

   
下面的代码定义了挂号页面的URL模式,它为包含在users/urls.py中:

urls.py

“””为应用程序users定义URL模式”””
from django.conf.urls import url
from django.contrib.auth.views import login

from . import views

urlpatterns = [
    #登录页面
   
url(r’^login/$’,login,{‘template_name’:’users/login.html’},name=’login’),
    #注销
    url(r’^logout/$’,views.logout_view,name=’logout’),
    #注册页面
    url(r’^register/$’,views.register,name=’register’),
]

    这个模式及URL
http://localhost:8000/users/register/匹配,并将请求发送给我们即将编写的函数register()。

   
2.视图函数register()

   
在注册页面首蹩脚让请时,视图函数register()需要展示一个空的挂号表单,并以用户提交填写好的登记表单时对那进展处理。如果注册成功,这个函数还欲受用户自行登录。请以users/views.py中补充加如下代码:

views.py

from django.shortcuts
import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
#自从Django核心解析器中导入模块
from django.contrib.auth import logout,login,authenticate
from django.contrib.auth.forms import UserCreationForm
    def logout_view(request):
    “””注销用户”””
    logout(request)
    return HttpResponseRedirect(reverse(‘learning_logs:index’))

    def register(request):
    “””注册新用户”””
    if request.method != “POST”:
        form =
UserCreationForm()                                            (1)
    else:
        #处理填写好之表单
        form =
UserCreationForm(data=request.POST)                            (2)
   
        if
form.is_valid():                                                   
(3)
            new_user =
form.save()                                             (4)
        #让用户自动登录,再重定向到主页
            authenticated_user =
authenticate(username=new_user.username,password=request.POST[‘password1’])    
(5)
           
login(request,authenticated_user)                                                                   
(6)
            return
HttpRedponseRedirect(reverse(‘learning_logs:index’))                                          
(7)

    context = {‘form”:from}
    return render(request,’users/register.html’,context)
   
我们先是导入了函数render(),然后导入了函数login()和authenticate(),以便在用户对地填写了注册信息经常让其活动登录。我们尚导入了默认表单UserCreationForm.在函数register()中,我们检查如果响应的是否是POST请求。如果无是,就创办一个UserCreationForm实例,且未深受它们提供其他初始数据(见(1)).

   
如果响应的凡POST请求,我们不怕根据提交的多少创建一个UserCreationForm实例(见(2)),并检讨这些数据是否行得通:就这里而言,是用户称不包含非法字符,输入的简单只密码相同,以及用户没有视图做恶意之业务。

   
如果提交的多寡中,我们便调用表单的方save(),将用户称以及密码的散列值保存到数据库中。方法save()返回新创的用户对象,我们以那个储存于new_user中。

   
保存用户之信息后,我们为用户自行登录,这带有两只步骤。首先,我们调用authenticate(),并以实参new_user.username和密码传送让它们(见(5))。用户注册时,被求输入密码两次于;由于表单是可行之,我们解输入的立即简单单密码是同等之,因此得以行使中任何一个。在这里,我们打表单的POST数据中获得与’password1’相关联的值。如果用户称与密码是,方法authenticate()将回到一个由此了身份验证的用户对象,而我辈将该储存于authenticated_user中。接下来,我们调用函数login(),并将对象request和authenticated_user传递给它,这将为新用户创建中之对话。最后,我们拿用户重定向到主页,其页眉中显了平久个性化的问候语,让用户知道注册成功了。

   
3.注册模板

   
注册页面的模板与登录页面的沙盘类似,请务必以其保存及login.html所于的目中:

register.html

{% extends
‘learning_logs/base.html’ %}

{% block content %}

  <form method=”post” action=”{% url ‘users:register’ %}”>
    {% csrf_token %}
    {{ form.as_p }}
    
    <button name=”submit”>register</button>
    <input type=”hidden” name=”text” value=”{% url
‘learning_logs:index’ %}” />
  </form>

{% endblock content %}
   
这里呢运用了办法as_p,让Django在表单中是地出示所有的字段,包括错误信息——如果用户没有正确地填写表单。

   
4.链接到报页面

   
接下,我们抬高这样的代码,即在用户并未登录时显得到注册页面的链接:

   
base.html

<p>
  <a href=”{% url ‘learning_logs:index’ %}”>Learning
Log</a> –
  <a href=”{% url ‘learning_logs:topics’ %}”>Topics</a> –
  {% if user.is_authenticated %}
    Hello,{{ user.username }}.
    <a href=”{% url ‘users:logout’ %}”>log out</a>
  {% else %}
**    <a href=”{% url ‘users:register’ %}”>register</a>


    <a href=”{% url ‘users:login’ %}”>log in</a>
  {% endif %}
</p>

{% block content %}{% endblock content %}

   
现在,已报到的用户看到底是个性化问候语和取消链接,而非登录的用户看到底是挂号与登录链接。请尝试用登记页面创建几只用户称各不相同的用户账户。

   
在生同样节省,我们以针对有的页面进行限定,仅为曾经报到的用户访问它们,我们尚将包每个主题且属特定用户。

注意:这边的挂号系统允许用户创建任意数量的账户。有些系统要求用户确认该身价:发送一封闭邮件,用户回复后该账户才生效。通过这样做,系统生成的垃圾账户将比较这里用的简约系少。然而,学习创建应用程序时,完全可以像这里所做的那样,使用简易的用户注册系统。

19.3 
让用户有和谐的数目

   
用户应能输入其专有的多少,因此我们拿创设一个体系,确定各数据所属的用户,再克对页面的访问,让用户只能利用自己之数。

   
在本节惨遭,我们以修改模型Topic,让每个主题且归入为特定用户。这吗拿震慑条目,因为每个条目都属特定的主题。我们事先来界定对一些页面的拜会。

19.3.1 
使用@login_required限制访问

   
Django提供了装饰器@login_required,让咱们会轻松地落实这样的对象:对于某些页面,只允许已经报到的用户访问它们。装饰器(decorator)是身处函数定义前面的指令,Python在函数运行前,根据它们来修改函数代码的行事。下面来拘禁一个示范。

   
1.范围对topics页面的看

   
每个主题且由特定用户有,因此只同意就登录的用户要topics页面。为是,在learning_logs/views.py中上加如下代码:

    views.py

from django.shortcuts
import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_requried
from .models import Topic,Entry
from .forms import TopicForm,EntryForm

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

@login_required
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”:
        #匪提交数据:创建一个初表单
    form = TopicForm()
    else:
    “””POST提交的多少,对数据开展拍卖”””
    form = TopicForm(request.POST)
    if form.is_valid():
        form.save()
            return
HttpResponseRedirect(reverse(‘learning_logs:topics’))

def new_entry(request,topic_id):
    “””在一定的主题中补充加新条目”””
    topic = Topic.objects.get(id=topic_id)
    if request.method != “POST”:
    #切莫提交数据,创建一个空表单
    form = EntryForm()
    else:
    #POST提交的数目,对数据开展处理
    form = EntryForm(data=request.POST)
        if form.is_valid():
        new_entry = form.save(commit=False)
        new_entry.topic = topic
        new_entry.save()
        return
HttpResponseRedirect(reverse(‘learning_logs:topic’,args=[topic_id]))

def edit_entry(request,entry_id):
    #编既来条文
    entry = Entry.objects.get(id=entry.id)
    topic = entry.topic
    if request.method != “POST”:
        #元请求,使用时章填充表单
    form = EntryForm(instance=entry)
    else:
    #POST提交的多寡,对数码开展处理
    form = EntryForm(instance=entry,data=request.POST)
    if form.is_valid():
        form.save()
        return
HttpResponseRedirect(reverse(‘learning_logs:topic’,args=[topic.id]))

context = {‘entry’:entry,’topic’:topic,’form’:form}
return render(request,”learning_logs/edit_entry.html’,context)

   
我们先是导入了函数login_required()。我们将login_required()作为装饰器用于视图函数topics()——在其前面加上@login_required,让Python在运转topics()的代码前先运行login_required()的代码。

   
login_required()的代码检查用户是否就登录,仅当用户登录时,Django才运行topics()的代码。如果用户不登录,就重定向到登录页面。

   
为实现这种又定向,我们需要修改settings.py,让Django知道到哪去追寻登录页面。请以settings.py末尾添加如下代码:

settings.py

“””
Django settings for learning_log project.

Generated by ‘django-admin startproject’ using Django 1.11.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
“””

import os

# Build paths inside the project like this: os.path.join(BASE_DIR,
…)
BASE_DIR =
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Quick-start development settings – unsuitable for production
# See
https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = ‘lw7xtyu&b2et$$!)$m*ja6flzz#1@-653ief@15p!5_%!(%d!b’

# SECURITY WARNING: don’t run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = [
    ‘django.contrib.admin’,
    ‘django.contrib.auth’,
    ‘django.contrib.contenttypes’,
    ‘django.contrib.sessions’,
    ‘django.contrib.messages’,
    ‘django.contrib.staticfiles’,
    #自之应用程序
    “learning_logs”,
    ‘users’,
]

MIDDLEWARE = [
    ‘django.middleware.security.SecurityMiddleware’,
    ‘django.contrib.sessions.middleware.SessionMiddleware’,
    ‘django.middleware.common.CommonMiddleware’,
    ‘django.middleware.csrf.CsrfViewMiddleware’,
    ‘django.contrib.auth.middleware.AuthenticationMiddleware’,
    ‘django.contrib.messages.middleware.MessageMiddleware’,
    ‘django.middleware.clickjacking.XFrameOptionsMiddleware’,
]

ROOT_URLCONF = ‘learning_log.urls’

TEMPLATES = [
    {
        ‘BACKEND’: ‘django.template.backends.django.DjangoTemplates’,
        ‘DIRS’: [],
        ‘APP_DIRS’: True,
        ‘OPTIONS’: {
            ‘context_processors’: [
                ‘django.template.context_processors.debug’,
                ‘django.template.context_processors.request’,
                ‘django.contrib.auth.context_processors.auth’,
               
‘django.contrib.messages.context_processors.messages’,
            ],
        },
    },
]

WSGI_APPLICATION = ‘learning_log.wsgi.application’

# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/\#databases

DATABASES = {
    ‘default’: {
        ‘ENGINE’: ‘django.db.backends.sqlite3’,
        ‘NAME’: os.path.join(BASE_DIR, ‘db.sqlite3’),
    }
}

# Password validation
#
https://docs.djangoproject.com/en/1.11/ref/settings/\#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        ‘NAME’:
‘django.contrib.auth.password_validation.UserAttributeSimilarityValidator’,
    },
    {
        ‘NAME’:
‘django.contrib.auth.password_validation.MinimumLengthValidator’,
    },
    {
        ‘NAME’:
‘django.contrib.auth.password_validation.CommonPasswordValidator’,
    },
    {
        ‘NAME’:
‘django.contrib.auth.password_validation.NumericPasswordValidator’,
    },
]

# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = ‘en-us’

TIME_ZONE = ‘UTC’

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = ‘/static/’
#自之装
LOGIN_URL = ‘/users/login/’

    现在,
如果未登录的用户请求装饰器@login_required的掩护页面,Django将重定向到settings.py中的LOGIN_URL指定的URL。

   
要测试是装置,可取消并跻身主页。然后,单击链接Topics,这将重定向到登录页面。接下来,使用我们的账户登录,并更单击主页中的Topics链接,我们将看到topics页面。

   
2.圆展示对项目”学习笔记”的访

   
Django让我们会轻松地范围对页面的拜会,但我们须对要维护哪些页面做出决定。最好先确定项目的那些页面不需保障,再克对其余的富有页面的顾。我们好轻松地改过于严厉的访问限制,其高风险比较非限量对快页面的拜会更低。

   
在项目“学习笔记”中,我们用非限量对主页、注册页面和撤销页面的看,并限对其他具有页面的访问。

   
在底下的learning_logs/views.py中,对除index()外之每个视图都施用了装饰器@login_required:

views.py

from django.shortcuts
import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_requried
from .models import Topic,Entry
from .forms import TopicForm,EntryForm

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

@login_required
def topics(request):
    “””显示所有的主题”””
    topics = Topic.objects.order_by(“date_added”)
    context = {‘topics’:topics}
    return render(request,’learning_logs/topics.html’,context)

@login_required
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)

@login_required
def new_topic(request):
    “””添加新主题”””
    if request.method != “POST”:
        #无提交数据:创建一个初表单
    form = TopicForm()
    else:
    “””POST提交的多寡,对数码开展处理”””
    form = TopicForm(request.POST)
    if form.is_valid():
        form.save()
            return
HttpResponseRedirect(reverse(‘learning_logs:topics’))

@login_required
def new_entry(request,topic_id):
    “””在特定的主题中上加新条令”””
    topic = Topic.objects.get(id=topic_id)
    if request.method != “POST”:
    #不提交数据,创建一个空表单
    form = EntryForm()
    else:
    #POST提交的数额,对数码进行拍卖
    form = EntryForm(data=request.POST)
        if form.is_valid():
        new_entry = form.save(commit=False)
        new_entry.topic = topic
        new_entry.save()
        return
HttpResponseRedirect(reverse(‘learning_logs:topic’,args=[topic_id]))

@login_required
def edit_entry(request,entry_id):
    #编写既出条款
    entry = Entry.objects.get(id=entry.id)
    topic = entry.topic
    if request.method != “POST”:
        #初请求,使用时章填充表单
    form = EntryForm(instance=entry)
    else:
    #POST提交的数据,对数码进行拍卖
    form = EntryForm(instance=entry,data=request.POST)
    if form.is_valid():
        form.save()
        return
HttpResponseRedirect(reverse(‘learning_logs:topic’,args=[topic.id]))

context = {‘entry’:entry,’topic’:topic,’form’:form}
return render(request,”learning_logs/edit_entry.html’,context)

   
如果我们当无登录的情状下品尝看这些页面,将让重定向到登录页面。另外,我们尚非可知单击到new_topic等页面的链接。但若输入URL
http://localhost:8000/new\_topic/,将重定向到登录页面。对于所有与私有用户数据相关的URL,都应限制对它们的访问。

19.3.2 
将数据涉嫌到用户

   
现在,需要用数据涉嫌到付其的用户。我们仅需要将最为高层的数据涉嫌到用户,这样更低层的数量以活动关联到用户。例如,在类型”学习笔记”中,应用程序的尽高层数据是主题,而备条条框框都和特定主题相关联。只要每个主题且属特定用户,我们便会确定数据库中每个条目的持有者。

   
下面来窜模型Topic,在内部添加一个涉嫌到用户的外键。这样做后,我们亟须对数据库进行搬迁。最后,我们必须对有些视图进行改动,使该独自展示和当下报到的用户相关联的数。

   
1.改动模型Topic

   
对models.py的改只是关乎两行代码:

models.py

from django.db import
models
from django.contrib.auth.models import User

# Create your models here.
class Topic(models.Model):
    ”’用户学习的主题”’
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey(User)

    def __str__(self):
        ”’返回模型的字符串表示”’
        return self.text

class Entry(models.Model):
    “””学到之关于某主题的现实知识”””
    topic = models.ForeignKey(Topic)
    text = models.TextField()
    date_added = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = ‘entries’

        def __str__(self):
            “””返回模型的字符串表示”””
            return self.text[:50] + “…”

   
2.确定当前发生怎样用户

   
我们搬迁数据库时Django将对数据库进行修改,使其会仓储主题以及用户中的涉及。为实施迁移,Django需要懂得该拿逐条既出主题涉及到哪个用户。最简易的不二法门是,将既是出主题都提到到与一个用户,如极品用户。为是,我们需要懂得该用户之ID。

   
下面来查看已开立的拥有用户之ID.为这个,启动一个Django
shell会话,并履行如下命令:

   
3.迁数据库

   
知道用户ID后,就可迁移数据库了。

   
我们首先实施了指令makemigrations.

19.3.3 
只允许用户访问自己之主题

   
当前,不管我们因为谁用户之身价登录,都能够看出所有的主题。我们来转这种景象,只向用户展示属于自己的主题。

   
在views.py中,对函数topics()做出如下修改:

views.py

from django.shortcuts
import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_requried
from .models import Topic,Entry
from .forms import TopicForm,EntryForm

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

@login_required
def topics(request):
    “””显示有的主题”””
    topics =
Topic.objects.filter(owner=request.user).order_by(“date_added”)

    context = {‘topics’:topics}
    return render(request,’learning_logs/topics.html’,context)

@login_required
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)

@login_required
def new_topic(request):
    “””添加新主题”””
    if request.method != “POST”:
        #切莫提交数据:创建一个初表单
    form = TopicForm()
    else:
    “””POST提交的多少,对数据开展处理”””
    form = TopicForm(request.POST)
    if form.is_valid():
        form.save()
            return
HttpResponseRedirect(reverse(‘learning_logs:topics’))

@login_required
def new_entry(request,topic_id):
    “””在一定的主题中补充加新条目”””
    topic = Topic.objects.get(id=topic_id)
    if request.method != “POST”:
    #勿提交数据,创建一个空表单
    form = EntryForm()
    else:
    #POST提交的数目,对数据开展处理
    form = EntryForm(data=request.POST)
        if form.is_valid():
        new_entry = form.save(commit=False)
        new_entry.topic = topic
        new_entry.save()
        return
HttpResponseRedirect(reverse(‘learning_logs:topic’,args=[topic_id]))

@login_required
def edit_entry(request,entry_id):
    #编排既来条文
    entry = Entry.objects.get(id=entry.id)
    topic = entry.topic
    if request.method != “POST”:
        #首先请求,使用时章填充表单
    orm = EntryForm(instance=entry)
    else:
    #POST提交的多寡,对数码开展处理
    form = EntryForm(instance=entry,data=request.POST)
    if form.is_valid():
        form.save()
        return
HttpResponseRedirect(reverse(‘learning_logs:topic’,args=[topic.id]))

context = {‘entry’:entry,’topic’:topic,’form’:form}
return render(request,”learning_logs/edit_entry.html’,context)

   
用户登录后,request对象将生一个user属性,这个特性存储了有关该用户的信。代码Topic.objects.filter(owner=request.user)让Django只从数据库中获得owner属性为当前用户Topic对象。由于我们没改主题的显示方式,因此无需对页面topics的模版做任何修改。

   
要翻看结果,以富有既来主题涉及到之用户之位置登录,并走访topics页面,我们拿见到有着的主题。然后,注销并以任何一个身份登录,topics页面将不见面列出任何主题。

19.3.4 
保护用户的主题

   
我们尚未曾限制对显示单个主题的页面的访,因此别已报到的用户还好输入类似于http://localhost:8000/topics/1/的URL,来访问显示相应的主题的页面。

   
我们和好摸索一碰就知晓了。以富有具有主题的用户位置登录,访问特定的主题,并复制该网页的URL,或用中间的ID记录下来。然后,注销并为其他一个用户的地位登录,再输入显示前述主题的页面的URL.虽然我们是为任何一个用户登录的,但仍能够查阅该主题的条目。

   
为修复这种题材,我们以视图函数topic()获取请求的条规前实行检查:

views.py

from django.shortcuts
import render
from django.http import HttpResponseRedirect, Http404                
     (1)

from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required

from .models import Topic, Entry
from .forms import TopicForm, EntryForm

def index(request):
    “””The home page for Learning Log.”””
    return render(request, ‘learning_logs/index.html’)

@login_required
def topics(request):
    “””Show all topics.”””
    topics =
Topic.objects.filter(owner=request.user).order_by(‘date_added’)
    context = {‘topics’: topics}
    return render(request, ‘learning_logs/topics.html’, context)

@login_required
def topic(request, topic_id):
    “””Show a single topic, and all its entries.”””
    topic = Topic.objects.get(id=topic_id)
    # 确认请的主题属于即用户
    if topic.owner !=
request.user:                                     (2)

        raise Http404
        
    entries = topic.entry_set.order_by(‘-date_added’)
    context = {‘topic’: topic, ‘entries’: entries}
    return render(request, ‘learning_logs/topic.html’, context)
   
服务器上尚无请求的资源时,标准的做法是回来404响应。在这里,我们导入了好Http404(见(1)),并于用户请求其不能够查的主题时引发这很。收到主题请求后,我们于渲染网页前检查该主题是否属于即报到的用户。如果请的主题不归当前用户所有,我们
就吸引Http404格外,让Django返回一个404谬误页面。

   
现在,如果我们视图查看其他用户的主题条目,将张Django发送的Page Not
Found。在第20章节,我们拿对这个路展开配置,让用户观看更贴切的谬误页面。

19.3.5  保护页面
edit_entry

   
页面edit_entry的URL为http://localhost:8000/edit\_entry/entry\_id/,其中entry\_id是一个数字。下面来保护这个页面,禁止用户通过输入类似于前面的URL来访问其他用户的条目:

views.py

@login_required
def edit_entry(request, entry_id):
    “””Edit an existing entry.”””
    entry = Entry.objects.get(id=entry_id)
    topic = entry.topic
    if topic.owner != request.user:
        raise Http404

   
我们赢得指定的章以及和的并行关联的主题,然后检查主题的主人是否是时报到的用户,如果未是,就吸引Http404好。

19.3.6 
将新主题涉及到当前用户

   
当前,用于补充加新主题的页面是问题,因此她从未拿新主题涉及到一定用户。如果我们尝试添加新主题,将视错误信息IntegritError,指出learning_logs_topic.user_id不克为NULL。Django的意是说,创建新主题时,我们要指定其owner字段的价。

   
由于我们可由此request对象获悉当前用户,因此存在一个修复这种问题之粗略方案。请添加下面的代码,将新主题涉及到眼前用户:

views.py

@login_required
def
new_topic(request):
    “””Add a new
topic.”””
    if request.method
!= ‘POST’:
        # No data
submitted; create a blank form.
        form =
TopicForm()
    else:
        # POST data
submitted; process data.
        form =
TopicForm(request.POST)
        if
form.is_valid():
           
new_topic = form.save(commit=False)           (1)

           
new_topic.owner = request.user                (2)

           
new_topic.save()                              (3)

            return
HttpResponseRedirect(reverse(‘learning_logs:topics’))

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

   
我们首先调用form.save(),并传递实参commit=False,这是盖咱们先行改新主题,再将其保存及数据库中(见(1)),接下,将新主题owner的性能设置也当下用户(见(2))。最后,对刚刚定义的主题调用save()(见(3))。现在主题包含有必要的数量,将吃成功地保留。

   
现在,这个类别允许其他用户注册,而每个用户想补充加多少新主题都足以。每个用户都只好看自己的数量,无论是查看数据、输入新数据要修改旧数据经常还这样。

19.4 
小结

   
在本章中,我们念了怎样用表单来受用户添加新主题、添加新条令和编辑既来条款。接下来,我们上学了什么落实用户账户。我们深受一直用户能够登录以及撤销,并学习了哪利用Django提供的表单UserCreationForm让用户能够创立新账户。

   
建立简单的用户身份验证和挂号系统后,我们常见采取装饰器@login_requried禁止非登录的用户访问特定页面。然后,我们由此以外键将数据涉嫌到特定用户,我们尚读了哪履行要求指定默认数据的数据库迁移。

   
最后,我们上了怎样修改视图函数,让用户只能观属于他的数码。我们采取方式filter()来获得合适的多少,并学习了什么拿呼吁的多寡的所有者及当前登录的用户展开较。

   
该为哪些数据可不论是看,该队那些数据开展维护呢?这恐怕毫无总是那么强烈,但经不断地练就可知左右这种技术。在本章中,我们即便该如何护用户数据所召开的核定表明,与丁合作开发项目是个不利的专注:有人对项目进行检讨的话,更易察觉那薄弱环节。

   
至此,我们创建了一个功能齐备的类型,它运行在地方电脑达。在本书的尾声一回,我们以装这个类别的样式,使该再出色;我们还拿将其配置至平光服务器上,让任何人都只是通过互联网注册并创建账户。

 

 

 

 

   

 

 

   

 

 

   

 

   

 

 

 

 

   

 

网站地图xml地图