Django项目实战
Django
从今天开始进行Django项目的实战系列更新,希望这系列笔记,能给帮助到和我一样正在学习路上的你和我!
Django中请求处理流程
因为之前没有Web开发的经验,本身也是转行到计算机这个领域中来,之前也折腾过很长的时间,视频教程、博客、国内的书籍等等,各有利弊,所以这几天看了一些国外的书籍,国外的书籍相比较于国内的更新速度更快,这是很大的一个优点,再就是觉得还是看书比较的系统掌握像Django这样的框架,高屋建瓴的了解一些思想、逻辑会有助于理解框架的代码细节。
这里先回忆昨天学习的成果。相比较于Java Spring全家桶的开发工作来说,Django的开发工作明显较少,很多事情Django都已经帮助你完成了,因此开发速度快速,但是对于大型服务系统来说,是不是鲁棒性会差一点??(这里还不是很懂,请大家指教)

1、 用户通过浏览器请求一个页面(上传数据、点击访问等等)
2、请求到达Request Middlewares,中间件对request做一些预处理或者直接response请求
3、URLConf通过urls.py文件和请求的URL找到相应的View()
4、View Middlewares被访问,它同样可以对request做一些处理或者直接返回response
5、调用View中的函数
6、View中的方法可以选择性的通过Models访问底层的数据
7、所有的Model-to-DB的交互都是通过manager(QuerySet)完成的
8、如果需要,Views可以使用一个特殊的Context
9、Context被传给Template用来生成页面
a.Template使用Filters和Tags去渲染输出
b.输出被返回到View
c.HTTPResponse被发送到Response Middlewares
d.任何Response Middlewares都可以丰富response或者返回一个完全不同的response
e.Response返回到浏览器,呈现给用户
可以从流程图中看出Middleware(中间件,包括request、view、exception、response)发挥了很重要的作用,这里暂未涉及,先不讨论。
参考:https://www.cnblogs.com/lyq-biu/p/9626234.html、https://segmentfault.com/a/1190000002399134
URLConf(URL映射):就像是Django所支撑网站的目录。本质是URL模式以及要为该URL模式调用的视图函数(view)之间的映射表。就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。例如,当用户访问/foo时,调用视图函数foo_view(),视图函数存在于views.py中。
在执行创建项目的时候,会自动创建一个URLConf(就是项目的urls.py)。在同时创建的settings.py文件中,还有一个变量ROOT_URLCONF,其变量的值就是根(项目文件夹下的)URLconf的模块名。默认值是urls.py文件的模块名。
###Django application组织原则
实际上,每个 Django App(Application) 的组织结构符合 Django 的 MTV 法则——Model (模型)+ Template (模板)+ View (视图)。MTV 与大家比较熟悉的 MVC 在思想上非常相似,但是命名有比较大的出入,如下表所示:

区别:
MVC中的View的目的是「呈现哪一个数据」,而MTV的View的目的是「数据如何呈现」。
也就是把MVC中的View分成了视图(展现哪些数据) 和 模板(如何展现) 2个部分,而Contorller这个要素由框架自己来实现了,我们需要做的就是把(带正则表达式的)URL对应到视图就可以了,通过这样的URL配置,系统将一个请求发送到一个合适的视图。
https://www.cnblogs.com/wqzn/p/10009217.html、https://blog.csdn.net/YangHeng816/article/details/52213983
理解视图(views):业务逻辑的编写
创建Application之后,下一步就是编写业务逻辑和接入路由,完成访问了。
如前所述Django中的Project目录和application目录下分别有urls.py文件
分别负责不同的功能:
一个是全局路由,一个是子应用路由组成。简单来说,根据用户输入的 URL,全局路由表进行匹配并选择正确的子应用路由,再由所选择的子应用路由匹配并选择正确的视图( View )。整个流程如下图所示:

例如,用户访问 example.com/apple/buy,然后全局路由根据 /apple/buy 先选择 apple 的路由表,再从 apple 路由表中根据 /buy 选择 /buy 路由,然后执行 /buy 对应的 BuyView 视图,返回给用户结果。
编写第一个视图
首先打开 news/views.py ,写一个简单的视图函数,返回一串 Hello World!
from django.http import HttpResponse
def index(request):
return HttpResponse('Hello World!')
上面这个 index 函数可以说是一个最简单的视图函数了,实际大部分应用的视图要比这复杂得多。Django 同时支持基于函数的视图( FBV,Function-based View )和基于类的视图( CBV,Class-based View ),这里显然是 FBV,接收一个 request 请求对象作为参数,返回了一个 HttpResponse 对象。
将视图接入路由(view.py–>urils.py)
接着,我们要让路由系统能够访问到刚才写好的视图函数。因此先实现子应用news 的路由表,创建 news/urls.py 文件如下:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
每一个 Django 路由表模块( urls.py )中都约定必须包含一个 urlpatterns 列表用来存放路由映射表。列表中每个元素是一个用 django.urls.path 函数封装好的路由映射,通常接收以下三个参数:
- route:必须,即实际的访问路由,空字符串等于 /,即空路由
- view:必须,该路由将要访问的视图(重要参数)
- name:可选,该路由的名称,方便后续在模板中使用
我们将刚刚写好的 news 路由表接入全局路由表。由于我们希望新闻能够展示在首页(即通过 / 就能访问,无需/news),因此 news 应用路由在全局路由中的 URL 是一个空字符串。在 django_news/urls.py 中修改如下:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
## route为空字符串
path('', include('news.urls')),
]
这里使用django.urls.include 函数将 news 应用的路由表接入进来,并且 include 函数的参数是路由模块路径的字符串news.urls,省去了手动 import 的麻烦(写法并不规范,不要学习此示例写法。这里记录的目的是为了学习Django流程化的开发思路)。
注意
添加路由规则时顺序是很重要的,因为在尝试匹配时会按照从上到下的顺序进行,因此应该把最模糊的路由(即空路由)放在最下面。
现在已经完成了一个非常简单的demo开发工作。
理解模板:网页前端的实现
想想前面我们做了什么?我们首先定义了view(视图)、定义了urls.py(主路由和子应用路由),完成了一次访问配置。接下来,我们将实现一个 Django 模板作为网页前端,从而给用户呈现更丰富的内容。
Django模板语言基础
表达式插值
最常用的语法,没有之一。通过在一对花括号{{}} 放入一个表达式,就能够在视图(views)中传入表达式中变量的内容,并最终渲染成包含变量具体内容的 HTML 代码。需要注意的是,所支持的表达式仅支持以下形式(可以自由组合):
<!-- 单个变量 -->
{{ variable }}
<!-- 获取字典的键或对象的属性 -->
{{ dict.key }}
{{ object.attribute }}
<!-- 获取列表中的某个元素 -->
{{ list.0 }}
例如,模板这样写:
<h1>{{ name }}</h1>
<p>{{ news.title }}</p>
<p>{{ news.visitors.0 }}</p>
如果我们在视图中(view)传入以下上下文字典( Context Dictionary ):
{
'name': 'Tuture',
'news': {
'title': 'Hello World',
'visitors': ['Tom', 'Marc'],
}
}
那么最终渲染成的HTML代码就是:
<h1>Tuture</h1>
<p>Hello World</p>
<p>Tom</p>
条件语句
条件语句的定义如下:
{% if is_true %}
<h1>It is true!</h1>
{% else %}
<h1>It is false!</h1>
{% endif %}
如果变量 is_true 为真,那么最终渲染出来的就是 <h1>It is true!</h1>,否则就是 <h1>It is false!</h1>。注意:整个条件语句必须以 {% endif %} 结束,并且 {% else %} 是可选的。
循环语句
https://v2ex.com/t/663675
理解模型(model):和数据库的联动
- 由于高度解耦的设计,可轻松切换各种关系型数据库(默认的 SQLite,可选 MySQL 、PostgreSQL 、Oracle 等等)
- 强大的 ORM ( Object Relation Mapping,对象关系映射)模块,使得用 Python 操作数据库非常轻松,免去了使用 SQL 的麻烦
- 优秀的数据库迁移机制( Migration ),修改数据模式( Schema )比较方便,能够适应不断变化的功能需求
理解ORM
简单来说,ORM 能够将面向对象的代码转换成相应的 SQL 语句,从而对数据库进行操作。SQL 是用于访问和处理数据库的标准的计算机语言,但是直接写在代码里面显然难以维护,而且对使用者的要求也非常高,写的糟糕的 SQL 代码查询效率非常低下。因此,使用设计良好的 ORM 不仅让代码可读性更好,也能帮助开发者进行查询优化,节省不少力气。
优点:1、操作简单,使用python代码即可维护数据库
缺点:ORM模式笨重,不支持非关系型数据库
理解数据库迁移
数据库迁移是指将用 Django 定义的模型转换成 SQL 代码(即迁移文件),并在数据库中进行建表操作(或更新表)。看下面这张图就知道了:

一般的开发流程就是这样:
1、用 Django 定义了一个新的数据模型
2、用 makemigrations 命令创建迁移文件(存储在子应用的 migrations 目录里面)
3、用 migrate 命令执行迁移
4、在开发中发现第 1 步中定义的模型不完善,更新数据模型
5、跳转到第 2 步,反复循环
实现第一个数据模型
终于到了动手的环节。我们首先定义数据模型 Post ,包括标题 title 字段和 content 字段,代码如下:
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
def __str__(self):
return self.title
随后就是创建迁移文件并完成迁移(makemigrations&&migrate)。
配置后台管理接口
还需要在admin.py中完成application的注册。
from django.contrib import admin
from .models import Post
admin.site.register(Post)
这是一篇博客介绍,从这里主要学习Django流程上的开发思路,可以看出主要是model.py、views.py部分比较复杂一些。
URL调度过程实例
代码示例
#1. mysite.urls.py
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r"^$","mysite.views.index"),
url(r"^about/","mysite.views.about"),
url(r"^people/",include(people.urls)),
url(r"^contact/","mysite.views.contact"),
url(r"^update/","mysite.views.update"),
)
#2.people.urls.py
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r"^daoluan/","people.views.daoluan"),
url(r"^sam/","people.views.sam"),
url(r"^jenny/","people.views.jenny"),
)
#3.people.views.py
def daoluan(request):
return HttpResponse("hello")
当我们访问http://example.com/people/daoluan/的时候URL dispatcher的调度过程(蓝色部分):

1.BaseHandler.get_response()中根据settings.py中的ROOT_URLConf设置选项构造RegexURLResolver对象,并且调用RegexURLResolver.resolve("/people/daoluan/")启动解析,其中RegexURLResolver.regex="^\",也就是说它会过滤"",url变为"people/daoluan/";
2.resolve()函数中调用RegexURLResolver.url_patterns(),加载了所有的匹配信息如下(与图中一样):
- (类型)RegexURLPattern(正则表达式) [^$]
- RegexURLPattern [^about/]
- RegexURLResolver [^people/]
- RegexURLPattern [^contact/]
- RegexURLPattern [^update/]
语句 for pattern in self.url_patterns: 开始依次匹配. 第一个因为是 RegexURLPattern 对象, 调用 resolve() 为 RegexURLPattern.resolve(): 它直接用 [\^$] 去匹配 “people/daoluan/”, 结果当然是不匹配.
3. 下一个pattern过程同上
4. 第三个 pattern 因为是 RegexURLResolver 对象, 所以 resolve() 调用的是 RegexURLResolver.resolve(), 而非上面两个例子中的 RegexURLPattern.resolve(). 因为第三个 pattern.regex = "^people/", 所以会将 "people/daoluan/" 过滤为 "daoluan/". pattern.resolve() 中会调用 RegexURLResolver.url_patterns(), 加载了所有的匹配信息如下(和图中一样):
- RegexURLPattern [^daoluan$]
- RegexURLPattern [^sam$]
- RegexURLPattern [^jenny$]
语句 for pattern in self.url_patterns: 开始依次匹配. 第一个就中, 过程和刚开始的过程一样. 因此构造 ResolverMatch 对象返回. 于是 BaseHandler.get_response() 就顺利得到 ResolverMatch 对象, 其中记录了有用的信息. 在 BaseHandler.get_response() 中有足够的信息让你知道开发人员在 views.py 中定义的函数是 def daoluan(request): 在什么时候调用的:
# BaseHandler.get_response() 的定义
# 处理请求的函数, 并返回 response
def get_response(self, request):
......
# 实例化 RegexURLResolver, 暂且将其理解为一个 url 的匹配处理器, 下节展开
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
......
# 调用 RegexURLResolver.resolve(), 可以理解为启动匹配的函数; 返回 ResolverMatch 实例
resolver_match = resolver.resolve(request.path_info)
......
# resolver_match 对象中存储了有用的信息, 譬如 callback 就是我们在 views.py 中定义的函数.
callback, callback_args, callback_kwargs = resolver_match
......
# 这里调用的是真正的处理函数, 我们一般在 view.py 中定义这些函数
response = callback(request, *callback_args, **callback_kwargs)
......
return response
总结:url 调度器主要 RegexURLResolver, RegexURLPattern, ResolverMatch 和三个辅助函数 url(), include(), patterns() 完成. 可以发现, url 的调度顺序是根据 urls.py 中的声明顺序决定的, 意即遍历一张表而已, 有没有办法提高查找的效率?(https://www.cnblogs.com/daoluanxiaozi/p/3323352.html、http://www.nowamagic.net/academy/detail/13281030#)
再就是Template(模板),Django中的模板不支持直接写Python代码,只支持写template filter、template flag等。可以使用jinja2进行扩展。
问题
1.project下面有一个urls.py,每个application下面也有一个urls.py,这里衍生出一个问题,两个urls.py有什么作用?分别是做什么的?
参见:https://stackoverflow.com/questions/58554737/what-are-the-differences-between-urls-py-in-projects-folder-and-app-folder-in-d

简单来说就是:project下的urls.py是"基础"路由,例如访问0.0.0.0/blog/1.html时,会先把请求交给blog application的路由,再根据1.html在blog application的urls.py下找打特定的请求地址。
# project's urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('myapp/', include("myapp.urls")) # requests on a route starting with "myapp/" will be forwarded to "myapp.urls"
]
# myapp's urls.py
from django.urls import path
from . import views
app_name = "myapp"
urlpatterns = [
path("", views.index, name="index"),
path("contact/", views.contact, name="contact")
]

例如,如果我请求“localhost:8000/myapp/contact”,您的project的urls.py将检测到它必须将该请求转发到您的application–>myapp,而应用程序myapp将调用它的view .contact。
开始第一个Demo
使用下面的命令创建一个名为mysite的项目
django-admin startproject mysite
可以看到以下形式的目录结构:

这里的manage.py是一个用于与项目交互的命令行实用程序。它是对django-admin.py工具的简单封装。不需要对这个文件进行任何的修改。
- mysite/:
- _init_.py:一个空文件,用来让Python知道把mysite目录作为Python的一个module。
- asgi.py:这是作为ASGI运行项目的配置,ASGI是用于异步web服务器和应用程序的新兴标准。
- settings.py:包含了初始默认的项目设置和配置
- urls.py:这是URL patterns所在的位置。这里定义的每一个URL都映射到一个view。
- wsgi.py:这是作为Web Server Gateway interface(WSGI)应用程序运行项目的配置。
生成的settings.py文件包含项目设置,包括一个使用SQLite3数据库的基本配置和一个名为INSTALLED_APPS的列表,其中包含默认添加到项目中的常见Django应用程序。我们将在稍后的Project settings 部分介绍这些应用程序。
这篇博客介绍了Django项目实战的第一部分,主要讲解Django的请求处理流程,包括视图、模板和模型的理解。详细阐述了Django中URL调度、视图函数、模板语言基础和数据库模型的操作,同时提供了实际的代码示例。文章强调了Django的MTV模式和中间件在处理请求中的作用,以及URLConf和路由配置的重要性。

716

被折叠的 条评论
为什么被折叠?



