django初学总结

激动人心的时刻来了,孩子又学django了。

四部曲:路由、视图、模板、模型

————————————————————————分割线————————————————————————————————

PART 1 请求与响应

(一)django文件框架

1
2
3
4
5
6
7
8
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py

各文件和目录解释:

  • 外层的mysite/目录与Django无关,只是你项目的容器,可以任意重命名。
  • manage.py:一个命令行工具,管理Django的交互脚本。
  • 内层的mysite/目录是真正的项目文件包裹目录,它的名字是你引用内部文件的Python包名,例如:mysite.urls
  • mysite/__init__.py:一个定义包的空文件。
  • mysite/settings.py:项目的配置文件。
  • mysite/urls.py:路由文件,所有的任务都是从这里开始分配,相当于Django驱动站点的目录。
  • mysite/wsgi.py:一个基于WSGI的web服务器进入点,提供底层的网络通信功能,通常不用关心。
  • mysite/asgi.py:一个基于ASGI的web服务器进入点,提供异步的网络通信功能,通常不用关心。

(二)创建APP

在 Django 中,每一个应用(app)都是一个 Python 包,并且遵循着相同的约定。Django 自带一个工具,可以帮你生成应用的基础目录结构。

app应用与project项目的区别:

  • 一个app实现某个具体功能,比如博客、公共档案数据库或者简单的投票系统;
  • 一个project是配置文件和多个app的集合,这些app组合成整个站点;
  • 一个project可以包含多个app;
  • 一个app可以属于多个project!

app的存放位置可以是任何地点,但是通常都将它们放在与manage.py脚本同级的目录下,这样方便导入文件。

(三)编写视图

  1. Include

    include语法相当于多级路由,它把接收到的url地址去除与此项匹配的部分,将剩下的字符串传递给下一级路由urlconf进行判断。在路由的章节,有更加详细的用法指导。

    include的背后是一种即插即用的思想。项目的根路由不关心具体app的路由策略,只管往指定的二级路由转发,实现了应用解耦。app所属的二级路由可以根据自己的需要随意编写,不会和其它的app路由发生冲突。app目录可以放置在任何位置,而不用修改路由。这是软件设计里很常见的一种模式。

    建议:除了admin路由外,尽量给每个app设计自己独立的二级路由。

(四)path()方法

path(route, view**,** kwargs=None**,** name=None**)**

一个路由配置模块就是一个urlpatterns列表,列表的每个元素都是一项path,每一项path都是以path()的形式存在。

path()方法可以接收4个参数,其中前2个是必须的:routeview,以及2个可选的参数:kwargsname

route

view

kwargs

name

PART 2 模型与后台

(一)数据库配置

默认情况,INSTALLED_APPS中会自动包含下列条目,它们都是Django自动生成的:

  • django.contrib.admin:admin管理后台站点
  • django.contrib.auth:身份认证系统
  • django.contrib.contenttypes:内容类型框架
  • django.contrib.sessions:会话框架
  • django.contrib.messages:消息框架
  • django.contrib.staticfiles:静态文件管理框架

(二)创建模型

  1. 模型本质上就是数据库表的布局,再附加一些元数据。

  2. Django通过自定义Python类的形式来定义具体的模型,每个模型的物理存在方式就是一个Python的类Class,每个模型代表数据库中的一张表,每个类的实例代表数据表中的一行数据,类中的每个变量代表数据表中的一列字段。

  3. ORM

(三)启用模型

  1. Make migrations

    通过运行makemigrations命令,Django 会检测你对模型文件的修改,也就是告诉Django你对模型有改动,并且你想把这些改动保存为一个“迁移(migration)”。

    migrations是Django保存模型修改记录的文件,这些文件保存在磁盘上。在例子中,它就是polls/migrations/0001_initial.py

  2. migrate的命令将对数据库执行真正的迁移动作

    1. migrate命令对所有还未实施的迁移记录进行操作,本质上就是将你对模型的修改体现到数据库中具体的表中。
    2. Django通过一张叫做django_migrations的表,记录并跟踪已经实施的migrate动作,通过对比获得哪些迁移尚未提交。(请务必牢记这张表的作用和名称)
  3. sqlmigrate的命令可以展示SQL语句

  4. 修改模型时的操作分三步

    • 在models.py中修改模型;
    • 运行python manage.py makemigrations为改动创建迁移记录文件;
    • 运行python manage.py migrate,将操作同步到数据库。

(四)体验模型自带的API

  1. 调用manage.py参数能将DJANGO_SETTINGS_MODULE环境变量导入,它将自动按照mysite/settings.py中的设置,配置好你的python shell环境,这样,你就可以导入和调用任何你项目内的模块了。

(五)admin后台管理站点

1. 创建管理员用户

2. 启动开发服务器

3. 进入站点

4. 注册投票应用

Part 3:视图和模板

(一)概述

在Django中,网页和其它的一些内容都是通过视图来处理的。视图其实就是一个简单的Python函数(在基于类的视图中称为方法)。Django通过对比请求的URL地址来选择对应的视图,也就是路由。

(二)编写视图

  1. detail(request=<HttpRequest object>, question_id=34)

    函数中的question_id=’34’参数,是由path('<int:question_id>/', views.detail, name='detail')中的<int:question_id>/而来。使用尖括号“捕获”这部分 URL,且以关键字参数的形式发送给视图函数。上述字符串的question_id部分定义了将被用于区分匹配模式的变量名,而int则是一个转换器决定了应该以什么变量类型匹配这部分的 URL 路径。

(三)编写能实际干点活的视图

  1. 这里有个非常重要的问题:在当前视图中的HTML页面是硬编码的。如果你想改变页面的显示内容,就必须修改这里的Python代码。为了解决这个问题,需要使用Django提供的模板系统,解耦视图和模板之间的硬连接。

  2. 项目settings.py文件中的 TEMPLATES配置项描述了 Django 如何载入和渲染模板。默认的设置文件设置了 DjangoTemplates 后端作为模板引擎,并将 APP_DIRS设置成了 True。这一选项将会让 DjangoTemplates 在每个 INSTALLED_APPS 文件夹中寻找 “templates“ 子目录。

  3. 模板命名空间:

    你也许会想,为什么不把模板文件直接放在polls/templates目录下,而是费劲的再建个子目录polls呢?设想这么个情况,有另外一个app,它也有一个名叫index.html的文件,当Django在搜索模板时,有可能就找到它,然后退出搜索,这就命中了错误的目标,不是我们想要的结果。解决这个问题的最好办法就是在templates目录下再建立一个与app同名的子目录,将自己所属的模板都放到里面,从而达到独立命名空间的作用,不会再出现引用错误。

  4. 快捷方式:render()

(四)返回404错误

get_object_or_404()

get_list_or_404()

用来替代filter()函数,当查询列表为空时弹出404错误

(五)使用模版系统

在模板系统中圆点.是万能的魔法师,你可以用它访问对象的属性。在例子{{ question.question_text }}中,Django首先会在question对象中尝试查找一个字典,如果失败,则尝试查找属性,如果再失败,则尝试作为列表的索引进行查询。

(六)删除模板中硬编码的URLs

polls/index.html文件中,还有一部分硬编码存在,也就是href里的“/polls/”部分:

1
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

它对于代码修改非常不利。设想如果你在urls.py文件里修改了路由表达式,那么你所有的模板中对这个url的引用都需要修改,这是无法接受的!

我们前面给urls定义了一个name别名,可以用它来解决这个问题。具体代码如下:

1
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

(七)URL names的命名空间

Django是如何区分这些app之间的URL name

举个例子,polls 应用有 detail 视图,可能另一个博客应用也有同名的视图。Django 如何知道 {% url %} 标签到底对应哪一个应用的 URL 呢?

答案是使用URLconf的命名空间。可以在polls/urls.py文件的开头部分,添加一个app_name的变量来指定该应用的命名空间:

1
app_name = 'polls'   # 重点是这一行

polls/templates/polls/index.html中的代码改为:

1
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

Part 4:表单和类视图

(一)表单form

  1. 例子:
1
2
3
4
5
6
7
8
9
10
11
12
<h1>{{ question.question_text }}</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
<input type="submit" value="Vote">
</form>

简要说明:

  • 上面的模板显示一系列单选按钮,按钮的值是选项的ID,按钮的名字是字符串”choice”。这意味着,当你选择了其中某个按钮,并提交表单,一个包含数据choice=#的POST请求将被发送到指定的url,#是被选择的选项的ID。这就是HTML表单的基本概念。
  • 如果你有一定的前端开发基础,那么form标签的action属性和method属性你应该很清楚它们的含义,action表示你要发送的目的url,method表示提交数据的方式,一般分post和get。
  • forloop.counter是Django模板系统专门提供的一个变量,用来表示你当前循环的次数,一般用来给循环项目添加有序数标。
  • 由于我们发送了一个POST请求,就必须考虑一个跨站请求伪造的安全问题,简称CSRF(具体含义请百度)。Django为你提供了一个简单的方法来避免这个困扰,那就是在form表单内添加一条{% csrf_token %}标签,标签名不可更改,固定格式,位置任意,只要是在form表单内。这个方法对form表单的提交方式方便好使,但如果是用ajax的方式提交数据,那么就不能用这个方法了。
  1. 有些新的东西,我们要解释一下:
    • request.POST是一个类似字典的对象,允许你通过键名访问提交的数据。本例中,request.POST[’choice’]返回被选择选项的ID,并且值的类型永远是string字符串,哪怕它看起来像数字!同样的,你也可以用类似的手段获取GET请求发送过来的数据,一个道理。
    • request.POST[’choice’]有可能触发一个KeyError异常,如果你的POST数据里没有提供choice键值,在这种情况下,上面的代码会返回表单页面并给出错误提示。
    • 在选择计数器加一后,返回的是一个HttpResponseRedirect而不是先前我们常用的HttpResponseHttpResponseRedirect需要一个参数:重定向的URL。这里有一个建议,当你成功处理POST数据后,应当保持一个良好的习惯,始终返回一个HttpResponseRedirect。这不仅仅是对Django而言,它是一个良好的WEB开发习惯。
    • 我们在上面HttpResponseRedirect的构造器中使用了一个reverse()函数。它能帮助我们避免在视图函数中硬编码URL。它首先需要一个我们在URLconf中指定的name,然后是传递的数据。例如'/polls/3/results/',其中的3是某个question.id的值。重定向后将进入polls:results对应的视图,并将question.id传递给它。白话来讲,就是把活扔给另外一个路由对应的视图去干。