Django编写第一个开源项目

写在前面:之前跟了实验室的“缘游”项目,也是使用 django 后台开发的。但是由于这个项目不是从头搭建,是在之前的项目的基础上面改出来的。所以呢对于整体流程的认识并没有那么清楚。今天根据 django 的官方文档,来搭建一个投票系统,期望对整体的流程有一定的认识。

另外,看了看之前写的 django 笔记
(来自自强学堂),觉得写的太散了,所以,要学一个新的技术或者框架,最好的方法还是动手写一个开源项目。


Notice

注意,首先要对 django 的版本选择有一定的认识。对于不同的版本,对应不同的 python 版本对应说明


创建项目

在想要创建的位置用 命令行 进行创建。

django-admin startproject mysite 

可以看到在相应的目录下就创建了django工程。具体树结构如下图。


工程树
  • 最外层的 mysite/ 根目录只是项目的容器,django 是不关心这个名字的,所以只要不产生命名冲突,这里可以随便给
  • manage.py: 命令行工具。在之前做“缘游”的时候你也发现了,基本上都是manage.py 然后一些列操作。这个工具具体的细节功能可以参考官方文档。
  • mysite/ 目录包含了项目,是纯 python 包,引用该包内的东西需要包含项目路径,mysite.urls
  • mysite/init.py:空文件,告诉 python 这个目录应该是一个纯 python 包。
  • mysite/settings.py:配置文件。
  • mysite/urls.py: URL声明,就像是网站“目录”
  • mysite/wsgi.py:项目运行在 WSGI 兼容的 Web 服务器上的入口。(这个不懂是什么)

用于开发的简易服务器

官方文档在这一节的意思是告诉我们,创建好项目之后,就可以启动 django 自带的建议服务器了。

 python manage.py runserver  [可以指定ip和端口]

官方在这里说明了,这个轻量级的 Web 服务器只是为了开发用的。如果是用于生产等,还是要配专业服务器 apache 什么的。


创建投票应用

python manage.py startapp polls

创建 App。这时候就可以看到创建了app。
这里要理解 app 和 project 之间的关系,一个 pro 下面可以有多个 app。


app 树结构

这里面包含了投票应用的全部内容。


编写第一个视图

这里在 polls 应用下的 view.py 视图中,编写第一个函数。首先,写入一个 捕获 request 请求并返回数据的方法,命名为 index;然后,在 app 下创建一个 url.py 脚本来指明我们 app 的 URLconf,将这个方法映射到相应的 url 规则;最后,要在 根URLconf 的 url.py 文件下指定我们创建的 app 的 URLconf ,这里使用 include() 方法。

这里官方文档说明为什么要使用 include() 方法指明 app 中的 URLconf。
因为对于 投票应用来说,应该有自己的 URLconf,但是其实 这个 polls/url.py 所写的 URLconf,应该在其他的应用下也可以使用,处于这个考虑,才有了 include() 方法。

最终,运行服务器,可以发现这里的新写的视图产生了作用。
访问 localhost:8000/polls 即可。


数据库配置

这里在 mysite/settings.py 下面进行系列设置,这里我们采用 mysql 数据库,填入 用户名、密码等。

  • migrate
    这里有一些 django 自带应用在 settings 中是默认开启的,而这些应用是需要一些数据表的,所以在这之前要在数据库中创建一些相应的表单,所以要执行:
python manage.py migrate

这里的 migrate 命令就是说同步数据库和当前应用二者的状态(因为这里的默认开启的应用在数据库中是没有表单的,所以执行 migrate操作之后会在相应的 数据库中 建立表单,如下图)


migrate操作后自带应用的表单
  • makemigrations:
    这个指令是用来生成迁移代码的。
    大白话讲一下:每次改动 models.py之后,先要makemigrations,然后 migrate才能更改数据库中的表单。

创建模型

其实在做一个 Web 应用时候,第一步应该是创建模型,就是说表单等数据库的设计。
我们在这个投票应用中,创建两个模型:Question & Choice。


激活模型

就是说我们已经更改过模型了,下一步就是让模型对应的数据库表单生效,也就是我们上面说过的 makemigrations & migrate两步

这里官方文档中还说了把 polls 添加进 工程设置文件内的 已安装app 中。
如果没有安装,就不会检测到 polls 中的任何变化。
个人理解,这些命令都是以 project 的层面执行的。


初试API

这里官方文档 讲了 shell 接口

python manage.py shell

在这个接口下可以实现很多直接对数据库的操作。其实这些操作在可视化数据库中更加便捷。这里只需要知道有这么个功能就好了。


介绍 Django 管理页面

这里讲了一下创建一个超级用户来进入后台管理界面。之后进入管理界面。

不知道为什么半天没有登陆进去,但是最后啥也没变就登录进去了。奇怪。浪费了很多时间。


向管理页面中加入投票应用

这时候在任何地方都看不到我们的投票应用。
所以这个时候需要告诉管理,问题 Question 对象需要被管理。 在 polls/admin.py 中进行编辑。

from .models import Question

admin.site.register(Question) 

之后就会看到这个 Question 类被添加到了后天管理页面。


添加 Question 的管理页面

点击Question之后可以看到之前生成的 Question 对象,点击对象可以进行编辑,这里编辑的 表单是 Django 根据 Question 模型自动生成的。


真正开始编写投票应用

前面的可以说都是准备工作,从这里开始我们才真正的开始。
首先需要明白视图的概念:“一类具有相同功能和模板的网页的集合”。在投票应用中,我们需要以下几个视图:

  • 问题索引页:展示最近的几个投票问题
  • 问题详情页:问题和相应的选项
  • 问题结果页:投票的结果
  • 投票处理器:响应投票操作

编写更多的视图

现在开始编写上面对应的视图并且添加URL映射。
这里文档中让我们编写了跟上面页面对应的4个视图(包括最最开始的 index 视图),然后添加入 URLconf。
这里添加了一个 ‘<int:question_id>’的匹配规则。


写一个真正有用的视图

Django 中要求一个视图必须要 返回一个 HttpResponse 、或者抛出一个异常。
这里更改 index 这个view函数,来显示最近发布的5个问题。
之后会发现,index 页面的设计是写死在视图函数的代码里的。想要更改设计,就要采用 templates。
在 poll/ 目录下新建 templates 目录,在 templates目录下建立 polls 目录(为了防止不同app之间重名的问题),然后建立 index.html 脚本。在脚本中写入 html 样式代码,然后再回到视图中的 index 函数中,通过将模板内的变量映射为 python 对象 来使用模板。

流程为: 载入模板,填充上下文,返回由模板生成的 HttpResponse 对象。
同时 Django 还提供了一个快捷函数 render() 来完成这个流程,就不需要 HttpResponse了。

这时候 索引页面 index 已经有了一点样式了


抛出404错误

这里官方文档给出了捕获404异常从而给出 debug 的方法,同时也给出了 get_object_or_404() 这个快捷函数。同样可以起到 try: except 的作用。


去除模板中的硬编码URL

这里是说 用 {% url %} 标签来通过 name 选项寻找 url。这样做可以在产生 url 变更时只需要在URLconf 中做相应的更改,只要不改变这里的名字,模板中就可以找到URL。


为URL名称添加命名空间

接上面,这里说到了重复名称的问题。为了区分不同app相同的 url 名。文档给出了在 URLconf中加上 app_name 设置命名空间的方法。


编写一个简单的表单

文档在这里给出了 detials.html 页面的表单编写方法。并指出了 但凡是调用 post 方法都是要更改后台数据库,所以在 Django 中规定了 所有 Post 表单都要使用 {%csrf_token%}。 Post 的 view 函数也一样。

这里的 csrf 是一个验证的方式。
csrf 机制要求,发送 post/put/delete 请求的时候,是先以 get 方式发起一个请求,服务器分配一个随机字符串给客户端,之后客户端携带这个 字符串再 发送请求。从而验证身份是否合法。

然后文档给出了 投票提交页面的视图编写方法。文档指出,在处理 post 信息之后一定要重定向一个 URL,不然当用户点击返回按钮会重复 post.
这里重新写了 vote() 视图等


使用通用视图:代码还是少点好

可以看到,我们之前 detial() results() 和 index() 视图实现的功能都很类似:根据 URL 中的参数从数据库获取数据、载入模板、返回渲染后的模板。针对这种问题,django 提供一种叫做“通用视图”的系统。
这里以投票 vote() 视图为例子,将其转换成使用通用视图系统:

  1. 转换 URLconf
  2. 删除旧的、不需要的视图
  3. 基于 django 通用视图 引入新的 视图
改良 URLconf

这里首先在 URLconf 中进行更改,使用通用视图,首先要在 URLconf 中将视图方法更改成为通用视图方法。


URLconf
修改视图为通用视图方法

然后需要更改视图方法的代码( 这里vote() 视图方法不变的 )。

# 使用通用视图之前
def index(request):
    late_list = Question.objects.order_by('-publish_date')[:5]
    return render(request, 'polls/index.html', {'latest_ques_list': late_list})


def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})


def results(request,question_id):
    question = get_object_or_404(Question, pk = question_id)
    return render(request, 'polls/results.html', {'question': question})

# 使用通用视图后
class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        return Question.objects.order_by('-publish_date')[:5]


class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'


class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'

可以看到呢,这里使用了两个通用视图 generic.ListView & generic.DetailView 视图。

  • 每个通用视图需要知道它作用于哪个模型。由 model 属性提供
  • DetailView 视图期望从 URL 中捕获 名为“pk”的主键值,所以把 URLconf 中的 question_id 该为 pk。

对于通用视图,其有默认的模板(如果我们不指定的话)。比如 DetailView 默认使用 <app name>/<model name>_detail.html 模板。而这里我们用 template_name 来指定其使用的模板。

这里所谓的通用视图能够减少代码量体现在
看使用通用视图之前的代码,使用模板的时候(render()方法)都是需要提供 question 或者 latest_question_list 变量的context,而这些 变量是 需要我们手动写代码去获取的。
但是,在通用视图中,只需要我们提供 model 名,比如这里的 DetailView 中 写的 model = Question , 然后Django 就会自动的生成一个合适名字的 context。这就是所谓的 减少重复代码 / 代码少一点
ListView 这个通用视图和 DetailView有点不同,这里我们除了 template_name 还提供了 context_object_name,因为这里 Django 自动生成的context 变量是 question_list,而我们想用别的名字,于是提供了一个覆盖指令而已。注意这里 ListView 的model 名是写在 get_queryset 中的(都是规定好的)。

这里的通用视图,就是说Django其实已经提供好了一整套流程,只需要我们填入 model 和 templates 等,不需要再手动相关变量名了。

到了这里我们已经把整个投票系统搭建完成了。
下面的文档都是教我们怎么样来进行测试和 debug 了。


自动化测试

这里文档介绍了自动化测试对于整个项目的重要意义。并且指出,如果是一个用 django 找工作的人,他必须很会写测试。


开始写我们的第一个测试

首先有一个bug

这里在之前开发的时候,其实是有一个 bug 的:Question.was_published_recently() 方法对一天前发布的问题是返回 True 的。 但是实际上来说,对于发布在未来时间的 对象,也是返回 True 的。这就是一个现有的 bug。
可以在 shell 命令行里面试一下就会发现了。

创建一个测试来 暴露这个 bug

其实刚刚我们刚刚在 shell 里面进行这个 “试一下” 的过程其实就是在 测试,不过这个是手动的测试,而我们这里讲的是自动的测试!
我们在 polls/tests.py 脚本下编写自动化测试的代码。
这里生成一个 TestCase 的子类。子类中定义一个 测试方法,在测试方法中进行相关的创建和测试操作,最终用一个 self.assertIs(方法名,期待输出) 来决定这次测试的正确性。
最终在命令行中运行测试: python manage.py test polls


test 结果

可以看到测试过程是:

  • python manage.py test polls 这个命令来寻找相应的 test.py 中的代码
  • 它找到了 django.test.TestCase 的一个子类(即我们创建的子类)
  • 创建了一个测试 database
  • 在类中寻找测试方法,以test开头的方法
  • 根据方法,创建了一个 30天后的 Question 实例
  • 用 assertIs() 方法,发现was_published_rencently() 方法反回了 True,但是我们期望返回的是 False
    所以测试系统通知我们测试失败了。以及失败的地方。

修复这个 bug

修复也很简单,就是说只让过去的时间满足,而未来的时间都一律返回 False。
在models.py 中将原始的 was_pub_renc() 方法作相应的更改就行。
然后重新运行测试。可以看到测试成功了。


测试成功

更全面的测试

很多时候在改正一个 bug 的时候会有另外的 bug 出现。所以更全面的测试是很有必要的。所以针对过去,现在,将来发布时间的实例都应该有测试。
同样在 polls/tests.py 中将其他两个测试方法也写出来。运行测试,发现通过测试。


测试视图

其实我们的视图也是有问题的,因为对于所有的问题,都会在 index 里面 发布。但是应该是 未来发布的问题,现在暂时不显示。所以这个也是一个 bug。


django 测试工具 Client

这里介绍了 Client 这个测试工具,其可以模拟用户,来跟视图层的代码进行交互。


改善视图代码

修复上面说的 会显示未来投票的问题。
之前我们使用了 ListView 这个通用视图类。这里我们需要改进一下 get_queryset() 方法,让她可以做一下比较来决定是否显示这个 question 实例。
这里只需要在 get_queryset() 方法中加入一个 filter 步骤,

# 这里的 lte 代表 less than or equals 小于等于的意思,就是说发布时间在现在之前。
return Question.objects.filter(publish_date__lte=timezone.now()).order_by(......)

测试新视图

这里增加对于改进后的新视图的测试方法。通过上面提到过的 client() 方法进行测试。这里给出一个例子

 def test_future_question_and_past_question(self):
        """都有,只显示过去的问题"""
        create_question(question_text="Future question.", days=30)
        create_question(question_text="Past question.", days=-30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(response.context['latest_question_list'], ['<Question: Past question.>'])

写完所有的测试代码


测试 DetailView

这里还是有一个问题,就算是未来的问题不会出现,但是如果别人猜到了问题的 ID,还是可以访问。所以这里还是要在 DetailView 中增加一些约束。跟 ListView 通用视图一样,用 get_queryset() 方法来 filter。
之后还可以测试一下 DetailView, 写相关的测试类


测试越多越好

这里有几个点:

  • 不要怕测试多,越多越好
  • 每一个 model 或者 View,都应该有独立的 test class
  • 每个测试方法都有自己独立的测试环境,不用我们来手动的测试,这就是 django 高效的原因
  • 测试方法的名字要描述其功能,以 test 开始

到这里基本的测试就讲完了。下面看一下静态资源文件管理


静态文件

服务端除了生成 HTML 之外,还需要一些额外的文件--图片、脚本、样式表等 来帮助渲染网页。这些东西统称为 静态文件
这里对于静态文件存放的位置也是很有讲究的。
这里我们采用 django.contrib.staticfiles 来管理各个应用的静态文件。


自定义应用的界面和风格

首先在 polls 应用下创建 static 的路径,用于存放静态文件资源。然后在 static 下创立 polls 路径,在路径下创立 style.css 的样式文件,这跟之前创建 templates 一样,需要规定 应用名以方便区分。
然后在 样式表文件中写入基本的样式代码,在index.html 中引入这个样式表。重启服务器,发现就会有变化,连接变成了 green


添加一个背景图

创建存放图像的目录, polls/static/polls/images/background.gif

这里由于做的时候把图片没有放在 imgas 目录下而是放在了其同级的地方,浪费了一些时间。真是一个低级的错误。

到这里这些基础静态资源文件的就很粗略的介绍了一下。

下面文档介绍了如何自定义 Django 自动生成后台网页的过程。


自定义后台表单

之前我们 通过 admin.site.register(Question) 来注册了 Question 模型。重新登录。

注意这里说之前提到过的 admin 的登录问题,只存在于 chrome 浏览器中,换成 IE 就没有问题了。
这里给出了在 admin.py 中通过自定义后台表单来更改后台表单的样式,如下图。


自定义后台表单样式

添加关联的对象

这里只显示了 Question 但是没有显示 Choice.
这里说了一种方法,将 Choice 关联到 Question 页面中。就可以同时在问题页中看到选项。


自定义后台更改列表

这里可以通过一系列的django内置的选项功能添加,来为 后台表单添加各种 过滤块,搜索框,显示方式等等。


自定义后台列表

自定义后台界面和风格

这里也提到了 templates 模板的 问题。文档指出,我们将所有的模板文件放在一个地方(templates文件夹),这里后台的模板放在 mysite/templates,而polls之前我们用的 模板 放在 ./polls/templates 下。

大概就是这样来自定义后台界面。

整个Django的流程大概就是这样。然后自己还是要从项目中来学习。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343