django之中间件、缓存、信号、admin内置后台
分类:
IT文章
•
2022-07-20 11:19:19
目录:
一、中间件
1、什么是中间件?

由上图可知,中间件是位于wsgi和路由系统中间的环节。用户请求和和回应请求都会经过中间件。
看下面的代码在settings里中间件的类:
1 MIDDLEWARE_CLASSES = [
2 'django.middleware.security.SecurityMiddleware',
3 'django.contrib.sessions.middleware.SessionMiddleware',
4 'django.middleware.common.CommonMiddleware',
5 'django.middleware.csrf.CsrfViewMiddleware',
6 'django.contrib.auth.middleware.AuthenticationMiddleware',
7 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
8 'django.contrib.messages.middleware.MessageMiddleware',
9 'django.middleware.clickjacking.XFrameOptionsMiddleware',
10 ]
View Code
当有请求过来的时候,默认从上倒下执行!然后在返回的时候从下面在返回回去
2、自定义中间件
中间件中可以定义五个方法,分别是:
- process_request(self,request)
- process_view(self, request, callback, callback_args, callback_kwargs)
- process_exception(self, request, exception) 这个方法只有在出现错误的时候才会触发
- process_response(self, request, response)
- process_template_response(self,request,response) 这个方法只有在返回对象中有render方法的时候才执行,如render_to_response('/index/')
以上方法的返回值可以是None和HttpResonse对象,如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户。

在django的的project目录下创建一个middleware目录,并创建一个md.py的脚本,内容如下:
1 class Testmiddle:
2 def process_request(self,request):
3 print 'Testmiddle process_request'
4 def process_view(self, request, callback, callback_args, callback_kwargs):
5 print 'Testmiddle process_view'
6 def process_exception(self, request, exception):
7 pass
8 def process_response(self, request, response):
9 print 'Testmiddle process_response'
10 return response
11
12 class Nextmiddle:
13 def process_request(self,request):
14 print 'Nextmiddle process_request'
15 def process_view(self, request, callback, callback_args, callback_kwargs):
16 print 'Nextmiddle process_view'
17 def process_exception(self, request, exception):
18 pass
19 def process_response(self, request, response):
20 print 'Nextmiddle process_response'
21 return response
修改settings.py文件,在MIDDLEWARE_CLASSES中添加我们创建的中间件
1 MIDDLEWARE_CLASSES = [
2 'django.middleware.security.SecurityMiddleware',
3 'django.contrib.sessions.middleware.SessionMiddleware',
4 'django.middleware.common.CommonMiddleware',
5 'django.middleware.csrf.CsrfViewMiddleware',
6 'django.contrib.auth.middleware.AuthenticationMiddleware',
7 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
8 'django.contrib.messages.middleware.MessageMiddleware',
9 'django.middleware.clickjacking.XFrameOptionsMiddleware',
10 'middleware.middle.Testmiddle',
11 'middleware.middle.Nextmiddle',
12 ]
1 Testmiddle process_request
2 Nextmiddle process_request
3 Testmiddle process_view
4 Nextmiddle process_view
5 This app01 Views.index
6 Nextmiddle process_response
7 Testmiddle process_response
从输出结果可以看出:
他是先执行Testmiddle 的request 方法又执行了Nextmiddle的 process_request方法, 然后在执行Testmiddle的view方法,Nextmiddle的view方法
注意:django版本1.10以后,会报如下错误:

解决如下:
1 from django.utils.deprecation import MiddlewareMixin
2 class Testmiddle(MiddlewareMixin):
3 def process_request(self,request):
4 print 'Testmiddle process_request'
5 def process_view(self, request, callback, callback_args, callback_kwargs):
6 print 'Testmiddle process_view'
7 def process_exception(self, request, exception):
8 pass
9 def process_response(self, request, response):
10 print 'Testmiddle process_response'
11 return response
12
13 class Nextmiddle(MiddlewareMixin):
14 def process_request(self,request):
15 print 'Nextmiddle process_request'
16 def process_view(self, request, callback, callback_args, callback_kwargs):
17 print 'Nextmiddle process_view'
18 def process_exception(self, request, exception):
19 pass
20 def process_response(self, request, response):
21 print 'Nextmiddle process_response'
22 return response
View Code
注意:
* 老版本中在中间件中如果某个中间件类的方法返回的不是none,则后面的中间件不会再处理请求,也不会到达路由系统,而是直接到第一个process_response,开始所有的process_response处理
* 新版本中在中间件中如果某个中间件类的方法返回的不是none,则后面的中间件不会再处理请求,也不会到达路由系统,而是直接到最后一个process_response处理,然后返回给客户端
二、缓存
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django中提供了6种缓存方式:
- 开发调试
- 内存
- 文件
- 数据库
- Memcache缓存(python-memcached模块)
- Memcache缓存(pylibmc模块)
1、开发调试
1 # 此为开始调试用,实际内部不做任何操作
2 # 配置:
3 CACHES = {
4 'default': {
5 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎
6 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
7 'OPTIONS':{
8 'MAX_ENTRIES': 300, # 最大缓存个数(默认300)
9 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
10 },
11 'KEY_PREFIX': '', # 缓存key的前缀(默认空)
12 'VERSION': 1, # 缓存key的版本(默认1)
13 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
14 }
15 }
16
17
18 # 自定义key
19 def default_key_func(key, key_prefix, version):
20 """
21 Default function to generate keys.
22
23 Constructs the key used by all other methods. By default it prepends
24 the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
25 function with custom key making behavior.
26 """
27 return '%s:%s:%s' % (key_prefix, version, key)
28
29 def get_key_func(key_func):
30 """
31 Function to decide which key function to use.
32
33 Defaults to ``default_key_func``.
34 """
35 if key_func is not None:
36 if callable(key_func):
37 return key_func
38 else:
39 return import_string(key_func)
40 return default_key_func
View Code
2、内存
1 # 此缓存将内容保存至内存的变量中
2 # 配置:
3 CACHES = {
4 'default': {
5 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
6 'LOCATION': 'unique-snowflake',
7 }
8 }
9
10 # 注:其他配置同开发调试版本
View Code
3、文件
1 # 此缓存将内容保存至文件
2 # 配置:
3
4 CACHES = {
5 'default': {
6 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
7 'LOCATION': '/var/tmp/django_cache',
8 }
9 }
10 # 注:其他配置同开发调试版本
View Code
4、数据库
1 # 此缓存将内容保存至数据库
2
3 # 配置:
4 CACHES = {
5 'default': {
6 'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
7 'LOCATION': 'my_cache_table', # 数据库表
8 }
9 }
10
11 # 注:执行创建表命令 python manage.py createcachetable
View Code
5、Memcach缓存(python-memcached模块)
1 # 此缓存使用python-memcached模块连接memcache
2
3 CACHES = {
4 'default': {
5 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
6 'LOCATION': '127.0.0.1:11211',
7 }
8 }
9
10 CACHES = {
11 'default': {
12 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
13 'LOCATION': 'unix:/tmp/memcached.sock',
14 }
15 }
16
17 CACHES = {
18 'default': {
19 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
20 'LOCATION': [
21 '172.19.26.240:11211',
22 '172.19.26.242:11211',
23 ]
24 }
25 }
View Code
6、Memcache缓存(pylibmc模块)
1 # 此缓存使用pylibmc模块连接memcache
2
3 CACHES = {
4 'default': {
5 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
6 'LOCATION': '127.0.0.1:11211',
7 }
8 }
9
10 CACHES = {
11 'default': {
12 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
13 'LOCATION': '/tmp/memcached.sock',
14 }
15 }
16
17 CACHES = {
18 'default': {
19 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
20 'LOCATION': [
21 '172.19.26.240:11211',
22 '172.19.26.242:11211',
23 ]
24 }
25 }
View Code
7、应用方式:
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存
1 MIDDLEWARE = [
2 'django.middleware.cache.UpdateCacheMiddleware',
3 # 其他中间件...
4 'django.middleware.cache.FetchFromCacheMiddleware',
5 ]
6
7 CACHE_MIDDLEWARE_ALIAS = ""
8 CACHE_MIDDLEWARE_SECONDS = ""
9 CACHE_MIDDLEWARE_KEY_PREFIX = ""
View Code
1 方式一:
2 from django.views.decorators.cache import cache_page
3
4 @cache_page(60 * 15)
5 def my_view(request):
6 ...
7
8 方式二:
9 from django.views.decorators.cache import cache_page
10
11 urlpatterns = [
12 url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
13 ]
View Code
1 a. 引入TemplateTag
2
3 {% load cache %}
4
5 b. 使用缓存
6
7 {% cache 5000 缓存key %}
8 缓存内容
9 {% endcache %}
View Code
参考L:https://docs.djangoproject.com/en/1.9/topics/cache/
三、信号
Django中提供了“信号调度”,用于在框架执行操作时解耦。通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。
1、django内置信号
1 Model signals
2 pre_init # django的modal执行其构造方法前,自动触发
3 post_init # django的modal执行其构造方法后,自动触发
4 pre_save # django的modal对象保存前,自动触发
5 post_save # django的modal对象保存后,自动触发
6 pre_delete # django的modal对象删除前,自动触发
7 post_delete # django的modal对象删除后,自动触发
8 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
9 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
10 Management signals
11 pre_migrate # 执行migrate命令前,自动触发
12 post_migrate # 执行migrate命令后,自动触发
13 Request/response signals
14 request_started # 请求到来前,自动触发
15 request_finished # 请求结束后,自动触发
16 got_request_exception # 请求异常后,自动触发
17 Test signals
18 setting_changed # 使用test测试修改配置文件时,自动触发
19 template_rendered # 使用test测试渲染模板时,自动触发
20 Database Wrappers
21 connection_created # 创建数据库连接时,自动触发
2、对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:
1 from django.core.signals import request_finished
2 from django.core.signals import request_started
3 from django.core.signals import got_request_exception
4
5 from django.db.models.signals import class_prepared
6 from django.db.models.signals import pre_init, post_init
7 from django.db.models.signals import pre_save, post_save
8 from django.db.models.signals import pre_delete, post_delete
9 from django.db.models.signals import m2m_changed
10 from django.db.models.signals import pre_migrate, post_migrate
11
12 from django.test.signals import setting_changed
13 from django.test.signals import template_rendered
14
15 from django.db.backends.signals import connection_created
16
17
18 def callback(sender, **kwargs):
19 print("xxoo_callback")
20 print(sender,kwargs)
21
22 xxoo.connect(callback)
23 # xxoo指上述导入的内容
24 from django.core.signals import request_finished
25 from django.dispatch import receiver
26
27 @receiver(request_finished)
28 def my_callback(sender, **kwargs):
29 print("Request finished!")
3、django自定义信号
1 import django.dispatch
2 pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"]
View Code
1 def callback(sender, **kwargs):
2 print("callback")
3 print(sender,kwargs)
4
5 pizza_done.connect(callback)
View Code
1 from 路径 import pizza_done
2
3 pizza_done.send(sender='seven',toppings=123, size=456)
View Code
由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。
参考L:https://docs.djangoproject.com/en/dev/topics/signals/
四、admin后台
1、admin简介:
Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件,使用方式有:
依赖APP:
django.contrib.auth
django.contrib.contenttypes
django.contrib.messages
django.contrib.sessions
模板的context_processors:
django.contrib.auth.context_processors.auth
django.contrib.messages.context_processors.messages
中间件:
django.contrib.auth.middleware.AuthenticationMiddleware
django.contrib.messages.middleware.MessageMiddleware
2、配置路由:
urlpatterns
=
[
url(r
'^admin/'
, admin.site.urls),
]
3、当前配置的路由可以创建一些路由映射关系:
/admin/
/admin/login/
/admin/logout/
/admin/password_change/
/admin/password_change/done/
/admin/app名称/model名称/
/admin/app名称/model名称/add/
/admin/app名称/model名称/ID值/history/
/admin/app名称/model名称/ID值/change/
/admin/app名称/model名称/ID值/delete/
4、定制Admin
在admin.py中只需要讲Mode中的某个类注册,即可在Admin中实现增删改查的功能,如:
admin.site.register(models.UserInfo)
但是,这种方式比较简单,如果想要进行更多的定制操作,需要利用ModelAdmin进行操作,如:
1 方式一:
2 class UserAdmin(admin.ModelAdmin):
3 list_display = ('user', 'pwd',)
4
5 admin.site.register(models.UserInfo, UserAdmin) # 第一个参数可以是列表
6
7
8 方式二:
9 @admin.register(models.UserInfo) # 第一个参数可以是列表
10 class UserAdmin(admin.ModelAdmin):
11 list_display = ('user', 'pwd',)
ModelAdmin中提供了大量的可定制功能,如
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 list_display = ('user', 'pwd', 'xxxxx')
4
5 def xxxxx(self, obj):
6 return "xxxxx"
View Code
- list_display_links,列表时,定制列可以点击跳转。
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 list_display = ('user', 'pwd', 'xxxxx')
4 list_display_links = ('pwd',)
View Code
- list_filter,列表时,定制右侧快速筛选。
1 from django.utils.translation import ugettext_lazy as _
2
3 @admin.register(models.UserInfo)
4 class UserAdmin(admin.ModelAdmin):
5 list_display = ('user', 'pwd')
6
7 class Ugg(admin.SimpleListFilter):
8 title = _('decade born')
9 parameter_name = 'xxxxxx'
10
11 def lookups(self, request, model_admin):
12 """
13 显示筛选选项
14 :param request:
15 :param model_admin:
16 :return:
17 """
18 return models.UserGroup.objects.values_list('id', 'title')
19
20 def queryset(self, request, queryset):
21 """
22 点击查询时,进行筛选
23 :param request:
24 :param queryset:
25 :return:
26 """
27 v = self.value()
28 return queryset.filter(ug=v)
29
30 list_filter = ('user',Ugg,)
View Code
- list_select_related,列表时,连表查询是否自动select_related
- 分页相关
1 # 分页,每页显示条数
2 list_per_page = 100
3
4 # 分页,显示全部(真实数据<该值时,才会有显示全部)
5 list_max_show_all = 200
6
7 # 分页插件
8 paginator = Paginator
View Code
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 list_display = ('user', 'pwd','ug',)
4 list_editable = ('ug',)
View Code
- search_fields,列表时,模糊搜索的功能
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3
4 search_fields = ('user', 'pwd')
View Code
- date_hierarchy,列表时,对Date和DateTime类型进行搜索
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3
4 date_hierarchy = 'ctime'
View Code
- preserve_filters,详细页面,删除、修改,更新后跳转回列表后,是否保留原搜索条件
- ave_as = False,详细页面,按钮为“Sava as new” 或 “Sava and add another”
- save_as_continue = True,点击保存并继续编辑
1 save_as_continue = True
2
3 # 如果 save_as=True,save_as_continue = True, 点击Sava as new 按钮后继续编辑。
4 # 如果 save_as=True,save_as_continue = False,点击Sava as new 按钮后返回列表。
5
6 New in Django 1.10.
View Code
- save_on_top = False,详细页面,在页面上方是否也显示保存删除等按钮
- inlines,详细页面,如果有其他表和当前表做FK,那么详细页面可以进行动态增加和删除
1 class UserInfoInline(admin.StackedInline): # TabularInline
2 extra = 0
3 model = models.UserInfo
4
5
6 class GroupAdminMode(admin.ModelAdmin):
7 list_display = ('id', 'title',)
8 inlines = [UserInfoInline, ]
View Code
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3
4 # 定制Action行为具体方法
5 def func(self, request, queryset):
6 print(self, request, queryset)
7 print(request.POST.getlist('_selected_action'))
8
9 func.short_description = "中文显示自定义Actions"
10 actions = [func, ]
11
12 # Action选项都是在页面上方显示
13 actions_on_top = True
14 # Action选项都是在页面下方显示
15 actions_on_bottom = False
16
17 # 是否显示选择个数
18 actions_selection_counter = True
View Code
1 add_form_template = None
2 change_form_template = None
3 change_list_template = None
4 delete_confirmation_template = None
5 delete_selected_confirmation_template = None
6 object_history_template = None
View Code
- raw_id_fields,详细页面,针对FK和M2M字段变成以Input框形式
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3
4 raw_id_fields = ('FK字段', 'M2M字段',)
View Code
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 fields = ('user',)
View Code
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 exclude = ('user',)
View Code
- readonly_fields,详细页面时,只读字段
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 readonly_fields = ('user',)
View Code
- fieldsets,详细页面时,使用fieldsets标签对数据进行分割显示
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 fieldsets = (
4 ('基本数据', {
5 'fields': ('user', 'pwd', 'ctime',)
6 }),
7 ('其他', {
8 'classes': ('collapse', 'wide', 'extrapretty'), # 'collapse','wide', 'extrapretty'
9 'fields': ('user', 'pwd'),
10 }),
11 )
View Code
- 详细页面时,M2M显示时,数据移动选择(方向:上下和左右)
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 filter_vertical = ("m2m字段",) # 或filter_horizontal = ("m2m字段",)
View Code
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 ordering = ('-id',)
4 或
5 def get_ordering(self, request):
6 return ['-id', ]
View Code
- view_on_site,编辑时,是否在页面上显示view on set
1 view_on_site = False
2 或
3 def view_on_site(self, obj):
4 return 'https://www.baidu.com'
View Code
- radio_fields,详细页面时,使用radio显示选项(FK默认使用select)
1 radio_fields = {"ug": admin.VERTICAL} # 或admin.HORIZONTAL
View Code
- show_full_result_count = True,列表时,模糊搜索后面显示的数据个数样式
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 # show_full_result_count = True # 1 result (12 total)
4 # show_full_result_count = False # 1 result (Show all)
5 search_fields = ('user',)
View Code
- formfield_overrides = {},详细页面时,指定现实插件
1 from django.forms import widgets
2 from django.utils.html import format_html
3
4 class MyTextarea(widgets.Widget):
5 def __init__(self, attrs=None):
6 # Use slightly better defaults than HTML's 20x2 box
7 default_attrs = {'cols': '40', 'rows': '10'}
8 if attrs:
9 default_attrs.update(attrs)
10 super(MyTextarea, self).__init__(default_attrs)
11
12 def render(self, name, value, attrs=None):
13 if value is None:
14 value = ''
15 final_attrs = self.build_attrs(attrs, name=name)
16 return format_html('<textarea {}>
{}</textarea>',final_attrs, value)
17
18
19
20 @admin.register(models.UserInfo)
21 class UserAdmin(admin.ModelAdmin):
22
23 formfield_overrides = {
24 models.models.CharField: {'widget': MyTextarea},
25 }
View Code
- prepopulated_fields = {},添加页面,当在某字段填入值后,自动会将值填充到指定字段。
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3
4 prepopulated_fields = {"email": ("user","pwd",)}
View Code
PS: DjangoAdmin中使用js实现功能,页面email字段的值会在输入:user、pwd时自动填充
- form = ModelForm,用于定制用户请求时候表单验证
1 from app01 import models
2 from django.forms import ModelForm
3 from django.forms import fields
4
5
6 class MyForm(ModelForm):
7 others = fields.CharField()
8
9 class Meta:
10 model = models = models.UserInfo
11 fields = "__all__"
12
13 @admin.register(models.UserInfo)
14 class UserAdmin(admin.ModelAdmin):
15
16 form = MyForm
View Code
- empty_value_display = "列数据为空时,显示默认值"
1 @admin.register(models.UserInfo)
2 class UserAdmin(admin.ModelAdmin):
3 empty_value_display = "列数据为空时,默认显示"
4
5 list_display = ('user','pwd','up')
6
7 def up(self,obj):
8 return obj.user
9 up.empty_value_display = "指定列数据为空时,默认显示"
View Code