趁着一个项目学了学django框架,总的感觉是大而全,只接触到皮毛,慢慢学习吧。
django项目的创建
新建项目
新建一个django项目:
1 | django-admin startproject django_project |
运行成功后会有如下目录:
1 | django_project |
我们会发现执行命令后,新建了一个 django_project
目录,其中还有一个 django_project
目录
django_project
: 项目的容器。manage.py
: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。__init__.py
: 一个空文件,告诉 Python 该目录是一个 Python 包。asgi.py
: 一个 ASGI 兼容的 Web 服务器的入口,以便运行你的项目。settings.py
: 该 Django 项目的设置/配置。urls.py
: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站”目录”。wsgi.py
: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。
启动开发服务器
可以运行python manage.py runserver 0.0.0.0:8000
命令来启动django项目
创建app
在 Django 中,每一个应用(app)都是一个 Python 包,并且遵循着相同的约定。Django 自带一个工具,可以帮你生成应用的基础目录结构。
app应用与project项目的区别:
- 一个app实现某个功能,比如博客、公共档案数据库或者简单的投票系统;
- 一个project是配置文件和多个app的集合,这些app组合成整个站点;
- 一个project可以包含多个app;
- 一个app可以属于多个project!
- app的存放位置可以是任何地点,但是通常都将它们放在与manage.py脚本同级的目录下,这样方便导入文件。
进入mysite项目根目录,输入下述命令:
1 | python manage.py startapp app |
系统会自动生成 app应用的目录,其结构如下:
1 | app/ |
创建了之后还需要将app添加到setting.py
中INSTALLED_APPS
列表中,使得 django 能自动找到app中的模板文件(app-name/templates/
下的文件)和静态文件(app-name/static/
中的文件)
模型
基本设置
安装mysql驱动
1 | pip install mysqlclient |
修改setting.py
配置
1 | DATABASES = { |
模型的创建和迁移(激活)
模型类需要定义在app下的modules.py
中,一个数据表对应一个模型类,表中的字段对应模型中的类属性。
创建模型
模型类需要继承自django.db.modules.Module
类
1 | from django.db import modules |
上面的代码相当于SQL语句:
1 | CREATE TABLE app_users ( |
其中,表名app_users
由Django自动生成,默认格式为“项目名称+下划线+小写类名”,Django默认自动创建自增主键id
,当然,你也可以自己指定主键。
可以通过在模型类中定义元数据类,自定义表名
1 | from django.db import modules |
常用字段类型
AutoField
一个自动增加的整数类型字段BooleanField
布尔值类型。默认值是None
。CharField
字符串类型。必须接收一个max_length
参数,表示字符串长度不能超过该值DateField
日期类型。一个Python中的datetime.date
的实例。EmailField
邮箱类型,默认max_length
最大长度254位。FileField
上传文件类型ImageField
图像类型,后面单独介绍IntegerField
整数类型,最常用的字段之一。取值范围-2147483648到2147483647。TextField
大量文本内容UUIDField
用于保存通用唯一识别码的字段。使用Python的UUID
类。
模型迁移(激活)
完成模型创建后,执行命令
1 | 制作迁移 |
增加数据
新建一个模型对象的方法有如下几种:
1 | User.objects.create(username='admin', password='pwd') |
1 | user = User(username='admin', password='pwd') |
1 | user = User() |
1 | User.boject.get_or_create(username='admin', password='pwd') |
查询数据
查询对象有以下方法
获取所有数据
1
User.objects.all()
切片操作
1
2# 获取5个人,不支持负索引,切片可以节约内存
User.objects.all()[:5]获取满足条件的一个对象,如果有多条会报错
1
User.objects.get(username='admin')
使用
filter
进行条件查询1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# 查询username字段严格等于'admin'的所有对象
User.objects.filter(username='amdin')
User.objects.filter(username__iexact='admin') #不区分大小写
# 查询username中包含admin的所有对象
User.objects.filter(username__contains='admin')
User.objects.filter(username__icontains='admin') # 不区分大小写
# 查询username中以'admin'开头的
User.objects.filter(username__startwith='admin')
User.objects.filter(username__istartwith='admin') # 不区分大小写
# 查询username中以'admin'结尾的
User.objects.filter(username__endwith='admin')
User.objects.filter(username__iendwith='admin') # 不区分大小写
# 正则表达式查询
User.objects.filter(name__regex='^admin')
User.objects.filter(name__iregex='^admin') # 不区分大小写除此之外,还有
__gt
大于__gte
大于等于__lt
小于__lte
小于等于__in
存在于一个list
范围内使用
exclude
排除符合条件的1
2# 排除包含 admin 的User对象
User.objects.exclude(username__contains="admin")链式过滤
filter
和exclude
的结果依然是个QuerySet
,因此它可以继续被filter
和exclude
,这就形成了链式过滤:1
User.objects.filter(username='admin').filter(password='password')[0]
使用Q对象进行复杂查询
普通filter
函数里的条件都是“and
”逻辑,如果你想实现“or
”逻辑就需要使用Q对象
Q来自django.db.models.Q
,用于封装关键字参数的集合,可以作为关键字参数用于filter
、exclude
和get
等函数
可以使用&
或者|
或~
来组合Q对象,分别表示与或非逻辑。它将返回一个新的Q对象。
1 | Q(question__startswith='Who')|Q(question__startswith='What') |
默认情况下,以逗号分隔的都表示AND关系:
1 | Poll.objects.get( |
当关键字参数和Q对象组合使用时,Q对象必须放在前面,如下例子:
1 | Poll.objects.get( |
如果关键字参数放在Q对象的前面,则会报错
修改(更新)数据
使用update()
方法可以批量为QuerySet
中所有的对象进行更新操作。
1 | # 更新所有用户名为admin的密码为new_pwd |
update
方法会被立刻执行,并返回操作匹配到的行的数目
删除数据
删除对象使用的是对象的delete()
方法。该方法将返回被删除对象的总数量和一个字典,字典包含了每种被删除对象的类型和该类型的数量
1 | # 删除所有用户名为admin的对象 |
其他有用的QuerySets的API
order_by()
对查询集进行排序
1 | # 结果将按照id降序排序,然后再按照score升序排序。 |
reverse()
反向排序distinct()
对查询集去重values()
返回一个包含数据的字典的queryset
,而不是模型实例
每个字典表示一个对象,键对应于模型对象的属性名称。
1 | # 列表中包含的是Blog对象 |
视图
视图层是Django处理请求的核心代码层,我们大多数Python代码都集中在这一层面。
它对外接收用户请求,对内调度模型层和模版层,统合数据库和前端,最后根据业务逻辑,将处理好的数据,与前端结合,返回给用户。
URL路由
URL是Web服务的入口,用户通过浏览器发送过来的任何请求,都是发送到一个指定的URL地址,然后被响应。
在Django项目中编写路由,就是向外暴露我们接收哪些URL的请求,除此之外的任何URL都不被处理,也没有返回。通俗地理解,不恰当的形容,URL路由是你的Web服务对外暴露的API。
URL路由在Django项目中的体现就是urls.py
文件,这个文件可以有很多个,但绝对不会在同一目录下。实际上Django提倡项目有个根urls.py
,各app下分别有自己的一个urls.py
,既集中又分治,是一种解耦的模式。
根 URL 就是在创建项目时自动创建的urls.py
1 | from django.contrib import admin |
默认导入了path
方法和admin
模块,然后有一条指向admin
后台的url路径。
我们自己要编写的url路由,基本也是这个套路。
Django处理请求的流程
- 决定要使用的根
URLconf
模块。通常,这是ROOT_URLCONF
设置的值,但是如果传入的HttpRequest
对象具有urlconf
属性(由中间件设置),则其值将被用于代替ROOT_URLCONF
设置。通俗的讲,就是你可以自定义项目入口url是哪个文件! - 加载该模块并寻找可用的
urlpatterns
。 它是django.urls.path()
或者django.urls.re_path()
实例的一个列表。 - 依次匹配每个URL模式,在与请求的URL相匹配的第一个模式停下来。也就是说,url匹配是从上往下的短路操作,所以url在列表中的位置非常关键。
- 导入并调用匹配行中给定的视图,该视图是一个简单的Python函数(被称为视图函数),或基于类的视图。 视图将获得如下参数:
- 一个
HttpRequest
实例。 - 如果匹配的表达式返回了未命名的组,那么匹配的内容将作为位置参数提供给视图。
- 关键字参数由表达式匹配的命名组组成,但是可以被
django.urls.path()
的可选参数kwargs
覆盖。
- 一个
- 如果没有匹配到任何表达式,或者过程中抛出异常,将调用一个适当的错误处理视图。
简单实例
1 | from django.urls import path |
- 要捕获一段url中的值,需要使用尖括号
- 可以转换捕获到的值为指定类型,比如例子中的
int
。
默认情况下,捕获到的结果保存为字符串类型,不包含/
这个特殊字符; - 匹配模式的最开头不需要添加
/
,因为默认情况下,每个url都带一个最前面的/
,既然大家都有的部分,就不用浪费时间特别写一个了。
每当urls.py
文件被第一次加载的时候,urlpatterns
里的表达式们都将被预先编译,这会大大提高系统处理路由的速度。
path转换器
默认情况下,Django内置下面的路径转换器:
str
:匹配任何非空字符串,但不含斜杠/
,如果你没有专门指定转换器,那么这个是默认使用的int
:匹配0和正整数,返回一个int
类型slug
:可理解为注释、后缀、附属等概念,是url拖在最后的一部分解释性字符。该转换器匹配任何ASCII
字符以及连接符和下划线,比如building-your-1st-django-site
uuid
:匹配一个uuid
格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写,例如075194d3-6885-417e-a8a8-6c931e272f00
。返回一个UUID
对象path
:匹配任何非空字符串,重点是可以包含路径分隔符/
。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串。
使用正则表达式
使用re_path()
方法来进行基于正则的url匹配
1 | from django.urls import path, re_path |
与path()方法不同的在于两点:
year
中匹配不到10000等非四位数字,这是正则表达式决定的- 传递给视图的所有参数都是字符串类型。而不像
path()
方法中可以指定转换成某种类型。
指定视图参数的默认值
有一个小技巧,我们可以指定视图参数的默认值。 下面是一个URLconf和视图的示例:
1 | # URLconf |
在上面的例子中,两个URL模式指向同一个视图views.page
。但是第一个模式不会从URL中捕获任何值。 如果第一个模式匹配,page()
函数将使用num
参数的默认值”1”。 如果第二个模式匹配,page()
将使用捕获的num值。
自定义错误页面
当Django找不到与请求匹配的URL时,或者当抛出一个异常时,将调用一个错误处理视图。Django默认的自带的错误视图包括400、403、404和500,分别表示请求错误、拒绝服务、页面不存在和服务器错误
它们分别位于:
- handler400 —— django.conf.urls.handler400。
- handler403 —— django.conf.urls.handler403。
- handler404 —— django.conf.urls.handler404。
- handler500 —— django.conf.urls.handler500。
这些值可以在根URLconf中设置。在其它app中的二级URLconf中设置这些变量无效。
我们可以自定义错误页面
首先,在根URLconf中额外增加下面的条目,并导入views
模块:
1 | from django.contrib import admin |
然后在,app/views.py文件中增加四个处理视图:
1 | def bad_request(request): |
除了这些,还需要将setting.py
中的DEUBG = True
改为DEUBG = false
路由转发
通常,我们会在每个app里,各自创建一个urls.py
路由模块,然后从根路由出发,将app所属的url请求,全部转发到相应的urls.py
模块中。
路由转发使用的是include()
方法,需要提前导入,它的参数是转发目的地路径的字符串,路径以圆点"."
分割。
1 | from django.urls import include, path |
每当Django 遇到include()
时,它会去掉URL中匹配的部分并将剩下的字符串发送给include
的URLconf
做进一步处理,也就是转发到二级路由去
反向解析
在URL中提供一个name
参数,并赋值一个你自定义的、好记的、直观的字符串。
通过这个name
参数,可以反向解析URL、反向URL匹配、反向URL查询或者简单的URL反查。
在需要解析URL的地方,对于不同层级,Django提供了不同的工具用于URL反查:
- 在模板语言中:使用
url
模板标签。(也就是写前端网页时) - 在Python代码中:使用
reverse()
函数。(也就是写视图函数等情况时) - 在更高层的与处理Django模型实例相关的代码中:使用
get_absolute_url()
方法。(也就是在模型model中)
例如:
URLconf:
1 | from django.urls import path |
2019年对应的归档URL是/articles/2019/。
模板的代码中:
1 | <a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a> |
Python代码中:
1 | from django.http import HttpResponseRedirect |
在app
中的urls.py
中,需要声明此app的名称,使用app_name = 'app名字'
之后需要反向解析到此urls.py
中的路由,需要使用app名字.路由
的格式。
比如:
1 | from django.urls import path |
视图函数
一个简单的视图
1 | from django.http import HttpResponse |
每个视图函数都接收一个HttpRequest
对象作为第一位置参数,一般取名为request
该视图返回一个HttpResponse
对象,其中包含生成的HTML页面。
Django内置的一些方法
Django在django.shortcuts
模块中,为我们提供了很多快捷方便的类和方法,它们都很重要,使用频率很高
render()
1 | render(request, template_name, context=None, content_type=None, status=None, using=None)[source] |
结合一个给定的模板和一个给定的上下文字典,返回一个渲染后的HttpResponse
对象。
必须参数:
request
:视图函数处理的当前请求,封装了请求头的所有数据,其实就是视图参数request
。template_name
:要使用的模板的完整名称或者模板名称的列表。如果是一个列表,将使用其中能够查找到的第一个模板。
可选参数:
context
:添加到模板上下文的一个数据字典。默认是一个空字典。可以将认可需要提供给模板的数据以字典的格式添加进去。
这里有个小技巧,使用Python内置的locals()
方法,可以方便的将函数作用于内的所有变量一次性添加。content_type
:用于生成的文档的MIME类型。 默认为DEFAULT_CONTENT_TYPE
设置的值。status
:响应的状态代码。 默认为200。using
:用于加载模板使用的模板引擎的NAME。
一个例子:
1 | from django.shortcuts import render |
这个示例等同于:
1 | from django.http import HttpResponse |
redirect()
1 | redirect(to, permanent=False, args, *kwargs)[source] |
根据传递进来的url参数,返回HttpResponseRedirect
参数to可以是:
- 一个模型:将调用模型的
get_absolute_url()
函数,反向解析出目的url; - 视图名称:可能带有参数:
reverse()
将用于反向解析url; - 一个绝对的或相对的URL:将原封不动的作为重定向的目标位置。
默认情况下是临时重定向,如果设置permanent=True将永久重定向。
1 | 传递视图名,使用reverse()方法反向解析url: |
get_object_or_404()
1 | get_object_or_404(klass, args, *kwargs)[source] |
常用于查询某个对象,找到了则进行下一步处理,如果未找到则给用户返回404页面。
在后台,Django其实是调用了模型管理器的get()
方法,只会返回一个对象。不同的是,如果get()
发生异常,会引发Http404
异常,从而返回404页面,而不是模型的DoesNotExist
异常。
必需参数:
klass
:要获取的对象的Model
类名或者Queryset
等;**kwargs
:查询的参数,格式应该可以被get()
接受。
HttpRequest对象
每当一个用户请求发送过来,Django将HTTP数据包中的相关内容,打包成为一个HttpRequest
对象,并传递给每个视图函数作为第一位置参数,也就是request
,供我们调用。
常用属性
HttpRequest.scheme
字符串类型,表示请求的协议种类,’http
‘或’https
‘。HttpRequest.body
bytes
类型,表示原始HTTP请求的正文。它对于处理非HTML形式的数据非常有用:二进制图像、XML等。
如果要处理常规的表单数据,应该使用HttpRequest.POST
。HttpRequest.path
字符串类型,表示当前请求页面的完整路径,但是不包括协议名和域名。
例如:”/music/bands/the_beatles/
“。
这个属性,常被用于我们进行某项操作时,如果不通过,返回用户先前浏览的页面。非常有用!HttpRequest.path_info
在某些Web服务器配置下,主机名后的URL部分被分成脚本前缀部分和路径信息部分。path_info
属性将始终包含路径信息部分,不论使用的Web服务器是什么。
使用它代替path可以让代码在测试和开发环境中更容易地切换。
例如,如果应用的WSGIScriptAlias设置为/minfo
,那么HttpRequest.path
等于/music/bands/the_beatles/
,而HttpRequest.path_info
为/minfo/music/bands/the_beatles/
。HttpRequest.method
字符串类型,表示请求使用的HTTP方法。默认为大写。 像这样:HttpRequest.encoding
字符串类型,表示提交的数据的编码方式(如果为None
则表示使用DEFAULT_CHARSET
设置)。
这个属性是可写的,可以通过修改它来改变表单数据的编码。任何随后的属性访问(例如GET或POST)将使用新的编码方式。HttpRequest.content_type
表示从CONTENT_TYPE
头解析的请求的MIME类型。HttpRequest.content_params
包含在CONTENT_TYPE
标题中的键/值参数字典。HttpRequest.GET
一个类似于字典的对象,包含GET请求中的所有参数。HttpRequest.POST
一个包含所有POST请求的参数,以及包含表单数据的字典。
如果需要访问请求中的原始或非表单数据,可以使用HttpRequest.body
属性。
POST中不包含上传文件的数据。HttpRequest.COOKIES
包含所有Cookie信息的字典。键和值都为字符串。
可以类似字典类型的方式,在cookie中读写数据HttpRequest.FILES
一个类似于字典的对象,包含所有上传的文件数据。FILES
中的每个键为<input type="file" name="" />
中的name属性值。FILES
中的每个值是一个UploadedFile
要在Django中实现文件上传,就要靠这个属性!
如果请求方法是POST且请求的<form>
中带有enctype="multipart/form-data"
属性,那么FILES
将包含上传的文件的数据。 否则,FILES
将为一个空的类似于字典的对象,属于被忽略、无用的情形。HttpRequest.META
包含所有HTTP头部信息的字典。 可用的头部信息取决于客户端和服务器,下面是一些示例:CONTENT_LENGTH
—— 请求正文的长度(以字符串计)。CONTENT_TYPE
—— 请求正文的MIME类型。HTTP_ACCEPT
—— 可接收的响应Content-Type。HTTP_ACCEPT_ENCODING
—— 可接收的响应编码类型。HTTP_ACCEPT_LANGUAGE
—— 可接收的响应语言种类。HTTP_HOST
—— 客服端发送的Host头部。HTTP_REFERER
—— Referring页面。HTTP_USER_AGENT
—— 客户端的user-agent字符串。QUERY_STRING
—— 查询字符串。REMOTE_ADDR
—— 客户端的IP地址。想要获取客户端的ip信息,就在这里!REMOTE_HOST
—— 客户端的主机名。REMOTE_USER
—— 服务器认证后的用户,如果可用。REQUEST_METHOD
—— 表示请求方法的字符串,例如”GET” 或”POST”。SERVER_NAME
—— 服务器的主机名。SERVER_PORT
—— 服务器的端口(字符串)。
除CONTENT_LENGTH
和CONTENT_TYPE
之外,请求中的任何HTTP头部键转换为META
键时,都会将所有字母大写并将连接符替换为下划线最后加上HTTP_前缀。
QueryDict对象
在HttpRequest
对象中,GET
和POST
属性都是一个django.http.QueryDict
的实例
request.POST
或request.GET
的QueryDict
都是不可变,只读的。如果要修改它,需要使用QueryDict.copy()
方法,获取它的一个拷贝,然后在这个拷贝上进行修改操作。
方法
django sessioin
启用session
编辑settings.py
中的一些配置
MIDDLEWARE
确保其中包含以下内容'django.contrib.sessions.middleware.SessionMiddleware'
INSTALLED_APPS
是包含'django.contrib.sessions'
这些是默认启用的。如果你不用的话,也可以关掉这个以节省一点服务器的开销。
sessioin的配置
使用数据库+缓存的session
配置settings.py
1 | SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 |
session的基本操作
以键值对的格式写session
1 | request.session['键']=值 |
根据键读取值
1 | request.session.get('键',默认值) |
清除所有session,在存储中删除值的部分
1 | request.session.clear() |
清除session数据,在存储中删除session的整条数据
1 | request.session.flush() |
删除session中的指定键及值,在存储中只删除某个键及对应的值
1 | del request.session['键'] |
设置session数据有效时间; 如果不设置,默认过期时间为两周
1 | request.session.set_expiry(value) |
如果过期时间的value
是一个整数,则 session 数据 将在value
秒没有活动后过期。
如果过期时间的value
为None
,那么会话永不过期。
如果过期时间的value
为0,那么用户会话的Cookie将在用户的浏览器关闭时过期。
静态文件
Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。
在setting.py
中定义静态文件存放的物理目录
1 | STATIC_URL = '/static/' |
这样,就设置了静态文件的存储路径为/static
在模板中,我们首先需要使用{% load static %}
加载 Django 的 STATICFILES_STORAGE
,之后使用{% static "images/a.jpg" %}
来动态的获取静态文件,而不是写死的路径。
模板
默认配置下,Django 的模板系统会自动找到app下面的templates
文件夹中的模板文件。
变量:{{ }}
功能类的:{% %}
获取字典内容:
1 | user = {'name':'admin'} |
在模板中取字典的键是用点user.name
,而不是Python中的 user['name']
循环for,条件判断if
1 | {% for i in list %} |
for循环有一些属性:forloop.counter
索引从 1 开始算forloop.counter0
索引从 0 开始算forloop.revcounter
索引从最大长度到 1forloop.revcounter0
索引从最大长度到 0forloop.first
当遍历的元素为第一项时为真forloop.last
当遍历的元素为最后一项时为真forloop.parentloop
用在嵌套的 for
循环中,获取上一层 for
循环的 forloop
1 | {% for item in List %} |
列表中可能为空时,用for empty
1 | <ul> |