django笔记

参考

进阶内容 => 使用 Django
api => API 参考

Django 文档 | Django 文档 | Django (djangoproject.com)

待了解
  • [[#url 调度器|url 调度器 -> 命名空间]]
  • [[#异步|视图 -> 异步]]
python -m pip install django
python -m pip install daphne	# => 用于部署服务器 
python -m pip install Pillow	# => 用于 ImageField

配置

topic:Django 配置
ref:django-admin 和 manage.py, 配置

运行 Django 时会将项目的 settings 模块添加到 django.conf.settings

配置用途建议
ROOT_URLCONF设置根路由
TIME_ZONEAsia/Shanghai
DEBUG

生产环境中建议使用虚拟环境 => 参见 [[python#虚拟环境]]

asgi 部署 => 有三种可选项 Daphne, Hypercorn, Uvicorn
=> 以 daphne 为例:

  • python -m pip install daphne
  • 在项目根目录运行 daphne path.to.asgi:application
  • 也可以与 runserver 集成:INSTALLED_APPS 最前面添加 "daphne" => ASGI_APPLICATION = "path.to.asgi.application"

wsgi 部署 => 有三种可选项 Gunicorn, uWSGI, Apache & mod_wsgi
=> 以 Gunicorn 为例“

应用(app)

ref:应用程序, Django 实用程序, contrib 包,

一个 Django app 由 urls, views, template, static, model, migration, database 等组成

urls, views => 实现 前端 和 后端 的交互
template, static => 主要实行前端功能 => 可以交给前端框架实现前后端分离
model, migration, database => 主要实行后端功能

路由(urls)

topic:URL调度器
ref:django.urls 实用函数, URLconfs 中使用的 django.urls 函数, URLconfs 中使用的 django.urls 函数, 跨站请求伪造保护

路由在 django 中被称为 URLconfs

涉及的包和模块:
=> django.urls => path(), re_path(), include(), reverse()
=> django.http => HttpRequest, HttpResponse, HttpResponseRedirect

url 调度器

Django 处理请求的工作方式:
=> 根据 HttpRequest 对象的 urlconf 属性,或者 ROOT_URLCONF 确定根路由器
=> 根据 HttpRequestpath_info 部分匹配路由器模块中 urlpatterns 里的一条路由,并转入下一条路由器,或者一个视图;若匹配不到路由,则 Django 调用适当的错误处理视图,参见 [[#安全|安全->异常]]
=> 例子

urlpatterns = [
    path("articles/2003/", views.a),
    path("articles/<int:year>/", views.b),
    path("articles/<int:year>/<int:month>/", views.c),
    path("articles/<int:year>/<int:month>/<slug:slug>/", views.d),
]
  • /articles/2005/03/ => 匹配第三项,调用 views.c(request, year=2005, month=3)
  • /articles/2003/ => 匹配第一项,调用 views.a(request)
  • /articles/2003 => 匹配不到
  • /articles/2003/03/building-a-django-site/ => 匹配第四项,调用 views.d(request, year=2005, month=3, slug='building-a-django-site')

注:route 中的每个路径参数都必须有一个视图函数的形参与之对应;反之,视图函数的每个位置参数都必须有一个 route路径参数
注2:路由器模块都有一个路由表 urlpatterns: &path()[] | &re_path()[]

路径转换器 => 在 path() 函数的 route 参数中用于指定 path-params

  • <str:path_param> => 匹配 [^/]+ 并传递给 path_param: str 参数
  • <int:path_param> => 匹配自然数 \d+ 并传递给 path_param: int 参数
  • <slug:path_param> => 匹配 [^\x00-\xFF]+(ASCII 字母、下划线、连字符)并传递给 path_param: str 参数
  • <uuid:path_param> => 匹配一个格式化的 UUID
  • <path:path_param> => 匹配 .+ 并传递给 path_param: str 参数
    => 例子:

自定义路径转换器 => 定义一个类,其具有 regex 属性,to_python()to_url() 方法 => 然后用 register_converter(ExampleConverter: class, converter_name: str) 注册转换器

  • regex: str => 指定匹配的模式
  • to_python(self, value: str): any => 指定路径参数传递给视图函数形参时的数据
  • to_url(self, value: any): str => 指定数据转换为 url 中的路径参数时得到的字符串(即 to_python() 的逆变换),通常在调用 reverse() 时触发
    => 例子:
class ExampleConverter:
	regex = "\d{4}"
	def to_python(self, value):
		return int(value)
	def to_url(self, value):
		return "%04d" % value

from django.urls import path, register_converter
register_converter(ExampleConverter, "yyyy")
urlpatterns = [
	path("<yyyy:param>", ...)
]

正则路由 => re_path() => 使用 (?P<path-param>pattern)(pattern) 进行分组(或者说指定路径参数)
=> 例子: re_path(r'^email/(?P<param>\w+@\w+\.\w+)/$', ...)

  • 嵌套参数 =>

url 反向解析 => 即 根据

  • 模板标签 url => {% url <route_name> <path-params> %}
  • reverse(route_name: str, args?: any[]): str 函数
  • get_absolute_url() 方法

route_name => 在定义路由时,定义其路由名是一个好习惯!
url 命名空间 =>

  • 应用程序命名空间
  • 实例命名空间

注:app_name 用于设置应用程序空间?

附录-路由
urls 的函数用途参数解释
path(route: str, view: (req: HttpRequest, **path_params) => HttpResponse, args?: dict, name?: str)获取一条匹配规则 => 将匹配的路由转发到视图args => 传递给视图的形参的额外数据
name => 路由规则的标识符
re_path(route: regex, view: (req: HttpRequest, **path_params) => HttpResponse, args?: dict, name?: str)
include(“path.to.urls”, namespace: str)包含其他 URLconfsnamespace =>
include(urlpatterns: &path()[])
reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)current_app => 指定 app 命名空间

视图(views)

topic:编写视图, 视图装饰器, 文件上传, Django 便捷函数, 通用视图, 中间件, 如何使用会话
topic2:基于类的视图, 内置的基于类的通用视图, 使用基于类的视图处理表单, 在基于类的视图中使用混入,
topic3:条件视图处理, 分页
ref:内置基于类的视图 API, 内置视图, 请求和响应对象, 中间件

涉及的包和模块,或函数:

  • django.http => HttpResponse, HttpResponseRedirect, QueryDict

  • django.shortcuts => render()

  • django.conf.urls => handler400, handler403, handler404, handler500

  • django.views.decorators.http => @require_http_methods(), @require_GET(), @require_POST()

  • django.views.decorators.gzip => gzip_page()

  • django.views.decorators.vary => vary_on_cookie()

  • django.views.decorators.cache => cache_control()

  • django.views.decorators.common => no_append_slash()

  • django.views.static => serve()

  • django.views.defaults => page_not_found(), server_error(), permission_denied(), bad_request()

  • django.core.serializers.json => DjangoJSONEncoder

  • django.template.response => SimpleTemplateResponse, TemplateResponse

  • django.template.loader => get_template()

  • django.template.backends.django => Template

  • django.views.generic => View, TemplateView

涉及的异常:

  • django.http => Http404, HttpResponseNotAllowed
  • django.core.exceptions => PermissionDenied, SuspiciousOperation
编写视图

相关:[[#请求]], [[#响应]]

视图函数
=> 接受 web 请求并返回 web 相应的 python 函数
=> 形如 view_func(req: HttpRequest, path_param: str...): HttpResponse
注:Django 并不会限制这些函数的位置,而惯例是放在名为 views 的包或模块中
=> 常见响应 => HttpResponse 及其子类,参考 [[#附录-视图|HttpResponse 及其子类的构造器]]
=> 404 异常:Http404

自定义报错视图? => django.conf.urls 下的 handler400, handler403, handler404, handler500

CSRF_FAILURE_VIEW => 可以用来覆盖 CSRF 报错视图?

便捷工具 => django.shortcuts 包下的 render(), redirect(), get_object_or_404(), get_list_or_404()

视图装饰器

前置知识:[[python#装饰器]]

django.views.decorators.http 包下的某些装饰器可以声明一个视图函数需要指定的请求方法才能调用,否则会触发 django.http.HttpResponseNotAllowed 错误

@require_http_methods(methods: str[]) => 指定视图函数所需的请求方法
@require_GET() => 指定视图函数只接受 GET 方法
@require_POST() => 指定视图函数只接受 POST 方法
@require_safe() => 指定视图函数只接受 GETHEAD 方法
@condition(etag_func=None, last_modified_func=None) => 用于控制特殊视图中的缓存行为

django.views.decorators
=> gzip => 用于压缩
=> vary, cache => 用于控制缓存
=> common =>

异步
文件&异常

Django 的一些视图函数允许 用 url 读取文件,或自定义常见 http 错误时返回的模板

django.views.static => serve(req: HttpRequest, path: str, document_root: str, show_indexes?: bool) => 可以通过 url 获取文件
=> 配置的例子:

  • 全局设置 => MEDIA_ROOT = BASE_DIR / 'media'
  • 配置路由 => re_path(r"^media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}, "media")

django.views.defaults
=> page_not_found(req: HttpRequest, exception, template_name='404.html') => 发出 Http404 错误时,Django 会加载 page_not_found() 视图 => 若 templates/ 目录下存在 404.html 模板,那么该视图返回该模板;否则返回默认的 Not Found 页面
注:该视图给 404.html 提供了 2 个上下文参数 request_path, exception
注2:URLconf 找到匹配结果时,会自动发出 Http404 错误
注3:只有在 DEBUG=False 时生效
=> server_error(req: HttpRequest, template_name='500.html') => 视图中出现异常时调用
=> permission_denied(req: HttpRequest, exception, template_name='403.html') => 发出 PermissionDenied 异常时调用
注:该视图给 403.html 提供了上下文参数 exception
=> bad_request(req: HttpRequest, exception, template_name='400.html') => 发出 SuspiciousOperation 异常时调用

请求
HttpRequest 基本属性解释补充
scheme: str请求协议http, https
path: str请求路径
path_info: str请求路径的一个后缀
method: strHTTP METHOD
headers: dictHTTP HEADERS
content_type: strHTTP HEADERSContent-Type 键的值
content_params: dictHTTP HEADERSContent-Type 头对应的字典·
GET: QueryDictHTTP QUERY适用于所有 HTTP METHOD
POST: QueryDictHTTP BODY 的字典数据仅限于 POST 方法
FILES: MultiValueDict仅限于 Content-Type=multipart/form-dataPOST 方法,
body: byte_strHTTP BODY 的二进制数据适用于所有 HTTP METHOD
encoding: str解码表单提交数据的编码方案默认为 DEFAULT_CHARSET 的值
COOKIES: dictcookies 的字段
META: dictheaders(key 会做相应转换)
resolver_match: ResolverMatch
注:在调用 <HttpRequest>.read() 后调用 <HttpRequest>.body,会抛出 RawPostDataException 异常

应用程序代码设置的属性 => current_app, urlconf, exception_reporter_filter, exception_reporter_class
中间件设置的属性 => session, site, user

HttpRequest 的函数用途补充
read(): bytesHTTP BODY 的二进制数据
close()关闭读取 HTTP BODY 的流在 post 请求中,若不调用 req.close(),那么调用 req.postreq.read() 时会报错(由于锁机制)
auser()
get_host()
get_port()
get_full_path()
get_full_path_info()
build_absolute_uri()
get_signed_cookie()
is_secure()
accepts()
readline()
readlines()
__iter__()
QueryDict 的方法用途
__init__(query_params: str, mutable?: bool, encoding?: str)
__getitem__(key: str): str
__setitem__(key: str, val: str)
__contains__(key: str): bool
get(key: str, default?: str): str
setdefault(key: str, default?: str)
update(other: dict)
items(): 2-tuple[]
values(): str[]
copy(): QueryDict
getlist(key: str, default?: str[])
setlist(key: str, list: str[])
appendlist(key: str, item: str)
setlistdefault(key: str, default_list: str[])
lists(): 2-tuple[]
pop(key: str): str[]
popitem(): 2-tuple[]
dict(): dict
urlencode(safe?: str)
响应

HttpResponseBase(content_type?: str, status?: int, reason?: str, charset?: str, headers?: dict) => Django 大部分响应类的祖先类

HttpResponse

HttpResponse(data, headers?: dict)

HttpResponse 的属性解释
content: str
cookies: SimpleCookie
headers: dict
charset: str
status_code: str
reason_phrase: str
streaming: bool
closed: bool
HttpResponse 的方法用途
__init__(content=b’', content_type=None, status=200, reason=None, charset=None, headers=None)
__setitem__(header, value)
__delitem__(header)
__getitem__(header)
get(header, alternate=None)
has_header(header)
items()
setdefault(header, value)
set_cookie(key, value=‘’, max_age=None, expires=None, path=‘/’, domain=None, secure=False, httponly=False, samesite=None)
set_signed_cookie(key, value, salt=‘’, max_age=None, expires=None, path=‘/’, domain=None, secure=False, httponly=False, samesite=None)
delete_cookie(key, path=‘/’, domain=None, samesite=None)
close()
write(content)
flush()
tell()
getvalue()
readable()
seekable()
writable()
writelines(lines)
HttpResponse 子类

以下介绍 HttpResponse 的特殊子类:

注意两个响应头 => Content-Type, Content-Disposition

JsonResponse(data: dict | any, encoder=DjangoJSONEncoder, safe=True, json_dumps_params?: dict)

  • data 相应的数据
  • DjangoJSONEncoder < JSONEncoder => 编码器
  • safe => 是否只指定 data 只能是 dict
  • json_dumps_params => 指定与 json.dumps() 相同的参数

StreamingHttpResponse(streaming_content: generator_obj)

  • generator_obj => 生成器对象 => 例子:可以通过 (i for i in iterable_obj) 得到
StreamingHttpResponse 的属性解释
streaming_content: generator_obj
status_code: inthttp 状态码
reason_phrase
streaming: bool
is_async: bool

FileResponse(open_file: byte, as_attachment=False, filename='', **kwargs)
=> 例子:FileResponse(open('media/sd.jpg','rb'))
注:当前目录为 manage.py 所在目录,即当前目录总是项目的根目录

<FileResponse>.set_headers(open_file) => 根据 open_file 设置诸如 Content-Type, Content-Length, Content-Disposition 等响应头

模板响应

SimpleTemplateResponse < HttpResponse, HttpResponseBase

SimpleTemplateResponse(tmpl: str | Template [], context: dict, **args)

  • 注:args 中不包含 content: str
SimpleTemplateResponse 的属性解释
template_name: str | Template []
context_data: dict
rendered_content
is_rendered: bool
SimpleTemplateResponse 的方法用途
resolve_context(context: dict): dict预处理上下文数据
resolve_template(template)解析模板实例
add_post_render_callback()添加一个渲染发生后调用的回调
render()

TemplateResponse < SimpleTemplateResponse, HttpResponse, HttpResponseBase

TemplateResponse(req: HttpRequest, tmpl: str | Template [], context: dict, **args)

基于类的视图
class ExampleView(View):
    def get(self, req: HttpRequest):
    	pass

    def post(self, req: HttpRequest):
    	pass

    def put(self, req: HttpRequest):
    	pass

    def delete(self, req: HttpRequest):
    	pass

    def patch(self, req: HttpRequest):
    	pass

	...
附录-视图

django.http => HttpRequest, HttpResponse, JsonResponse, StreamingHttpResponse, FileResponse, HttpResponseBase, QueryDict

HttpResponseBase(content_type?: str, status?: int, reason?: str, charset?: str, headers?: dict)

HttpResponse < HttpResponseBase

HttpResponse 及其子类的构造器解释状态码
HttpResponse(content?: str, **args)通用响应默认为200
HttpResponseRedirect(url_or_path: str)重定向302
HttpResponsePermanentRedirect(url_or_path: str)永久重定向301
HttpResponseNotModified()页面未更改304
HttpResponseBadRequest400
HttpResponseNotFound404
HttpResponseForbidden403
HttpResponseNotAllowed(allowed_method: str[])方法不允许405
HttpResponseGone410
HttpResponseServerError500

django.shortcuts => 便捷工具

shortcuts 包的函数用途
render(req: HttpRequest, template_path: str, context?: dict, content_type?: str, status?: int, using?): HttpResponse
redirect(to, *args, permanent=False, **kwargs)
get_object_or_404(klass, *args, **kwargs)
get_list_or_404(klass, *args, **kwargs)

django.views.decorators.http => 指定视图函数接受的方法

decorators.http 包的装饰器用途
@require_http_methods(methods: str[])
require_GET()
require_POST()
require_safe()
SuspiciousOperation 的子类
DisallowedHost
DisallowedModelAdminLookup
DisallowedModelAdminToField
DisallowedRedirect
InvalidSessionKey
RequestDataTooBig
SuspiciousFileOperation
SuspiciousMultipartForm
SuspiciousSession
TooManyFieldsSent
TooManyFilesSent

模板(template)

topic:模板, 使用表单
ref:模板, TemplateResponse 和 SimpleTemplateResponse, 表单, 分页器

涉及的模块和后端

  • django.template.backends.django => DjangoTemplates
  • django.template.backends.jinja2 => Jinja2

引擎后端

  • DjangoTemplates => django 内置后端
  • Jinja2 => django 内置后端,但需要额外安装
  • 自定义后端

配置:=> 以 DjangoTemplates 为例

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            # ... some options here ...
        },
    },
]
  • BACKEND => 指定模板引擎后端
  • DIRS: str[] => 指定项目中的一些模板目录
  • APP_DIRS: bool => 是否每个 app 下的 templates 目录为模板目录?
  • OPTIONS: dict => 指定特定于模板后端的配置
语法

变量 => {{ <val> }}

  • 字典 / 数组 / 对象 检索值时使用句点语法 => 如,{{ my_dict.key }}, {{ my_arr.5 }}, {{ my_obj.attr }}

标签 => {% <tag> %}{% <tag_begin> %} ... {% <tag_end> %}

  • 大多数标签带参数 => 如,{% url 'my_blogs' 3 %}
  • 具有控制结构
    => {% if <condition> %} ... {% endif %}
    => {% for ... %} ... {% endif %}

过滤器 => {{ <val>|<filter> }}{{ <val>|<filter>:<arg> }}

注释 => {# comment_raw... }{% comment %} comment_raw... {% endcomment %}

组件

引擎 => django.template.Engine
模板 => django.template.Template
上下文 => django.template.Context, django.template.RequestContext
加载器 =>
上下文处理器

静态资源(static)

配置:

INSTALLED_APPS = [
	'django.contrib.staticfiles',
]
TEMPLATES = [
    {
        'OPTIONS': {
            'builtins' : [
                'django.templatetags.static',
            ],
        },
    },
]

=> django 默认只会搜索 <app-name>/static/ 目录下的静态文件
注:若不配置 builtins,那么 .html 文件要使用静态文件时都要使用指令 {% load static %}

STATICFILES_DIRS: str[] => 添加本地中额外的静态文件目录
=> 例子:

STATICFILES_DIRS = [
    BASE_DIR / 'static_local',
]

STATIC_URL => 配置 url 到本地静态文件出道路由,这些静态文件由 STATICFILES_DIRS 配置,或 <app-name>/static/ 提供
=> 例子:STATIC_URL = '/static_url/'

STATIC_ROOT => collectstatic 命令会将所有静态文件收集并整理到 STATIC_ROOT 所指定的目录

  • 例子 => STATIC_ROOT = BASE_DIR / 'staticfiles'

模型(models)

topic:模型, 执行查询, 聚合, 搜索, 管理器, 原生sql, 数据库事务, 模型关联 API 用法示例, 序列化 Django 对象
ref:模型, 信号, 验证器,

涉及的包和模块:

  • django.db.models => Model
  • django.db.models.fields => Field, SmallIntegerField, Integerxield, BigIntegerField, PositiveSmallIntegerField, PositiveIntegerField, PositiveBigIntegerField, SmallAutoField, AutoField, BigAutoField, FloatField, DecimalField, BooleanField, CharField, TextField, EmailField, URLField, DateField, DateTimeField, DurationField, GenericIPAddressField, FilePathField, SlugField, UUIDField
  • django.db.models.fields.files => FileField, ImageField
  • django.db.models.fields.json => JSONField
  • django.db.models.fields.generated => GeneratedField

Django 中只有 app 才拥有自己的 model,其定义在 <app>.models 下(可以是 .py 的模块文件,也可以是包的 __init__.py 文件)

注册模型 => 首先要注册 app => 即 INSTALLED_APPS 添加一条表示 model 所属 app 的配置
定义模型 => 定义字段 => 字段类型字段选项

字段类型的作用

  • 指定字段在数据库中的类型
  • 渲染表单字段时默认使用 html 表单进行渲染
模型类
  1. <app>.models 下的 django.db.models.Model 的子类总是对应一张数据库表(两个模型之间也可能有一张中间表)
  2. Model 子类的属性与对应的数据库表的字段一一映射

内部异常类 => DoesNotExist, MultipleObjectsReturned

模型字段

外链:模型字段参考 -> 字段类型

以下每个模型字段类都是 [[#models.fields.Field]] 的孙子 => 于是构造方法的大部分参数也可能继承

Field 类

参考:[[#models.fields.Field|django.models.fields.Field]]

Field 的构造器的参数

构造器参数解释表单?补充
null: bool数据库中若字段为空,则将字段设置为 NULL
blank: bool字段可为空
choices: 2-tuple[] | map | enum枚举字段(key为后端数据,value为前端数据)<select>model 添加新字段时,该选项应与 default 选项配合使用
default: any | func字段默认值
help_text: str
primary_key: bool表的唯一主键(该字段只读)若表中没有该字段,则 django 将添加 IntegerField 字段,并设为主键
unique: bool
verbose_name: str人类可读的字段名
构造器参数解释
db_column: str数据库底层中该字段对应的列名
db_comment: str数据库底层中对该字段的注释
db_default: any字段默认值,可以是字面量,或者数据库函数
db_index: bool数据库底层中对该字段创建索引
db_tablespace: bool
editable
error_messages
unique_for_date: bool
unique_for_month: bool
unique_for_year: bool
validators:验证器列表

Field 的属性:

普通属性解释
auto_created: bool
concrete: bool
hidden: bool
is_relation: bool是否为特殊字段
model当前模型
特殊字段的属性解释
many_to_many: bool
many_to_one: bool
one_to_many: bool
one_to_one: bool
related_model相关联的模型

注:特殊字段 => 用于定义两个模型之间的关系的字段(ForeignKey, ManyToManyField, OneToOneField 其中之一)

数字字段

包括 uuid、整数、浮点数、bool

字段类型解释表单部件相关验证补充
UUIDField用于存储通用唯一标识符的字段
SmallIntegerField用于存储 16 位整数MinValueValidator, MaxValueValidator
IntegerField用于存储 32 位整数
BigIntegerField用于存储 64 位整数
PositiveSmallIntegerField用于存储 16 位正整数
PositiveIntegerField用于存储 32 位正整数
PositiveBigIntegerField用于存储 64 位正整数
SmallAutoField根据可用的 id 自动递增的 SmallIntegerField
AutoField根据可用的 id 自动递增的 IntegerField
BigAutoField根据可用的 id 自动递增的 BigIntegerField
FloatField用于存储 floatlocalize=Ture => TextInput
localize=False => NumberInput
DecimalField(max_digits: int, decimal_places: int)用于存储 Decimallocalize=Ture => TextInput
localize=False => NumberInput
DecimalValidator
BooleanField用于存储 boolCheckboxInput
字符串字段

包括 原始字符串、邮箱、url、时间、文件、IP地址、slug 等

字段类型解释表单部件相关验证补充
CharField(max_length: int, db_collation?)用于存储 strTextInputMaxLengthValidator
TextField(max_length: int, db_collation?)用于存储大文本 strTextInput
EmailField(max_length=254)存储邮箱的 CharFieldEmailValidator
URLField存储 url 的 CharFieldURLValidator
GenericIPAddressField(protocol=‘both’, unpack_ipv4=False)
DateField(auto_now?: bool, auto_now_add?: bool)用于存储 datetime.dateDateInputauto_now, auto_now_add, default 三者互斥
TimeField(auto_now=False, auto_now_add=False)用于存储 datetime.time
DateTimeField(auto_now?: bool, auto_now_add?: bool)用于存储 datetime.datetimeDateTimeInput
DurationField用于存储 datetime.timedelta
FilePathField(path=‘’, match=None, recursive=False, allow_files=True, allow_folders=False, max_length=100)
SlugField(max_length=50)validate_slug 或 validate_unicode_slug
数据字段
字段类型解释参数说明表单部件相关验证补充
BinaryField(max_length: int)用于存储二进制数据MaxLengthValidator
FileField(upload_to?: str, storage, max_length=100)用于存储文件FieldFile?
ImageField(upload_to=None, height_field=None, width_field=None, max_length=100)用于验证有效的图像的 FileFieldClearableFileInput需要 pillow 库
JSONField(encoder: json.JSONEncoder, decoder: json.JSONDecoder)
GeneratedField(expression, output_field, db_persist=None)数据库底层中由其他字段计算的字段
注:FileField 实例会得到 django.db.models.fields.files.FieldFile 的代理 => 用于访问文件内容(如 open(mode='rb') 方法)
表间关系字段

定义模型间的关系的三个特殊字段

表间关系对应字段类型补充
多对一ForeignKey(model: Model | str, …)支持自关联关系
多对多ManyToManyField(model: Model | str, through: Model | str, …)1. 建议使用复数形式
2. 只能设置在两个多对多模型之一
3. 总是会产生中间模型
一对一OneToOneField(model: Model | str, …)
注:
  • model => 关联模型
  • through => 自定义中间模型 => 用于给多对多关系添加额外字段

多方的相关方法:

<model1>.<model2>_set.add(model: Model2, through_defaults: dict)
<model1>.<model2>_set.create(opt1, ..., through_defaults: dict)
<model1>.<model2>_set.set(model: Model2[], through_defaults: dict)
<model1>.<model2>_set.remove(model: Model2)
<model1>.<model2>_set.clear()

注:字段有命名限制 => 不能是关键字,不能有下划线,不能以下换线结尾

Meta 内部类

外链:模型 | Django 文档 | Django (djangoproject.com), 模型 Meta 选项 | Django 文档 | Django (djangoproject.com)

ordering: str[] => 指定排序的字段
verbose_name_plural =>
abstract: bool => 指定当前模型是否为抽象基类 => 若为 true,那么该模型不会创建任何数据库表
indexes: models.Index[] => 用于创建数据库索引,参见 [[#模型索引]]

模型自定义方法

模型中的方法可以执行表级操作(这些定义不需要迁移

例子:

class User(models.Model):
    username = models.CharField("user's name",max_length=20)
    password = models.CharField("user's password",max_length=20)

    def func(self):
        return f'{self.username} {self.password}'

    @property
    def virtual_attr(self):
        return f'{self.username} {self.password}'

两个常用的自定义方法:

  • __str__() => 用于交互式控制台或后台中现实某个模型本身的内容
  • get_absolute_url() => 告诉 Django 如何计算对象的 url

可以重写 save()delete()Model 方法 =>
例子:

def save(self, *args, **kwargs):
	# do something ...
	super().save(args,kwargs)
	# do something else ...
模型类继承

抽象基类 => 模型基类Meta 子类上定义字段 abstract = True => 该模型不会创建任何表,仅用于继承
多表继承 => 模型基类Meta 子类上定义字段 abstract = False => 继承链中的每个模型都会创建表
代理模型 => 模型子类Meta 子类上定义字段 proxy = True => 该模型不会创建任何表,但是可以操作基类的字段和方法

模型索引

外链:模型索引参考 | Django 文档 | Django (djangoproject.com)

[[#Meta 内部类]] 中定义的 indexes 用于给模型创建多条索引

models.Index 构造器选项解释
expressions
fields
name
db_tablespace
opclasses
condition
表单

django.forms.forms => BaseForm, Form
django.forms.models => BaseModelForm, ModelForm

  • 其中 Form < BaseFormModelForm < BaseModelForm

BaseForm(data?: dict, files?: dict, field_order, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, use_required_attribute, renderer)

  • data => 非文件数据的字典
  • files => 文件数据的字典

BaseModelForm(data?: dict, files?: dict, instance?: Model, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, use_required_attribute, renderer)

模型表单

参考:[[#models.fields.Field]], [[#forms.fields.Field]]

模型表单 => 绑定了模型的表单,支持的功能:

  • __str__ 返回渲染 html 表单

数据库模型可以将一部分字段和字段参数的映射到模型表单 => 相关细节可参考 [[#映射表]]

例子:=> 实现数据库模型 User 到模型表单 UserForm 的映射

from django.forms import ModelForm
class UserForm(ModelForm):
    class Meta:
        model = User
        fields = ['username', 'password', 'file']

注:上述定义大致能以继承 Form 类来实现部分功能,于是会比上述定义麻烦得多

映射表

数据库模型字段所属的包到模型表单字段所属的包的映射:

  • django.db.models.fields => django.forms.fields
  • django.db.models.fields.related => django.forms.models

数据库模型字段模型表单字段的映射表:

数据库模型字段模型表单字段补充
SmallIntegerField 或 BigIntegerFieldIntegerField
BooleanFieldBooleanField 或 NullBooleanField
BinaryField 或 TextFieldCharField
ForeignKeyModelChoiceField
ManyToManyFieldModelMultipleChoiceField
其他的类同名类
注:AutoField, BigAutoField, SmallAutoField => 不呈现在表单中

数据库模型字段参数模型表单字段参数的映射表:

模型字段参数表单字段参数
verbose_namelabel
help_texthelp_text
choices 非空widget = Select
blank=Truerequired = False
附录-模型
  • django.db.models.fields => Field
  • django.forms.fields => Field

以下每个小节的命名都是某个类的最短后缀路径

models.fields.Field
Field(
	  verbose_name?: str, 
	  name?: str, 
	  primary_key=False, 
	  max_length?: int, 
	  unique=False, 
	  blank=False, 
	  null=False,
	  db_index=False, 
	  rel, 
	  default=NOT_PROVIDED, 
	  editable=True, 
	  serialize=True, 
	  unique_for_date?: DateField, 
	  unique_for_month?: DateField, 
	  unique_for_year?: DateField, 
	  choices: dict<str, any>, 
	  help_text?: str, 
	  db_column, 
	  db_tablespace=False, 
	  auto_created=False, 
	  validators=(), 
	  error_messages, 
	  db_comment, 
	  db_default=NOT_PROVIDED
  )

  • blank: bool, null: bool => 内存 或 数据库 中的是否可为空
  • default: any, db_defaul: any => 内存 或 数据库 中的默认值
forms.fields.Field
Field(
	  required=True, 
	  widget, 
	  label, 
	  initial, 
	  help_text, 
	  error_msg, 
	  show_hidden_initial=False, 
	  validators=(), 
	  localize=False, 
	  disabled=False, 
	  label_suffix, 
	  template_name
  )
Form, ModelFormuery

模型查询

数据库查询相关的包或类

  • django.db.models.base => Model
  • django.db.models.related => ForeignKey, ManyToManyField
  • django.db.models.query => QuerySet
  • django.db.models.expressions => F
  • django.db.models.aggregates => Avg, Min, Max
  • django.db.models.manager => Manager
  • django.db.models.fields.json => KT
执行查询

创建或保存对象 =>

  • <Model>.save(**opts) => 保存模型对象
  • <Manager>.create(**attr_opts) => 创建并保存对象
    => 例子:
# 例1
user_model = UserModel(username='150',password='62')
user_model.save()
# 例2
UserModel.objects.create(username='150',password='62') # => 会返回对应模型

修改对象 =>

  • <Model>.<attr> = new_val => 直接修改
  • <Model>.<func>() => 间接修改
  • <Model>.<many-Model>_set.add(<many-Model1>, ...) => 添加一个多方模型的记录
  • <Model>.<one-Model> = <one-Model> => 覆盖一个一方模型的记录

检索对象 => 用到 QuerySet 类 => 通常通过 <Model>.objects.all() 得到
=> 通过 get() 获取单个对象
=> 通过 filter(), exclude() 过滤或排除检索到的对象集合
注:QuerySet 是惰性的 => 每次过滤都只是在添加限制条件,并非真正执行了 sql,只会在你 “要使用” 时才会执行
=> 支持切片极其step语法 => 例如:<QuerySet>[0], <QuerySet>[0:5], <QuerySet>[::2], <QuerySet>[1::2]
注:指定步长后会执行 sql

其中 filter(), exclude() 等方法支持如下形式的可选参数:

  • <attr> => 模型的属性
  • <attr>__<lookup_type> => 指定模型属性的各种复杂查询 => lookup_type 的常见类型 => lte, gte. lt, gt, exact, iexact, regex, iregex, contains, icontains, startswith, endswith, istartswith, iendswith, in
  • <one-model>_id => 关联的表模型的 id
  • <one-model>__<one-model-attr> => 指定 一方模型 的属性的限制条件
  • <one-model>__<one-model-attr>__<lookuptype> =>
  • <many-model>__<many-model-attr> => 指定至少满足一个 多方模型 的属性的限制条件
  • <many-model>__<many-model-attr>__<lookuptype> =>
    参数值:
  • 常量
  • F 表达式 => F(attr: str) => 可以引用当前模型的字段,并进行相关运算
  • 可以使用 Min, Max, Avg

缓存问题 => 执行查询 | Django 文档 | Django (djangoproject.com)

异步查询
json 查询

json 字段在 filter(), exclude() 等方法中的参数的组成:

  • 前缀 <json-attr>
  • __<int> => 索引 json 数组的第 <int> 个 json 对象
  • __<key> => 索引 json 对象 key 为 <key> 的 json 对象

KT(lookup: str) => 针对于 json 字段的类似于 F(lookup: str) 的函数

json 字段还支持更多的 lookup_type

  • contains =>
  • contained_by =>
  • has_key: str => 含有对应的 key
  • has_keys: str[] => 含有给定 key 数组中的所有 key
  • has_any_keys: str[] => 含有给定 key 数组中的一个 key
通过 Q 对象完成复杂查询

执行查询 | Django 文档 | Django (djangoproject.com)

附录-视图查询

注:下面的方法加上前缀 a 总是可以得到异步版本

Model 的类属性用途
objects: Manager
Model 的方法用途
save(force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None): void保存记录
Manager 的方法用途
all(): QuerySet
create(**kwargs): Model创建一个对象并保存记录
注:Manager 实例通常通过 Model.objects 获取
QuerySet 的方法用途
get(**kwargs): Model检索单个对象
filter(**lookup_args): QuerySet过滤对象
exclude(**lookup_args): QuerySet排除对象
values(): QuerySet
annotate(): QuerySet

迁移(migrations)

topic:迁移, 管理文件,
ref:迁移操作, SchemaEditor,

数据库(database)

topic:多数据库, 表空间, 数据库访问优化, 数据库工具, 辅助工具,
ref:数据库

测试(test)

topic:编写并运行测试, 测试工具, 进阶测试主题

文件

ref:文件处理

涉及的包和模块:

  • django.forms.forms => Form
  • django.forms.fields =>
  • django.core.files.uploadedfile => UploadedFile, TemporaryUploadedFile
获取文件上传

核心:视图中通过 <HttpRequest>.FILES.get("<file_key>"): TemporaryUploadedFile 获取指定文件

有几种方法:

  • open() 方法 => 直接将文件写入本地
  • ModelFileField 属性 => 可以将 TemporaryUploadedFile 实例直接赋值给该属性

例子1: => 将上传的文件写入本地 => 请求的表单中设置 enctype="multipart/form-data"method="POST" 属性,以及 <input type="file" name="my_file"> 标签

def handel_file(file: TemporaryUploadedFile):
    with open(f'media/{time.time_ns()}.html', 'wb+') as out:
        for x in file.chunks():
            out.write(x)

def my_view(req: HttpRequest):
	form = Form(req.POST, req.FILES)
	if req.method == 'POST' and form.is_valid():
		file = req.FILES.get('my_file')
		handel_file(file)
		return HttpResponse("uploded file saved")
	return HttpResponse("nothing to do")

例子2:

def my_view(req: HttpRequest):
	u = m_user.User(username='liangxiongsl', password='lx15062', file=req.FILES.get('myfile'))  
	u.save()

例子3:

from django.forms import ModelForm
class UserForm(ModelForm):
    class Meta:
        model = User
        fields = ['username', 'password', 'file']
def my_view(req: HttpRequest):
	uf = UserForm(data={
        'username': 'liangxiongsl',
        'password': 'lx15062',
    }, files={
        'file': req.FILES.get('myfile')
    })
	uf.save()

例子4:

  • 视图
def test(req: HttpRequest):
    match req.method:
        case 'GET':
            return TemplateResponse(req, 'test.html', {'form': SongForm()})
        case 'POST':
            form = SongForm(req.POST, req.FILES)

            if form.is_valid():
                form.save()
                return HttpResponse("test: 歌曲创建成功")
            else:
                return HttpResponse("test: 字段非法")
        case _:
            return HttpResponse("test: 请求方法错误")
  • 模板 test.html
<form action="{% url 'test' %}" method="post" enctype="multipart/form-data">
    {{ form }}
    <input type="submit">
</form>
获取多个上传的文件
handler
附录-文件

模块索引:

  • django.core.files.uploadedfile => UploadedFile, TemporaryUploadedFile
UploadedFile
UploadedFile 的方法用途补充
read()一次性读取文件的所有数据有内存溢出的风险
multiple_chunks(size: int): bool是否需要分块读取?
chunks(size: int): bytes分块读取数据不易出现内存溢出
UploadedFile 的属性解释例子
name: str上传的文件名sd.html
size: int上传文件的大小
content_type: strtext/html
content_type_extra: dict
charset: str
TemporaryUploadedFile

TemporaryUploadedFile < [[#UploadedFile]]

TemporaryUploadedFile 的方法解释例子
temporary_file_path(): str获取上传文件暂存在磁盘中的位置windows 上会暂存在 C:\Users\Administrator\AppData\Local\Temp\<file>
InMemoryUploadedFile

InMemoryUploadedFile < [[#UploadedFile]]

认证

django 的用户验证系统负责处理 用户账号、组、权限、基于 cookie 的会话、密码哈希化系统、为登录用户或限制内容提供表单和视图工具、可插拔的后端系统等

涉及的包和模块:

  • django.contrib.auth => authenticate(), login(), logout()
  • django.contrib.auth.models => User, UserManager
  • django.contrib.contenttypes =>
  • django.contrib.contenttypes.models => ContentType, ContentTypeManager
  • django.contrib.admin => ModelAdmin
  • django.contrib.admin.site => register()
  • django.contrib.auth.decorators => @login_required(), @permission_required()
  • django.contrib.auth.mixins => LoginRequiredMixin, PermissionRequiredMixin

注册 app 和中间件:

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.contenttypes',
]
MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
]

=> auth 提供了 User, Group, Permission 三种模型及其中间模型(共 6 个模型),以及对应的数据库表
=> contenttypes 提供了 ContentType 模型及其对应的数据库表
注:auth 应用会为你的应用的每个模型创建 add / change / delete / view 四种权限(在 auth_permission 中)

User < AbstractUser; AbstractBaseUser, PermissionsMixin; Model

  • 属性
    => username, password, email, first_name, last_name
    => user_permissions: ManyRelatedManager, groups: ManyRelatedManager => 2 个多对多字段
  • 类属性 => objects: UserManager
  • 命令
    => python manage.py createsuperuser --username=joe --email=joe@example.com => 创建管理员
    => python manage.py changepassword <username> => 修改用户密码

UserManager

  • 方法 => create_user(username, email?, password?, first_name?, last_name?): User => 创建普通用户

ContentType

  • 类属性 => objects: ContentTypeManager

ContentTypeManager

  • 方法 => get_for_model(model: Model, for_concrete_model=True): ContentType => 获取应用模型唯一对应的 ContentType 实例

用户验证 => authenticate(username, password): User | None

权限和认证

例子:=> 寻找属于某个应用的指定权限

from django.contrib.auth.models import Permission
def get_permission(app: str, model: str, permission: str):
    return Permission.objects.all().get(
        content_type__app_label=app,
        content_type__model=model,
        codename__startswith=permission
    )
# 添加权限
user.user_permissions.add(get_permission('admin', 'user', 'change'))

判断是否有权限 => <User>.has_perm('<app>.<permission>_<model>'): bool
获取用户的用户权限 => <User>.get_user_permissions(): str[]
获取用户的组权限 => <User>.get_group_permissions(): str[]
获取用户的所有权限 => <User>.get_all_permissions()
注册权限 =>

content_type = ContentType.objects.get_for_model(m_user.User)
try:
	Permission.objects.create(
		codename="do_something_with_user",
		name='do something with user model',
		content_type=content_type,
	)
except IntegrityError:
	print('add permission failed')

缓存问题 => 用户权限不会实时更新给 User 的实例,如 <User>.has_perm("<app>.<permission>_<model>") 只会检查该实例的缓存中是否有该权限

代理模型相关 => 代理模型不会继承其父模型的权限

Meta => 模型的 Meta 内部类的 permissions: 2-tuple[] 能添加默认权限?

web请求认证

前置知识:[[python#装饰器]]

获取请求的用户 => request.user: User | AnonymousUser => 得到 已认证用户 或 匿名用户
判断用户是否已认证 => <User>.is_authenticatedAnonymousUser.is_authenticated

认证 => authenticate(username, password): User | None
登录 => login(req: HttpRequest, user: User, backend) => 将认证成功的 <user> 插入到 django_session 表中,从而实现登录
登出 => logout(req: HttpRequest)

例子:=> 登录视图

def my_view(req: HttpRequest):  
    username = req.POST["username"]
    password = req.POST["password"]  
    user = authenticate(req, username=username, password=password)
    if user is not None:
    	login(req, user)
    	# Redirect to a success page ...
    else:
    	print("user don't exists")

装饰器 @login_required(redirect_field_name='next', login_url=None) => 若用户没有登录,那么请求会重定向到 <login_url>?<redirect_field_name>={<req>.path}
注:<login_url> 默认为 settings 模块中的变量 LOGIN_URL

例子2:=> 函数视图上使用装饰器 @login_required() => 强制请求进行登录

# (1) 配置登录路由:
urlpatterns = [
   path('login/', v_index.do_login, name='login'),
   path('any_view/', v_index.any_view, name='any_view'),
]

# (2) 定义登录视图:
# GET => 获取登录表单
# POST => 实现登录功能和转发
@require_http_methods(['GET', 'POST'])
def do_login(req: HttpRequest):
    if req.method == 'GET':
        return render(req, 'login.html', {'next': req.GET.get("next")})

    username = req.POST["username"]
    password = req.POST["password"]
    user = authenticate(req, username=username, password=password)
    if user is not None:
        login(req, user)
        print('login success')
        return HttpResponseRedirect(req.GET.get("next"))
    else:
        print('login failed')
        return HttpResponseRedirect(f'/login/?next={req.GET.get("next")}')

# (3) 在某个视图调用前进行验证
@login_required(redirect_field_name='next', login_url="/login/")
def any_view(req: HttpRequest):
	...

=> 类似的装饰器:

  • user_passes_test(test_func: (req) => bool, login_url=None, redirect_field_name='next') => test_func 返回 False 时会重定向
  • permission_required(perm: tuple, login_url=None, raise_exception=False) => 检查用户是否有权限(可以是多个权限)

混合类 LoginRequiredMixin

例子3:类视图上继承混合类 LoginRequiredMixin => 强制请求进行登录

... 

class AnyView(LoginRequiredMixin, TemplateView):
	redirect_field_name='next'
	login_url="/login/"
	
    template_name = 'test.html'

注:LoginRequiredMixin 必须是第一个继承
=> 类似的混合类:

  • UserPassesTestMixin
  • PermissionRequiredMixin
  • AccessMixin
认证视图

django.contrib.auth.urls 包含 8 条路由:

pathname模板
login/loginregistration/login.html
logout/logoutregistration/logged_out.html
password_change/password_changeregistration/password_change_form.html
password_change/done/password_change_doneregistration/password_change_done.html
password_reset/password_resetregistration/password_reset_form.html
password_reset/done/password_reset_doneregistration/password_reset_done.html
reset/<uidb64>/<token>/password_reset_confirmregistration/password_reset_confirm.html
reset/done/password_reset_completeregistration/password_reset_complete.html
密码管理
邮箱

鸣谢:Django使用QQ邮箱发送邮件 - 南风丶轻语 - 博客园 (cnblogs.com)

涉及的包和模块:

  • django.core.mail => send_mail(), send_mass_mail(), mail_admins(), mail_managers()

Django 支持多种邮箱后端:

  • django.core.mail.backends.smtp.EmailBackend
  • django.core.mail.backends.console.EmailBackend
  • django.core.mail.backends.filebased.EmailBackend
  • django.core.mail.backends.locmem.EmailBackend
  • django.core.mail.backends.dummy.EmailBackend

配置 smtp 的例子:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# qq smtp 主机
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
# qq 邮箱
EMAIL_HOST_USER = '1708494470@qq.com'
# qq 邮箱上开启 SMTP 后得到的授权码
EMAIL_HOST_PASSWORD = 'gotcasiskumechjd'

# EMAIL_USE_SSL = True
# EMAIL_SSL_KEYFILE = 
# EMAIL_SSL_CERTFILE = 

send_mail() => 发送普通邮件

send_mail(subject, message, from_email, recipient_list: str[], 
	fail_silently=False, auth_user?, auth_password?, connection?: Backend,
	html_message?
)

注:若指定了 html_message,那么邮件会变为 multipart/alternative 实例

send_mass_mail() => 大量发送邮件

send_mass_mail(data: tuple, 
	fail_silently=False, auth_user?, auth_password?, connection?: Backend
)

注:data 中每个元素为 (subject, message, from_email, recipient_list)
注2:只能发 text/plain

mail_admins() => 给网站所有者们发送邮件

mail_admins(subject, message, 
	fail_silently=False, connection?: Backend, 
	html_message?
)

=> 用到的额外配置:

EMAIL_SUBJECT_PREFIX = '[mysite:django] '
SERVER_EMAIL = '1708494470@qq.com'
ADMINS = [
    ('lx', '1506218507@qq.com'),
    # ('drk', '1708494470@qq.com')
]

mail_managers() => 给管理员们发送邮件

mail_admins(subject, message, 
	fail_silently=False, connection?: Backend, 
	html_message?
)

=> 用到的额外配置:

EMAIL_SUBJECT_PREFIX = '[mysite:django] '
SERVER_EMAIL = '1708494470@qq.com'
MANAGERS = [
    ('lx', '1506218507@qq.com'),
    # ('drk', '1708494470@qq.com')
]

EmailMessage

EmailMessage(subject?, body?, from_email?, to?: tuple | array,
	bcc?: tuple | array, connection?: Backend, 
	attachments?: MIMEBase | (filename, content, minetype),
	headers?: dict, cc?, reply_to?
)

=> 方法:

  • send(fail_silently=False): int => 发送邮件消息,发送成功返回 1,否则返回 0
  • message(): SafeMIMEMultipart =>
  • recipients(): array => 返回一个包含邮件所以收件人的列表
  • attach(filename?, content?, mimetype?) => 添加附件
  • attach_file(path, minetype?) => 相对于项目根目录发送附件,甚至可以把 db.sqlite3作为附件
    => 例子:
em = EmailMessage("嘿咻咻", "my content ...", '1708494470@qq.com', [user.email])  
em.attach('geji.html',  
          '<img src="https://ts4.cn.mm.bing.net/th?id=OVP.FqDKX5rpgVBhbjE43cH4PQEsC7&w=608&h=342&c=7&rs=1&qlt=90&o=6&dpr=1.3&pid=1.7">',  
          'text/html')  
em.attach_file('manage.py')  
em.send()

get_connection()

常见问题:

  • 使用 qq stmp 服务时,确保用于发邮件的邮箱只能是 EMAIL_HOST_PASSWORD 对应的邮箱

缓存

缓存的作用 => 加速访问者访问网站的内容;网站暂存和检索某些 python 对象

缓存引擎:

  • Memcached
  • Redis
  • 数据库缓存
  • 文件系统缓存
  • 本地内存缓存(默认)
  • 虚拟缓存(用于开发模式)
  • 自定义缓存后端

注:可以配置多条缓存,不同缓存可以使用不同缓存引擎

需要缓存某个实例(视图 / 模板 / 任何 python 对象)时,都需要指定 缓存引擎缓存键缓存版本号缓存实例
=> [[#缓存参数]],[[#缓存中间件]] 描述了一些必要配置
=> [[#视图缓存]],[[#模板片段缓存]] 描述了两种缓存过程
=> [[#访问缓存]] 能直观的了解这个过程

Memcached

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        # "BACKEND": "django.core.cache.backends.memcached.PyLibMCCache",
        "LOCATION": "127.0.0.1:11211",
        # "LOCATION": ["172.19.26.240:11211", "172.19.26.242:11211"],
        # "LOCATION": "unix:/tmp/memcached.sock",
    },
}

Redis

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        # "LOCATION": "redis://username:password@127.0.0.1:6379",
        # "LOCATION": [
        #     "redis://127.0.0.1:6379",  # leader
        #     "redis://127.0.0.1:6378",  # read-replica 1
        #     "redis://127.0.0.1:6377",  # read-replica 2
        # ],
    }
}

=> pip install redis, pip install hiredis
数据库缓存

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
        "LOCATION": "my_cache_table",
    }
}

=> python manage.py createcachetable => 创建缓存表
文件系统缓存

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "/var/tmp/django_cache",  
        # "LOCATION": "c:/foo/bar",
    }
}

注:LOCATION 目录不宜包含在 MEDIA_ROOTSTATICFILES_FINDERS 所指定的目录中,否则可能造成敏感数据的泄露
本地内存缓存

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "unique-snowflake",
    }
}

虚拟缓存

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.dummy.DummyCache",
    }
}
缓存参数

TIMEOUT => 缓存超时时间(s),默认为 300;设为 None 时将永久缓存;设为 0 时不缓存
OPTIONS => 传递给缓存后端的选项,通常包含 MAX_ENTRIES: int(最大缓存条目),CULL_FREQUENCY: int(条目达到 MAX_ENTRIES 时,将删除 MAX_ENTRIES / CULL_FREQUENCY 个条目)
KEY_PREFIX => 缓存实例进行保存或检索时,区别它们的 cache_key 的一部分(与缓存引擎 CACHES 的 key 一一对应)
注:cache_key 形如 <KEY_PREFIX>:<version>:<key>version 为缓存实例的版本号(默认为 1),key 是我们手动 或 Django 自动缓存实例时使用的键)
VERSION => 缓存实例的默认版本号
KEY_FUNCTION: str => 指定一条到某个函数的路径,该函数负责处理将缓存实例的 cache_key 进行更改,其形如 (cache_key: str) => str

例子:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "/var/tmp/django_cache",
        "TIMEOUT": 60,
        "OPTIONS": {"MAX_ENTRIES": 1000},
    }
}
缓存中间件

配置:

MIDDLEWARE = [
    "django.middleware.cache.UpdateCacheMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.cache.FetchFromCacheMiddleware",
]

注:中间件的执行顺序是从后往前依次执行的 => FetchFromCacheMiddleware 在请求阶段执行,UpdateCacheMiddleware 在响应阶段执行

其他配置:

  • CACHE_MIDDLEWARE_ALIAS => 用于存储的缓存别名
  • CACHE_MIDDLEWARE_SECONDS => 应缓存个页面的秒数,默认为 600
  • CACHE_MIDDLEWARE_KEY_PREFIX =>
视图缓存

django.views.decorators.cache => @cache_page()

@cache_page(timeout: int, cache?: str, key_prefix?: str)

  • timeout => 缓存过期时间(s)
  • cache => 指定缓存配置,即 CACHES 中的一个 key
  • key_prefix =>

通过路由缓存视图 => 根据 cache_page 的装饰器特性

  • 例子:
from django.views.decorators.cache import cache_page
urlpatterns = [
    path("foo/<int:code>/", my_view),
]
# => 
urlpatterns = [
    path("foo/<int:code>/", cache_page(60 * 15)(my_view)),
]

make_template_fragment_key()

模板片段缓存
{% load cache %}
{% cache 500 sidebar %}
    .. sidebar ..
{% endcache %}
访问缓存
相关模块和包内容
django.core.cachecaches, cache, CacheHandler
django.core.cache.backends.dbDatabaseCache

caches: CacheHandler 对象 => caches[cache_name: str]: DatabaseCache
cache: DatabaseCache 对象 => 等价于 caches['default']

DatabaseCache

  • set(key: str, value: any, timeout=DEFAULT_TIMEOUT, version=None) => 为一个数据设置缓存,具有时间限制(s) => 注:timeout 为 0 时不缓存,为 None 时缓存长时间不会过期
  • get(key: str, default=None, version=None): any => 获取一个缓存数据
  • add(key: str, value: any, timeout=DEFAULT_TIMEOUT, version=None) => 为一个数据设置缓存,仅当该 key 对应的数据不存在时
  • get_or_set(key: str, value: any, timeout=DEFAULT_TIMEOUT, version=None): any => 若 key 存在,那么返回 key 对应的值;否则将 key 对应的值设为 value,并返回 value
  • get_many(keys: str[], version=None): dict => 提供一组 key,返回一个缓存字典
  • delete(key: str, version=None) => 删除指定的键值对
  • delete_many(keys: str[], version=None) => 删除多个指定的键值对
  • clear() => 删除所有键值对
  • touch(key: str, timeout=DEFAULT_TIMEOUT, version=None) => 设置键值对的新的过期时间
  • incr(key: str, delta=1, version=None) => 增加键值对的过期时间
  • decr(key: str, delta=1, version=None) => 减少键值对的过期时间
  • close() => 关闭与缓存的连接

注:version 默认为 VERSION 指定的值,通常为 1

下游缓存

下游缓存 => 一般 Django 缓存的视图并不会区分请求的 request 是否不同,虽然有性能的优势,但是不能区分每个用户 => 造成安全问题

涉及的模块和包内容
django.views.decorators.vary@vary_on_headers(), @vary_on_cookie
django.views.decorators.cache@cache_control()
django.utils.cachepatch_vary_headers()

@vary_on_headers(*headers: str) => 通过头部信息来区分不同的请求

  • 例子:@vary_on_headers("User-Agent", "Cookie")

patch_vary_headers(res: HttpResponse, vary_headers: str[]) => 类似
=> 例子:

def my_view(request):
    response = render(request, "template_name")
    patch_vary_headers(response, ["User-Agent", "Cookie"])
    return response

@vary_on_cookie => 等价于 @vary_on_headers("Cookie")

用户通常面临两种缓存:

  • 浏览器缓存(私有缓存
  • 其服务提供商的缓存(公共缓存

@cache_control(private?: bool, public?: bool, max_age?: int) => 指定该视图是否允许公共缓存

patch_cache_control(res: HttpResponse, private?: bool, public?: bool, max_age?: int) => 同上
=> 例子:

@vary_on_cookie
def list_blog_entries_view(request):
    if request.user.is_anonymous:
        response = render_only_public_entries()
        patch_cache_control(response, public=True)
    else:
        response = render_private_and_public_entries(request.user)
        patch_cache_control(response, private=True)

    return response

会话

会话允许每个站点访问者存储和检索自己的数据

  • 基于 数据库 / 文件 的会话 => 存储于服务端
  • 基于 cookie 的会话 => 存储于客户端
  • 基于 缓存 的会话 => 依赖于 [[#缓存]] 的配置(可能基于 内存 / 数据库 / 文件 等)
涉及的模块和包内容
django.contrib.session.modelsSession

配置:

MIDDLEWARE = [
	'django.contrib.sessions.middleware.SessionMiddleware'
]

会话引擎

  • 基于数据库的会话 => Session 模型及其数据库表
  • 缓存会话 =>
  • 基于文件的会话
  • 基于 cookie 的会话

基于数据库的会话:

SESSION_ENGINE = "django.contrib.sessions.backends.db"
INSTALLED_APPS = [
	'django.contrib.sessions',
]

注:数据会保存在 django_session 表中
基于缓存的会话:

SESSION_CACHE_ALIAS = "caches_key" # 设置为 CACHES 配置的 key,默认为 "default"
# (1) 写透缓存(持久性缓存) => 会话读取使用缓存,或者如果数据已从缓存中逐出,则使用数据库 
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
# (2) 非持久性缓存 => 逐出过程 可能发生在缓存填满或缓存服务器重新启动时,这将意味着会话数据丢失,包括用户的注销状态
SESSION_ENGINE = "django.contrib.sessions.backends.cached"

基于文件的会话:

SESSION_ENGINE = "django.contrib.sessions.backends.file"
# 指定存储会话的目录
SESSION_FILE_PATH = BASE_DIR / "path/to/file-session-dir"

基于cookie的会话:

SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
# Django 脚手架初始化的项目会自动生成随机的 SECRET_KEY
SECRET_KEY = 

注:SESSION_COOKIE_HTTPONLY = TRUE 能防止 js 访问存储的数据
注2:由于 cookie 存储在客户端,通常使用 [[#加密签名]] 来保证数据安全

在视图中使用会话

涉及的模块和包

  • django.contrib.sessions.backends.base => SessionBase
  • django.contrib.sessions.backends.file => SessionStore
  • django.contrib.sessions.serializers => JSONSerializer
    注:SessionStore < SessionBase

<request>.session: SessionStore

SessionBase 的方法:

  • 重点)实现了 [[python#__something__|标准 dict 方法]]
  • get(key, default?): any, pop(key, default?): any
  • keys(): dict_keys, items(): dict_items, clear()
  • flush() => 删除 session => 其中的 cookie 也会被删除
  • set_test_cookie(), test_cookie_worked(): bool, delete_test_cookie() => 用于测试 cookie 是否可用
  • get_session_cookie_age(): int => 获取会话 cookie 的存活时间 (s)
  • get_expiry_age(modification=datetime.now(), expiry?): int => 获取会话 cookie 的剩余存活时间 (s)
  • get_expiry_date(): datetime => 获取 session 的过期日期
  • get_expire_at_browser_close(): bool => 会话 cookie 是否在用户关闭 web 浏览器时过期
  • clear_expired() => 删除已过期的会话
  • cycle_key() =>
    注:SESSION_COOKIE_AGE: int 默认为 1209600
    注2:cookie 存储的数据特定于某个 session,而且这些数据必须是可序列化的

SESSION_SERIALIZER => 序列化器,默认为 JSONSerializer,即存储的 Python 数据使用 json 序列化

在视图外使用会话
管理会话

会话保存的时机:

# Session is modified.
request.session["foo"] = "bar"

# Session is modified.
del request.session["foo"]

# Session is modified.
request.session["foo"] = {}

# Gotcha: Session is NOT modified, because this alters
# request.session['foo'] instead of request.session.
request.session["foo"]["bar"] = "baz"

request.session.modified = True => 告知 session 对象它已被修改 =>

SESSION_SAVE_EVERY_REQUEST: bool => 是否每次请求都更新 session
SESSION_EXPIRE_AT_BROWSER_CLOSE: bool = False => 在关闭浏览器时是否删除 session

python manage.py clearsessions => 清除会话存储,但不包括 缓存会话(缓存会在自动删除过时数据)、以及cookie会话(这种会话数据存储在客户端浏览器上)

会话相关的配置列表:

SESSION_CACHE_ALIAS
SESSION_COOKIE_AGE
SESSION_COOKIE_DOMAIN
SESSION_COOKIE_HTTPONLY
SESSION_COOKIE_NAME
SESSION_COOKIE_PATH
SESSION_COOKIE_SAMESITE
SESSION_COOKIE_SECURE
SESSION_ENGINE
SESSION_EXPIRE_AT_BROWSER_CLOSE
SESSION_FILE_PATH
SESSION_SAVE_EVERY_REQUEST
SESSION_SERIALIZER
扩展数据库后端
疑问

#疑问

消息

涉及的模块和包:

  • django.contrib.messages.storage.session => SessionStorage
  • django.contrib.messages.storage.cookie => CookieStorage
  • django.contrib.messages.storage.fallback => FallbackStorage
  • django.contrib.messages.storage.base => BaseStorage, Message
  • django.contrib.messages.constants => DEBUG, INFO, SUCCESS, WARNING, ERROR, DEFAULT_LEVELS, DEFAULT_TAGS
  • django.contrib.messages.api => add_message(), debug(), info(), success(), warning(), error(), get_messages(), set_level()
  • django.contrib.messages.views => SuccessMessageMixin

配置:

INSTALLED_APPS = [
	`django.contrib.messages`,
]
MIDDLEWARE = [
	`django.contrib.session.middleware.SessionMiddleware`,
	`django.contrib.message.middleware.MessageMiddleware`,
]
TEMPLATES = [
    {
        'OPTIONS': {
            'context_processors': [
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

存储后端

  • SessionStorage => 存储在请求的会话中的所有消息
  • CookieStorage => 将消息存储在 Cookie 中 => 使用密码哈希签名;cookie 数据超过 2048 字节时旧的消息会被覆盖
  • FallbackStorage => 默认的存储后端,使用 CookieStorageSessionStorage,拥有最好的性能
  • BaseStorage => 存储后端基类
    注:可以通过 MESSAGE_STORAGE 设置存储后端

消息级别 => 对应一个整数,以及一个字符串(消息标签

消息级别消息标签用途
DEBUG10debug与开发相关的消息
INFO20info给用户的参考消息
SUCCESS25success一个动作成功了
WARNING30warning未发生的故障,但可能即将发生
ERROR40error某项动作没有成功或发生了其他故障
注: MESSAGE_LEVEL: int 配置了需要处理的消息的最小级别
注2:DEFAULT_LEVELS: dict<str, int>, DEFAULT_TAGS: dict<int, str>
使用消息

添加消息:

  • add_message(req: HttpRequest, level: int, msg, extra_tags?: str, fail_silently=False)
  • debug(req: HttpRequest, msg, extra_tags?: str, fail_silently=False)
  • info(req: HttpRequest, msg, extra_tags?: str, fail_silently=False)
  • success(req: HttpRequest, msg, extra_tags?: str, fail_silently=False)
  • warning(req: HttpRequest, msg, extra_tags?: str, fail_silently=False)
  • error(req: HttpRequest, msg, extra_tags?: str, fail_silently=False)

获取消息:
get_messages(req: HttpRequest): BaseStorage => 可以看做是 Message 的数组

Message(level, message, extra_tags?)

  • level => 消息级别
  • message => 消息实际内容
  • extra_tags => 包含自定义消息标签的字符串,消息标签之间以空格 " " 分隔
  • level_tag => 消息标签
  • tags => 消息的所有标签

注册消息 => DEFAULT_TAGS[level] = tag

临时修改消息最低级别 => set_level(req: HttpRequest, level: int)

类视图添加消息 =>

标记要清理的消息 => <BaseStorage>.used = True
避免消息被清理 => <BaseStorage>.used = False
注:<BaseStorage>for 迭代后会进行 used 标记

信号

topic:信号

涉及的模块和包内容解释
django.dispatchSignal, @receiver()
django.db.models.signalspre_init, post_init, pre_save, post_save, pre_delete, post_delete, m2m_changed, class_prepared模型相关信号
django.db.models.signalspre_migrate, post_migrate迁移相关信号
django.core.signalsrequest_started, request_finished, got_request_exception请求/响应 相关信号
django.test.signalssetting_changed, template_rendered测试相关信号
django.db.backends.signalsconnection_created数据库相关信号

Signal(use_caching=False)

Signal 的方法:

  • connect(receiver: func, sender?, weak=True, dispatch_uid?)
  • send(sender, **named)
  • disconnect(receiver?: func, sender?, dispatch_uid?): bool

概念:

  • 信号处理器 => 一个 python 方法,形如 (sender, **kwargs) => void
  • 信号 => 一个 Signal 实例
  • 信号发送者 => 一个 python 对象
  • 绑定 处理器 和 信号:
    => 方式1 => @receiver(signal: Signal, **kwargs) => 信号处理器 接受 信号
    => 方式2 => <Signal>.connect(receiver: func, sender?, weak=True, dispatch_uid?) => 信号 连接 信号处理器
  • 发送信号
    注:django 会自动负责 request_finished, request_started, setting_changed, got_request_exception 等信号的发送 => 我们通常可以直接将这些信号与某些接收器进行连接
    注2:一对 信号 和 信号处理器 可能被绑定多次 =>

信号处理器过滤处理特定类型的 信号发送者 发送的信号 => @receiver(signal: Signal, sender=ExampleType)

安全

topic:Django中的用户认证加密签名, 发送邮件, Django 的安全性, 系统检查框架
ref:点击劫持保护, 系统检查框架,Django 异常

加密签名

SECRET_KEY: str => 用于生成签名值
注:配置会在 startproject 初始化项目时自动生成
SECRET_KEY_FALLBACKS =>

django.core.signing => Signer, BadSignature, TimestampSigner, SignatureExpired

Signer(*, key?: str, sep=":", salt?, algorithm?, fallback_keys?)

  • key => 默认为 SECRET_KEY 指定的值
  • sep => 不能是 字母 / 数字 / 连字符- / 下划线_
  • salt => 盐值
  • algorithm => hashlib 的算法类型,默认为 sha256
  • fallback_keys =>

Signer 的方法:

  • sign(original_val: any): str => 将数据进行签名
  • unsign(signed_val: str): any => 将签名的数据解签
  • sign_object(original_val: any, serializer=JSONSerializer, compress=False): str => 将数据进行签名
  • unsign_object(signed_val: str, serializer=JSONSerializer, **kwargs): any => 将签名的数据解签
    注:BadSignature 为解签异常

TimestampSigner(*, key?: str, sep=":", salt?, algorithm?) < Signer

TimestampSigner 的方法:

  • sign(original_val: any): str => 将数据进行签名,并附加时间戳
  • unsign(signed_val: str, max_age?: int | timedelta) => 若从签名到现在经过的时间不超过 max_age 秒,那么正常解签,否则会抛出异常 SignatureExpired

两个 TimestampSigner 对象签名/解签方法的方法的快捷方式:
dumps(obj, key?, salt="django.core.signing", serializer=JSONSerializer, compress=False) => 签名对象
loads(s, key?, salt="django.core.signing", serializer=JSONSerializer, max_age?, fallback_keys?) => 解签对象

CSRF

涉及的概念

  • keywords => CORS, CSRF
  • 中间件 => django.middleware.csrf.CsrfViewMiddleware
  • 框架 => DRF
  • 装饰器 => django.views.decorators.csrf.csrf_exempt

CSRF_TRUSTED_ORIGINS = ['http://localhost:5173']

跨域请求错误的三种解决方法:

  • 注释掉中间件 django.middleware.csrf.CsrfViewMiddleware => 取消跨域请求伪造防护机制
  • 视图前添加装饰器 csrf_exempt

注:浏览器发送不安全请求(如: post, put, delete)时会检查 response headers 的键 Access-Control-Allow-Origin 是否为本站的源(即是否有 协议://域名:端口号) => 在服务端添加上这个头即可

性能

topic:Django 缓存框架, 性能和优化

其他

topic:国际化和本地化, 日志, 扩展包, 异步支持
ref:日志, Unicode 数据

分页

django.core.paginator => Paginator, Page

Paginator(arr: any[], per_page: int)

  • per_page => 每页的对象个数

Paginator 的属性

  • count => 总共的对象个数
  • num_pages => 总页数
  • page_range: range => 页数范围,与 num_pages 差不多
    Paginator 的方法
  • page(num: int): Page => 获取某一页,下标从 1 开始

Page 的属性

  • object_list: any[]
    Page 的方法
  • __getitem__(index: int)
  • has_next(): bool
  • has_previous(): bool
  • has_other_pages(): bool
  • next_page_number(): int
  • start_index(): int
  • end_index(): int

实用技巧

路由直接转发到模板的闭包: (无需转发到其他的路由表,或视图)

from django.template.response import TemplateResponse
def to_template(t_path: str, context:dict = {}):
	def view(req: HttpResponse):
		return TemplateResponse(req, t_path, context=context)
	return view
# 例子:
urlpatterns = [
	path('', to_template("path/to/template.html"), {"foo": "bar"}),
]

参考:[[python#闭包]]

  • 34
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
对于武沛齐的Django笔记中的Ajax请求部分,以下是一些常见的步骤和注意事项: 1. 在前端页面中,使用JavaScript编写一个Ajax请求。可以使用原生的XMLHttpRequest对象,也可以使用jQuery等库来简化操作。例如,使用jQuery的$.ajax()函数可以发送Ajax请求。 2. 在Django中,创建一个处理Ajax请求的视图函数。这个视图函数需要使用装饰器`@csrf_exempt`来取消跨站请求伪造保护。 3. 在视图函数中,可以通过`request.is_ajax()`方法来判断请求是否为Ajax请求。如果是Ajax请求,可以使用`request.POST`或`request.GET`来获取前端发送的数据。 4. 处理完请求后,可以返回JSON格式的数据给前端。可以使用Django提供的`JsonResponse`类来方便地返回JSON响应。 以下是一个简单的示例代码: 前端页面中的JavaScript代码: ```javascript $.ajax({ type: "POST", url: "/ajax-request-url/", data: { key1: value1, key2: value2 }, success: function(response) { // 处理成功响应的逻辑 }, error: function(xhr, status, error) { // 处理错误响应的逻辑 } }); ``` Django中的视图函数: ```python from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt @csrf_exempt def ajax_request_view(request): if request.is_ajax(): key1 = request.POST.get('key1') key2 = request.POST.get('key2') # 处理数据逻辑 response_data = {'key': 'value'} return JsonResponse(response_data) else: return HttpResponseBadRequest("Bad Request") ``` 请注意,这只是一个简单的示例,实际的实现可能会根据具体需求有所不同。还请参考官方文档和其他资源以获取更详细的信息和示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

travis_acl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值