完整的Django入门指南学习笔记5

 

前言

 

欢迎来到本系列教程的第5部分,在这节课,我们将学习如何保护视图防止未登录的用户访问,以及在视图和表单中访问已经登录的用户,我们还将实现主题列表和回复列表视图,最后,将探索Django ORM的一些特性和数据迁移的简单介绍。

 


 

保护视图

 

我们必须保护视图防止那些未认证(登录)的用户访问,下面是发起一个新话题的页面

 

 

在上图中,用户还没有登录,尽管他们可以看到页面和表单.Django有一个内置的  视图装饰器 来避免它被未登录的用户访问:

 

boards / views.py完整代码

 

来自 django.contrib.auth.decorators  导入 login_required

@login_required 
def  new_topic request pk ):
    #...

 

现在如果用户没有登录,将被重定向到登录页面:

 

 

注意查询字符串  ?next = / boards / 1 / new /  ,我们可以改进登录模板以便利用  next 变量来改进我们的用户体验,(译注:实际上这步操作不加也没问题)

 

配置登录后的重定向地址

 

templates / login.html  (查看完整内容

 

<形式 方法= “POST”  的novalidate > 
  {% csrf_token  %} 
  <输入 类型= “隐藏”  名称= “下一个”  的值= {{  下次 }} > 
  {% 包括 '包括/ form.html'  %} 
  <按钮 type = “submit”  class = “btn btn-primary btn-block” >登录</ button> 
</ form>

 

现在尝试登录,登录成功后,应用程序会跳转到原来所在的位置。

 

 

下一个  参数是内置功能的一部分(译注:详情请参考Django的官方文档

 

测试

 

添加现在一个测试用例确保主题发布视图被  @login_required装饰器保护了,不过,我们还是先来重构一下  板/测试/ test_views.py  文件。

 

test_views.py拆分3分类中翻译个文件:

 

  • test_view_home.py  包含HomeTests类(完整代码
  • test_view_board_topics.py  包含BoardTopicsTests类(完整代码
  • test_view_new_topic.py  包含NewTopicTests类(完整代码

 

我的项目/
 |  -  myproject /
 | |  - 帐户/
 | |  - 板/
 | | |  - 迁移/
 | | |  -  templatetags /
 | | |  - 测试/
 | | | |  -  __init__.py
 | | | |  -  test_templatetags.py
 | | | |  -  test_view_home.py < - 这里
 | | | |  -  test_view_board_topics.py < - 这里
 | | | +  -  test_view_new_topic.py < - 在这里
 | | |  -  __init__.py
 | | |  -  admin.py
 | | |  -  apps.py
 | | |  -  models.py
 | | +  -  views.py
 | |  -  myproject /
 | |  - 静态/
 | |  -  templates /
 | |  -  db.sqlite3
 | +  -  manage.py
 +  -  venv /

 

重新运行测试,确保一切正常。

 

在现在  test_view_new_topic.py  中添加一个新测试用例,检查用来试图是否被@login_required保护:

 

boards / tests / test_view_new_topic.py  (完整代码

 

来自 django.test的 导入 来自django.urls的TestCase 
..models 导入导入反向   
   

class  LoginRequiredNewTopicTests TestCase ):
   def  setUp self ):
       Board 对象create name = 'Django' description = 'Django board。' self url  =  reverse 'new_topic' kwargs = { 'pk' 1 })
       self 响应 =  自我客户得到自我url def  test_redirection self ):
       login_url  =  reverse 'login' self assertRedirects 自我响应'{} LOGIN_URL?下一= {URL}' 格式LOGIN_URL = LOGIN_URL URL = 自我URL ))

 

在测试用例中,我们尝试在没有登录的情况下发送请求给  新主题  视图,期待的结果是请求重定向到登录页面。

 


 

访问已登录用户

 

现在我么可以改进  new_topic  视图,将发布主题的用户设置当前登录的用户,取代之前直接从数据库查询出来的第一个用户,之前这份代码是临时的,因为那时候还没有方法去获取登录用户,但是现在可以了:

 

boards / views.py  (完整代码

 

来自 django.contrib.auth.decorators  django.shortcuts 导入 login_required 
导入get_object_or_404 重定向渲染     

from  .forms  .models import Board Post 导入 NewTopicForm
    

@login_required 
def  new_topic request pk ):
    board  =  get_object_or_404 Board pk = pk if  request 方法 ==  'POST' 形式 =  NewTopicForm 请求POST 如果 形式is_valid ():
            topic  =  form save commit = False 主题=  董事会
            主题starter  =  请求用户  #< - 这里的
            主题保存()
            发布对象创建消息= 形式cleaned_data 获得'信息' ),
                话题= 主题CREATED_BY = 请求用户  #< -这里
            返回 重定向'board_topics' PK = PK #TODO:重定向到所创建的主题页面
    否则形式 =  NewTopicForm ()
    返回 渲染请求'new_topic.html' { '板' '形式' 形式})

 

我们可以添加一个新的主题快速验证一下:

 

 


 

主题回复列表

 

现在我们花点时间来实现主题的回复列表页面,先来看一下下面的线框图:

 

 

首先我们需要写URL路由:

 

myproject / urls.py完成代码

 

url(r'^ boards /(?P <pk> \ d +)/ topics /(?P <topic_pk> \ d +)/ $',views.topic_posts,name ='topic_posts'),

 

有两个关键字参数,pk用于唯一标识版块(局),topic_pk用于唯一标识该回复来自哪个主题。

 

boards / views.py完整代码

 

django.shortcuts  进口 get_object_or_404 使
.models  导入 主题

def  topic_posts request pk topic_pk ):
    topic  =  get_object_or_404 主题board__pk = pk pk = topic_pk return  render request 'topic_posts.html' { 'topic' topic })

 

注意我们正在间接地获取当前的版块,记住,主题(主题)模型是关联版块(局)模型的,所以我们可以访问当前的版块,你将在下一个代码段中看到:

 

templates / topic_posts.html完整代码

 

{% 延伸 'base.html文件'  %}

{% block  title  %} {{  topic.subject  }} {% endblock  %}

{% 面包屑 %} 
  <LI  类= “面包屑项目” > <a  href= “{% URL'home'%}”></A> </ LI> <LI 类= “面包屑项目” > <a href= “{%URL'board_topics'topic.board.pk %}”>{{ topic.board.name }} </A> </ LI> <LI 类= “面包屑项活性” > {{ 话题。subject }} </ li> {%endblock %}   
          
     
  

{% block  content  %}

{% endblock  %}

 

现在你会看到我们在模板中  board.name被替换掉了,在导航条,是使用的主题的属性:topic.board.name

 

 

现在我们给topic_posts添加一个新的测试文件:

 

板/测试/ test_view_topic_posts.py

 

django.contrib.auth.models  进口 用户
django.test  进口 的TestCase 
django.urls  进口 的决心相反

来自 ..models  导入 Board Post Topic 
from  ..views  import  topic_posts

class  TopicPostsTests TestCase ):
    def  setUp self ):
        board  =  Board 对象create name = 'Django' description = 'Django board。' user  =  User 对象create_user 用户名= '约翰' 电子邮件= 'john@doe.com' 密码= '123' 主题 =  主题对象create subject = 'Hello,world' board = board starter = user 发布对象创建消息= 'Lorem存有悲坐阿梅特' 主题= 主题CREATED_BY = 用户的url  =  反向'topic_posts' kwargs = { 'pk'文件PK 'topic_pk'主题pk })
        自我响应 =  自我客户得到网址def  test_status_code self ):
        self 的assertEquals 自我响应STATUS_CODE 200 def  test_view_function self ):
        view  =  resolve '/ boards / 1 / topics / 1 /' self 的assertEquals 视图FUNC topic_posts 

 

注意到,设置函数变得越来越复杂,我们可以创建一个minxin或者抽象类来重用这些代码,我们也可以使用第三方库来初始化设置一些测试数据,来减少这些样板代码。

 

同时,我们已经有了大量的测试用例,运行速度开始逐渐变得慢起来,我们可以通过用测试套件的方式测试指定的应用程序。

 

python manage.py测试板

 

为别名'default'创建测试数据库...
系统检查发现没有问题(0静音)。
.......................
-------------------------------------------------- --------------------
在1.246s中进行23次测试

销毁别名'default'的测试数据库...

 

我们还可以只运行指定的测试文件

 

python manage.py test boards.tests.test_view_topic_posts

 

为别名'default'创建测试数据库...
系统检查发现没有问题(0静音)。
..
-------------------------------------------------- --------------------
在0.129s中进行2次测试

销毁别名'default'的测试数据库...

 

抑或是指定单个测试用例

 

python manage.py test boards.tests.test_view_topic_posts.TopicPostsTests.test_status_code

 

为别名'default'创建测试数据库...
系统检查发现没有问题(0静音)。
-------------------------------------------------- --------------------
在0.100s内进行1次测试

销毁别名'default'的测试数据库...

 

很酷,是不是?

 

继续前行!

 

在topic_posts.html页面中,我们可以创建一个用于循环迭代主题下的回复

 

模板/ topic_posts.html

 

{% 延伸 'base.html文件'  %}

{% load  static  %}

{% block  title  %} {{  topic.subject  }} {% endblock  %}

{% 面包屑 %} 
  <LI  类= “面包屑项目” > <a  href= “{% URL'home'%}”></A> </ LI> <LI 类= “面包屑项目” > <a href= “{%URL'board_topics'topic.board.pk %}”>{{ topic.board.name }} </A> </ LI> <LI 类= “面包屑项活性” > {{ 话题。subject }} </ li> {%endblock %}   
          
     
  

{% block  content  %}

  <DIV  类= “MB-4” > 
    <a  href= "#"  class= "btn btn-primary"  role= "button">回复</A> </ DIV>
  

  {% for  post  in  topic.posts.all  %} 
    <div  class = “card mb-2” > 
      <div  class = “card-body p-3” > 
        <div  class = “row” > 
          <div  class = “col -2" > 
            <IMG  SRC = {% 静态 'IMG / avatar.svg'  %} ALT = {{  post.created_by.username  }} 类= “W-100” > 
            <小>文章:{{  post.created_by.posts。count  }} </ small> 
          </ div>
          <div  class = “col-10” > 
            <div  class = “row mb-3” > 
              <div  class = “col-6” > 
                <strong  class = “text-muted” > {{  post.created_by.username  }} </ strong> 
              </ div> 
              <div  class = “col-6 text-right” > 
                <small  class = “text-muted” > {{  post.created_at  }} </ small> 
              </ div> 
            </ div> 
            {{  post.message  }} 
            {% if  post。created_by  ==  user  %} 
              <div 类= “MT-3” > 
                <a  href= "#"  class= "btn btn-primary btn-sm"  role= "button">编辑</A> </ DIV> {%ENDIF %} </ DIV> </ div> </ div> </ div> {%endfor %}
              
              
          
        
      
    
    

{% endblock  %}

 

因为我们现在还没有任何方法去上传用户图片,所以先放一张空的图片,我从 IconFinder下载了一张免费图片,然后保存在项目的static / img目录。

 

我们还没有真正探索过的Django的ORM,代码但{{ post.created_by.posts.count }} 在数据库中会执行一个select count查询。尽管结果是正确的,但不是一个好方法。因为它在数据库中造成了多次不必要的查询。不过现在不用担心,先专注于如何与应用程序进行交互稍后,我们将改进此代码,以及如何改进那些复杂笨重的查询(译注:过早优化是万恶之源)。

 

另一个有意思的地方是我们正在测试当前帖子是否属于当前登录用户:{% if post.created_by == user %},我们只给帖子的拥有者显示编辑按钮。

 

因为我们现在要在主题页面添加一个URL路由到主题的帖子列表,更新topic.html模版,加上一个链接:

 

templates / topics.html  (完整代码

 

{% 主题 board.topics.all  %} 
  <TR> 
    <TD> <a  href= “{% URL'topic_posts'board.pk topic.pk %}”>{{ topic.subject }} </A> </ td> <td> {{ topic.starter.username }} </ td> <td> 0 </ td> <td> 0 </ td> <td> {{ topic.last_updated }} </ td> </ tr> {%endfor %}

 


 

主题回复功能

 

现在让我们来实现回复帖子的功能,以便我们可以添加更多的数据和改进功能实现与单元测试。

 

 

添加新的URL路由:

 

myproject / urls.py完整代码

 

url(r'^ boards /(?P <pk> \ d +)/ topics /(?P <topic_pk> \ d +)/ reply / $',views.reply_topic,name ='reply_topic'),

 

给回帖创建一个新的表单:

 

boards / forms.py  (完整代码

 

Django的 进口 形式
.models  导入 PostForm 形式的ModelForm ):
    模型 =  邮政
        字段 =  [ '消息' ]

 

新一个受的@login_required保护的视图,以及简单的表单处理逻辑

 

boards / views.py完整代码

 

django.contrib.auth.decorators  导入已 login_required 
django.shortcuts  进口 get_object_or_404 重定向使
者,恕不 导入 PostForm 
.models  导入 主题

@login_required 
def  reply_topic request pk topic_pk ):
    topic  =  get_object_or_404 主题board__pk = pk pk = topic_pk 如果 请求方法 ==  'POST' 形式 =  PostForm 请求POST 如果 形式is_valid ():
            post  =  form 保存提交= 帖子topic  =  主题
            帖子created_by  =  request 用户
            发帖save ()
            返回 redirect 'topic_posts' pk = pk topic_pk = topic_pk else form  =  PostForm ()
    return  render request 'reply_topic.html' { 'topic' topic 'form'form })

 

现在我们再会到new_topic视图函数,更新重定向地址(标记为  #TODO  的地方)

 

@ login_required
DEF new_topic 请求 PK = get_object_or_404  PK = PK 如果 request.method ==  'POST'  
        形式= NewTopicForm  request.POST 如果 form.is_valid ()  
            主题= form.save 提交= False #code suppress ... 
            return redirect 'topic_posts' pk = pk  topic_pk = topic.pk #< -  here 
    #code suppress ...

 

值得注意的是:在视图函数replay_topic中,我们使用topic_pk,因为我们引用的是函数的关键字参数,而在new_topic视图中,我们使用的是topic.pk,因为topic的英文一个对象(主题模型的实例对象),.pk是这个实例对象的一个属性,这两种细微的差别,其实区别很大,别搞混了。

 

回复页面模版的一个版本:

 

模板/ reply_topic.html

 

{% 延伸 'base.html文件'  %}

{% load  static  %}

{% block  title  %}发表回复{% endblock  %}

{% 面包屑 %} 
  <LI  类= “面包屑项目” > <a  href= “{% URL'home'%}”></A> </ LI> <LI 类= “面包屑项目” > <a href= “{%URL'board_topics'topic.board.pk %}”>{{ topic.board.name }} </A> </ LI> <LI 类= “面包屑项目” > <A HREF = {%URL 'topic_posts' topic.board.pk topic.pk   
          
         %} > {{  topic.subject  }} </a> </ li> 
  <li  class = ”breadcrumb-item active“ >发表回复</ li> 
{% endblock  %}

{% block  content  %}

  <形式 方法= “POST”  类= “MB-4” > 
    {% csrf_token  %} 
    {% 包括 '包括/ form.html'  %} 
    <按钮 类型= “提交”  类= “BTN BTN-成功” >发布回复</ button> 
  </ form>

  {% for  post  in  topic.posts.all  %} 
    <div  class = “card mb-2” > 
      <div  class = “card-body p-3” > 
        <div  class = “row mb-3” > 
          <div  class = “col-6” > 
            <strong  class = “text-muted” > {{  post.created_by.username  }} </ strong> 
          </ div> 
          <div  class = “col-6 text-right” > 
            <small  class = “text-muted” > {{  post。created_at  }} </ small> 
          </ div>
        </ div> 
        {{  post.message  }} 
      </ div> 
    </ div> 
  {% endfor  %}

{% endblock  %}

 

 

提交回复之后,用户会跳回主题的回复列表:

 

 

我们可以改变第一条帖子的样式,使得它在页面上更突出:

 

templates / topic_posts.html完整代码

 

{% topic.posts.all  %} 
  <DIV  类= “卡MB-2 {% 如果 for循环。首先 %} 边界暗{% ENDIF  %} > 
    {% 如果 for循环。首先 %} 
      <DIV  类= “card-header text-white bg-dark py-2 px-3” > {{  topic.subject  }} </ div> 
    {% endif  %} 
    <div  class = “card-body p-3” > 
      <! - 代码被抑制 - > 
    </ div> 
  </ div>
{% endfor %}

 

 

现在对于测试,已经实现标准化流程了,就像我们迄今为止所做的一样。在板/测试目录中创建一个新文件  test_view_reply_topic.py

 

boards / tests / test_view_reply_topic.py  (完整代码

 

来自 django.contrib.auth.models  导入 用户
来自 django.test  导入 来自django.urls的TestCase 
导入反向来自..models 导入帖子主题来自..views import reply_topic   
     
   

class  ReplyTopicTestCase TestCase ):
    ''' 
    在所有`reply_topic`视图测试
    ''' 
    def  setUp self ):
        self中使用基本测试用例董事会 =  董事会对象create name = 'Django' description = 'Django board。' self 用户名 =  “约翰” 
        自我口令 =  '123' 
        的用户 =  用户对象create_user 用户名= 自我用户名电子邮件= 'john@doe.com' 密码= 自我密码自我topic  =  主题对象创建主题= “你好,世界” 董事会= 自我首发= 用户发布对象创建消息='Lorem ipsum dolor sit amet' topic = self topic created_by = user self URL  =  反向'reply_topic' kwargs = { 'pk'文件自我PK 'topic_pk' 自我主题PK })

class  LoginRequiredReplyTopicTests ReplyTopicTestCase ):
    #...

class  ReplyTopicTests ReplyTopicTestCase ):
    #...

class  SuccessfulReplyTopicTests ReplyTopicTestCase ):
    #...

class  InvalidReplyTopicTests ReplyTopicTestCase ):
    #...

 

这里的精髓在于自定义了测试用例基类ReplyTopicTestCase。然后所有四个类将继承这个测试用例。

 

首先,测试我们是否视图受@login_required装饰器保护,然后检查HTML输入,状态码。最后,我们测试一个有效和无效的表单提交

 


 

查询集(查询结果集)

 

现在我们花点时间来探索关于模型的API。首先,我们来改进主页:

 

 

有3个任务:

 

  • 显示每个板块的总主题数
  • 显示每个板块的总回复数
  • 显示每个板块的最后发布者和日期

 

在实现这些功能前,我们先使用Python的终端

 

因为我们要在Python __str__ 终端尝试,所以,把所有的模型定义一个  方法是个好主意

 

boards / models.py完整代码

 

来自 django.db的 导入 模型
来自 django.utils.text  import  Truncator

一流的 董事会型号型号):
    #... 
    DEF  __str__ 个体经营):
        回归 自我名称

主题机型型号):
    #... 
    DEF  __str__ 个体经营):
        回归 自我学科

型号型号):
    #... 
    DEF  __str__ 个体经营):
        TRUNCATED_MESSAGE  =  自我消息返回 TRUNCATED_MESSAGE 字符30 

 

在Post模型中,使用了  Truncator 工具类,这是将一个长字符串截取为任意长度字符的简便方法(这里我们使用30个字符)

 

现在打开Python shell

 

python  管理来自boards.models 导入板的py  shell
   

#首先从数据库
=  Board 获取一个板实例对象get name = 'Django' 

 

这三个任务中最简单的一个就是获取当前版块的总主题数,因为Topic和Baoard是直接关联的。

 

board.topics.all()
<QuerySet [<主题:大家好!>,<主题:测试>,<主题:测试新帖子>,<主题:您好>]>

board.topics.count()
4

 

就这样子。

 

现在统计一个版块下面的回复数量有点麻烦,因为回复并没有和Board直接关联

 

董事会型号 进口 邮政

发布对象所有()
< 查询集 [ < 邮政我的 第一 主题..  :-) > < 邮政测试> < 帖子大家> < Post 这里的测试 > < 帖子测试回复功能>      < Post Lorem  ipsum  dolor  sit  amet ,... > < Post hi  there> < Post test> < Post Testing .. > < Post some  reply> < Post Random  random > 
] >

发布对象count ()
11

 

这里一共11个回复,但是它并不全部属于“Django”这个版块的。

 

我们可以这样来过滤

 

董事会型号 进口 邮政

董事会 =  董事会对象get name ='Django' 发布对象过滤器topic__board = < 查询集 [ < 邮政我的 第一 主题..  :-) > < 邮政测试> < 帖子有> < 帖子大家> < Post Lorem  ipsum  dolor  坐下 ,... > < 帖子这里的测试 > < 帖子测试回复功能> ] >
       


发布对象过滤器topic__board = board )。count ()
7

 

双下划线的topic__board用于通过模型关系来定位,在内部,Django在Board-Topic-Post之间构建了桥梁,构建SQL查询来获取属于指定版块下面的帖子回复。

 

最后一个任务是标识版块下面的最后一条回复

 

在`created_at`字段中排序,获取最新的第一个
帖子对象过滤器topic__board = board )。order_by '-created_at' < QuerySet  [ < Post testing> < Post new  post> < Post hi  there> < Post Lorem  ipsum  dolor  sit  amet ,... > < Post 测试 回复 功能> < Post 这里的测试 > < 帖子大家> < Post test > < 帖子我的第一个话题.. :-) > ] >   
          


#我们可以使用`第()`方法只获取其结果是我们感兴趣的
帖子对象过滤器topic__board = board )。order_by '-created_at' )。first ()
< Post testing >

 

太棒了,现在我们来实现它

 

boards / models.py  (完整代码

 

来自 django.db  导入 模型

一流的 董事会型号型号):
    名称 =  机型CharField max_length = 30 unique = True description  =  models CharField max_length = 100 def  __str __ self ):
        返回 自我名称

    def  get_posts_count self ):
        return  post 对象过滤器topic__board = self count ()

    def  get_last_post self ):
        return  post 对象过滤器topic__board = self order_by '-created_at' 第一个()

 

注意,我们使用的是self,因为这是Board的一个实例方法,所以我们就用这个Board实例来过滤这个QuerySet

 

现在我们可以改进主页的HTML模板来显示这些新的信息

 

模板/ home.html做为

 

{% 延伸 'base.html文件'  %}

{% block  breadcrumb  %} 
  <li  class = “breadcrumb-item active” > Boards </ li> 
{% endblock  %}

{% block  content  %} 
  <table  class = “table” > 
    <thead  class = “thead-inverse” > 
      <tr> 
        <th> Board </ th> 
        <th>帖子</ th> 
        <th>主题</ th > 
        <TH>最后发表</次> 
      </ TR> 
    </ THEAD> 
    <TBODY> 
      {% %} 
        <TR> 
          <TD> 
            <A  HREF = {% URL  'board_topics'  板。pk  %} > {{  board.name }} </A> 
            <小 类= “文本静音d区” > {{  board.description  }} </小> 
          </ TD> 
          <TD  类= “对齐中间人” > 
            {{  board.get_posts_count  } } 
          </ td> 
          <td  class = “align-middle” > 
            {{  board.topics.count  }} 
          </ td> 
          <td  class = “align-middle” > 
            {% with  post = board.get_last_post  %} 
              <small > 
                <a  href = {% URL  'topic_posts'  board.pk post.topic.pk  %} > 
                  通过{{  post.created_by.username  }}{{  post.created_at  }} 
                </A> 
              </小> 
            {% ENDWITH  %} 
          </ TD> 
        </ TR> 
      {% endfor  %} 
    </ tbody> 
  </ table> 
{% endblock  %}

 

现在是这样的效果

 

 

运行测试:

 

python manage.py测试

 

为别名'default'创建测试数据库...
系统检查发现没有问题(0静音)。
.................................................. ..... EEE ......................
================================================== ====================
错误:test_home_url_resolves_home_view(boards.tests.test_view_home.HomeTests)
-------------------------------------------------- --------------------
django.urls.exceptions.NoReverseMatch:反向'topic_posts',找不到参数'(1,'')'。尝试了1种模式:['boards /(?P <pk> \\ d +)/ topics /(?P <topic_pk> \\ d +)/ $']

================================================== ====================
错误:test_home_view_contains_link_to_topics_page(boards.tests.test_view_home.HomeTests)
-------------------------------------------------- --------------------
django.urls.exceptions.NoReverseMatch:反向'topic_posts',找不到参数'(1,'')'。尝试了1种模式:['boards /(?P <pk> \\ d +)/ topics /(?P <topic_pk> \\ d +)/ $']

================================================== ====================
错误:test_home_view_status_code(boards.tests.test_view_home.HomeTests)
-------------------------------------------------- --------------------
django.urls.exceptions.NoReverseMatch:反向'topic_posts',找不到参数'(1,'')'。尝试了1种模式:['boards /(?P <pk> \\ d +)/ topics /(?P <topic_pk> \\ d +)/ $']

-------------------------------------------------- --------------------
在5.663s中进行了80次测试

失败(错误= 3)
销毁别名'default'的测试数据库...

 

看起来好像有问题,如果没有回复的时候程序会崩溃

 

模板/ home.html做为

 

{% = board.get_last_post  %} 
  {% 如果 %} 
    <小> 
      <a  href= “{% URL'topic_posts'board.pk post.topic.pk %}”> 
        通过{{ post.created_by.username }}{{ post.created_at }} </A> </小> {%其他%} <小类= “文本静音” > <EM>没有发布信息。</ em> </ small>         
      
    
    
     
      
    
    
 结束 %}

 

再次运行测试:

 

python manage.py测试

 

为别名'default'创建测试数据库...
系统检查发现没有问题(0静音)。
.................................................. ..............................
-------------------------------------------------- --------------------
在5.630s中进行80次测试

销毁别名'default'的测试数据库...

 

我添加一个没有任何消息的版块,用于检查这个 “空消息”

 

 

现在是时候来改进回复列表页面了。

 

 

现在,我将告诉你另外一种方法来统计回复的数量,用一种更高效的方式

 

和之前一样,首先在Python shell中尝试

 

python manage.py shell

 

来自于 django.db.models  board.models 导入导入 计数
   

董事会 =  董事会对象get name = 'Django' 主题 =  董事会主题order_by '-last_updated' 注释回复= 计数'帖子' ))

主题 主题打印主题回复2 
4 
2 
1

 

这里我们使用annotate ,查询集将即时生成一个新的列,这个新的列,将被翻译成一个属性,通过柯林斯  topic.replies来访问,它包含了指定主题下的回复数

 

我们来做一个小小的修复,因为回复里面不应该包括发起者的帖子

 

topics = board.topics.order_by(' -  last_updated')。annotate(replylies = Count('posts') -  1)

主题中的主题:
    打印(topic.replies)

1
3
1
0

 

很酷,对不对?

 

boards / views.py  (完整代码

 

来自 django.db.models  django.shortcuts 导入 计数
导入get_object_or_404 .models 导入渲染    
   

def  board_topics request pk ):
    board  =  get_object_or_404 Board pk = pk topics  =  board 主题order_by '-last_updated' 注释回复= 计数'信息' -  1 返回 渲染请求'topics.html' { '板' '主题' 主题})

 

templates / topics.html完整代码

 

{% 主题 主题 %} 
  <TR> 
    <TD> <a  href= “{% URL'topic_posts'board.pk topic.pk %}”>{{ topic.subject }} </A> </ TD> <td> {{ topic.starter.username }} </ td> <td> {{ topic.replies }} </ td> <td> 0 </ td> <td> {{ topic.last_updated }} </ td> </ tr> {%endfor %}

 

 

下一步是修复主题的查看次数,但是,现在我们需要添加一个新的字段。

 


 

迁移

 

迁移(迁移)是Django的做网站开发的基本组成部分,它使得我们在演进应用的机型时,它能使得模型文件与数据库保持同步

 

当我们第一次运行命令  python manage.py migrate的时候,Django会抓取所有迁移文件然后生成数据库schema。

 

当Django应用了迁移之后,有一个特殊的表叫做django_migrations,在这个表中,Django注册了所有已经的迁移记录。

 

所以,如果我们重新运行命令:

 

python manage.py migrate

 

执行的操作:
  应用所有迁移:admin,auth,boards,contenttypes,sessions
正在运行迁移:
  无需迁移。

 

Django知道没什么事可做了。

 

现在我们添加在Topic模型中添加一个新的字段:

 

boards / models.py完整代码

 

主题机型型号主题 =  模型CharField max_length = 255 last_updated  =  models DateTimeField auto_now_add = True board  =  models ForeignKey Board related_name = 'topics' starter  =  models ForeignKey的用户related_name= '主题' views  =  models PositiveIntegerField 默认值= 0  < -  这里

    def  __str __ self 返回 自我学科

 

我们添加了一个PositiveIntegerField,因为这个字段将要存储的是页面的浏览量,不可能是一个负数

 

在我们可以使用这个新字段前,我们必须更新数据库架构,执行命令 makemigrations

 

python manage.py makemigrations

“董事会”的迁移:
  板/迁移/ 0003_topic_views.py
    - 为主题添加字段视图

 

makemigrations自动会生成0003_topic_views.py文件,将用于修改数据库(添加一个视图字段)

 

运行现在命令  migrate来应用迁移

 

python manage.py migrate

执行的操作:
  应用所有迁移:admin,auth,boards,contenttypes,sessions
正在运行迁移:
  应用boards.0003_topic_views ...好的

 

现在我们可以用它来追踪指定主题被阅读了多少次

 

boards / views.py  (完整代码

 

django.shortcuts  进口 get_object_or_404 使
.models  导入 主题

def  topic_posts request pk topic_pk ):
    topic  =  get_object_or_404 主题board__pk = pk pk = topic_pk 主题views  + =  1个
    主题save ()
    返回 渲染request 'topic_posts.html' { 'topic' topic })

 

templates / topics.html完整代码

 

{% 主题 主题 %} 
  <TR> 
    <TD> <a  href= “{% URL'topic_posts'board.pk topic.pk %}”>{{ topic.subject }} </A> </ TD> <td> {{ topic.starter.username }} </ td> <td> {{ topic.replies }} </ td> <td> {{ topic.views }} </ td> <! -  here  - - > <td> {{ topic.last_updated }} </ td> </ tr>{%endfor %}

 

现在打开一个主题,刷新页面几次,然后你会看到有页面阅读次数统计了。

 

 


 

总结

 

在这节课中,我们在留言板的基础功能上取得了一些进步,还剩下一些东西等待去实现,比如:编辑帖子,我的账户(更改个人信息)等等之后我们将提供降价语法和列表的分页功能。

 

下一节主要使用基于类的视图来解决这些问题,在之后,我们将学习到如何部署应用程序到网络服务器中去。

 

这部分的完整代码可以访问:https//github.com/sibtc/django-beginners-guide/tree/v0.5-lw

 

posted @ 2018-11-24 17:02  yanhuidj  阅读(362)  评论(0编辑  收藏  举报