笔记主要基于官方文档,从中提取要点和记录笔记,关键处包含了官方文档链接。详见官方文档。
官方文档:Django documentation
博客推荐:Django2.2教程
目录
静态文件、中间件、后台管理 等常用功能的笔记
静态文件
官方文档中:可参考静态文件入门。 更多关于设置和框架的资料,参考 管理静态文件 和 静态文件指南。部署静态文件 介绍了如何在真实服务器上使用静态文件。
项目中的CSS、图片、js等都是静态文件。
Django中,提供了django.contrib.staticfiles ,它将各个应用的静态文件(和一些你指明的目录里的文件)统一收集起来,这样一来,在生产环境中,这些文件就会集中在一个便于分发的地方。
配置静态文件
-
项目目录下创建一个名为
static
的目录。Django 将在该目录下查找静态文件。通常,将每个应用的静态文件放入各自的 命名空间 。也就是把这些静态文件放入 另一个 与应用名相同的目录中(在static目录下创建)。 -
确保
INSTALLED_APPS
包含了django.contrib.staticfiles
。 -
在配置文件中,定义
STATIC_URL
,设置访问静态文件对应的URL地址。例子:STATIC_URL = '/static/'
-
在模板中,用
static
模板标签基于配置STATICFILES_STORAGE
位给定的相对路径构建 URL。{% load static %} 使用静态文件: <img src="{% static "my_app/example.jpg" %}" alt="My image">
静态文件保存在程序中名为 static
的目录中。
工程中可能包含未与任何应用绑定的静态资源。除了在 apps 中使用 static/
目录,可以在setting.py配置文件中定义一个目录列表 (STATICFILES_DIRS
) ,设置静态文件存放的物理目录,Django 会从中寻找静态文件。例子:
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static"), ]
参考
STATICFILES_FINDERS
配置的文档了解staticfiles
是如何找到你的文件的细节。静态文件的查找顺序:
- Django会先去配置的目录去找。
- 找不到的话,再去应用的static目录下去找
导入from django.conf import setting,使用 sprint(settings.STATICFILES_FINDERS) 就可以看到查找的顺序:
['django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder']
上面所述其实不适合生产环境!参考 部署静态文件 了解如何正确地在生产环境提供静态文件服务的策略。
注:良好的目录结构是每个应用都应该创建自己的urls、forms、views、models、templates和static,每个templates包含一个与应用同名的子目录,每个static也包含一个与应用同名的子目录。
2.中间件
Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件函数是django框架给我们预留的函数接口,让我们可以干预请求和应答的过程。
浏览器请求和应答中的中间件:红色箭头的是通过中间件进行干预
Django在中间件中预置了一些方法,这些方法的区别在于不同的阶段执行,对输入或输出进行干预。
在中间件类中,实现以下方法的至少一种。(中间件类名是自由定义的,但是方法名是固定的)
方法如下:
1)初始化:服务器响应第一个请求的时候调用一次,用于确定是否启用当前中间件。
def __init__(self, get_response): pass
2)处理请求前:在每个请求上,request对象产生之后,url匹配之前调用,返回None或HttpResponse对象。
def process_request(self, request): pass
3)处理视图前:
process_view(request, view_func, view_args, view_kwargs)
在每个请求上,url匹配之后,视图函数调用之前调用,返回None或HttpResponse对象。
def process_view(self, request, view_func, *view_args, **view_kwargs): pass
4)处理响应后:视图函数调用之后,所有响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse对象。
def process_response(self, request, response): pass
5)异常处理:
process_exception(request, exception)
当视图抛出异常时调用,在每个请求上调用,返回一个HttpResponse对象。
(如果注册的多个中间件类中包含process_exception函数的时候,调用的顺序跟注册的顺序是相反的。)
def process_exception(self, request,exception): pass
6)视图刚好执行完毕
process_template_response(request, response)
在视图刚好执行完毕之后被调用,在每个请求上调用,返回实现了render方法的响应对象;
request
是一个HttpRequest
对象。response
是TemplateResponse
对象(或者等效对象),它通过 Django 视图或中间件返回
设想一个场景,我们需要禁止某些ip的浏览器访问我们的网址。可以有以下解决办法:
法1:在每一个视图中进行判断,浏览器的ip是否在禁止列表中(使用request对象的META属性,可获取浏览器端的ip地址);
法2:使用装饰器,装饰器用到每一个视图中;
以上方法都会有代码较大冗余,这时就可以使用中间件。
(1)在应用的目录新建文件,通常命名为 middleware.py 。定义中间件类。(使用了process_view)
# 方法1:接收get_response参数
from django.http import HttpResponse
class BlockedIPSMiddleware(object):
'''中间件类'''
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
EXCLUDE_IPS = ['192.168.43.28'] # 禁止访问的地址列表
def process_view(self, request, view_func, *view_args, **view_kwargs):
'''视图函数调用之前会调用'''
user_ip = request.META['REMOTE_ADDR']
print(user_ip)
if user_ip in BlockedIPSMiddleware.EXCLUDE_IPS: # 运行验证时,启动服务时指明ip,设置ALLOWED_HOSTS = ['*']
return HttpResponse('<h1>Forbidden</h1>')
# 方法2:兼容旧版本Django(推荐)
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
class BlockedIPSMiddleware(MiddlewareMixin):
EXCLUDE_IPS = ['192.168.43.28'] # 禁止访问的地址列表
def process_view(self, request, view_func, *view_args, **view_kwargs):
'''视图函数调用之前会调用'''
user_ip = request.META['REMOTE_ADDR']
print(user_ip)
if user_ip in BlockedIPSMiddleware.EXCLUDE_IPS: # 运行验证时,启动服务时指明ip,设置ALLOWED_HOSTS = ['*']
return HttpResponse('<h1>Forbidden</h1>')
可以使用 MiddlewareMixin 使中间件与1.10之前和1.10之后的Django版本兼容: 升级 Django 1.10 之前的中间件
(2)注册激活中间件组件,将其添加到 Django setting.py 设置中的 MIDDLEWARE
列表中。在 MIDDLEWARE
中,每个中间件组件由字符串表示:指向中间件工厂的类或函数名的完整 Python 路径。
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'booktest.middleware.BlockedIPSMiddleware', # 注册激活中间件类(booktest为应用名
]
关于中间件的一个坑:https://blog.csdn.net/qq_23996069/article/details/104922501
3.后台管理
官方文档:管理Admin
相关博客:https://www.liujiangblog.com/course/django/157
Admin站点是Django有别于其它Web框架最重要的一点。通过读取你的模型数据,快速构造出一个可以对实际数据进行管理的Web站点,常用于开发测试,简单管理等场合,适用于部门内部为工作方便的场合,但不建议在生产环境中使用。
Admin在创建应用时,默认会启用。使用时需注意:
- 确保添加 '
django.contrib.admin
' 和它所依赖的django.contrib.auth、
django.contrib.contenttypes
,、django.contrib.messages
anddjango.contrib.sessions
到setting.py的INSTALLED_APPS
中。- 在settings文件中的TEMPLATES中的
context_processors
选项内添加django.contrib.auth.context_processors.auth
和django.contrib.messages.context_processors.messages
这两条。同样,将django.contrib.auth.middleware.AuthenticationMiddleware
和django.contrib.messages.middleware.MessageMiddleware
添加到settings的MIDDLEWARE
内。默认情况下,这些都是配置好的。- 决定哪些模型需要在admin内进行管理,在
admin.py
文件中注册它们。- 对于每个模型,可以创建一个对应的
ModelAdmin
类,这个类将封装对模型的所有自定义设置。- 实例化一个AdminSite,将模型、模型对应的ModelAdmin类传给它。
- 链接AdminSite的URLs和URLconf.。这一步通常默认已经完成。
用
python manage.py createsuperuser
命令创建管理员账户。打开/admin/ 访问Admin登陆。
3.1.Admin的使用
1) 本地化。语言和时区本地化。
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
2) 创建超级管理员。
python mange.py createsuperuser
3) 注册模型类。
决定哪些模型需要在admin内进行管理,在admin.py
文件中注册它们。
from django.contrib import admin
from 你的应用名.models import 你的模型名
# Register your models here.
admin.site.register(你的模型名)
4) 自定义管理页面。
自定义模型管理类。
注册模型类的时候给register函数添加第二个参数,就是自定义模型管理类的名字。
3.2.自定义模型管理类
只对模型类注册往往是不够,有时需要对admin进行各种深度定制,以满足我们的需求。这时就要使用Django为我们提供的模型管理类 ModelAdmin类:
1)创建一个ModelAdmin的子类,2)注册的时候,将原模型和ModelAdmin的子类耦合起来
ModelAdmin类是一个模型在admin页面里的展示方法。通常保存在app的admin.py文件。点击ModelAdmin objects 查看官方文档和例子。
ModelAdmin属性 options
真正用来定制admin的手段,大部分都集中在这些ModelAdmin内置的属性上。它有许多内置属性,帮助我们自定义admin的界面和功能。详情可点击ModelAdmin options查看官网,或参考:https://www.liujiangblog.com/course/django/158
所有的属性都定义在ModelAdmin的子类中,如下方式:
from django.contrib import admin
class AuthorAdmin(admin.ModelAdmin):
date_hierarchy = 'pub_date'
常用的如下(点击可查看官网示例):
ModelAdmin.list_per_page:设置每页显示的数量。
ModelAdmin.list_display:列表页中的列。注意,list不仅可以写模型类的属性,还可以写模型类的方法,值为方法的返回值。
ModelAdmin.list_filter: 列表页右侧过滤栏
ModelAdmin.search_fields: 列表页上方的搜索框,以什么属性搜索传什么参数:search_fields = ['atitle']
ModelAdmin.fields:编辑页显示字段的顺序
ModelAdmin.fieldsets:编辑页分组的显示,根据字段对页面进行分组显示或布局了。
关联对象
在一对多的关系中,可以在一端的编辑页面中编辑多端的对象,嵌入多端对象的方式包括表格、块两种。
- 类型 InlineModelAdmin objects:表示在模型的编辑页面嵌入关联模型的编辑。
- 子类 TabularInline:以表格的形式嵌入。
- 子类 StackedInline:以块的形式嵌入。
admin中无法直接访问关联对象的属性或方法,可以在模型类中封装方法,访问关联对象的成员。
以块的形式:定义一个类,继承于admin.StackedInline。model属性写多类的名字。在一类的模型管理类,定义一个属性 inlines = [定义的类] 。
以块的形式:定义一个类,继承于admin.TabularInline。
上面一些相关的例子如下:(以地区管理的模型类为例)
models.py
from django.db import models
# Create your models here.
class AreaInfo(models.Model):
'''地址模型类'''
# 地区名称
atitle = models.CharField(verbose_name='标题', max_length=20) # verbose_name:在admin中,指定属性的列名
# 自关联属性
aParent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
def __str__(self): # 在admin中,以字符串显示字段
return self.atitle
def title(self): # 定义函数,在admin中,可以显示函数的返回值
return self.atitle
title.admin_order_field = 'atitle' # 在admin中,指定方法title对应的列可以依照atitle排序
title.short_description = '地区名称' # 在admin中,指定方法title对应的列的标题
def parent(self):
if self.aParent is None: # 处理没有父级地区导致的错误
return ''
return self.aParent.atitle
parent.short_description = '父级地区名称'
admin.py
from django.contrib import admin
from booktest.models import AreaInfo
# Register your models here.
# 关联对象,以块的形式,继承于admin.StackedInline
# class AreaStackedInline(admin.StackedInline):
# # model属性写多类的名字
# model = AreaInfo
# extra = 2 # 控制编辑页的空行数
# 关联对象,以块的形式,继承于admin.TabularInline
class AreaTabularInline(admin.TabularInline):
model = AreaInfo
extra = 2
# 创建一个ModelAdmin的子类
class AreaInfoAdmin(admin.ModelAdmin):
'''地区模型管理类'''
list_per_page = 10 # 指定每页显示10条数据
list_display = ['id', 'atitle', 'title', 'parent']
actions_on_bottom = True # 在下方显示actions的下拉列表框
actions_on_top = False
# list_filter = ['atitle'] # 列表页右侧过滤栏
search_fields = ['atitle'] # 列表页上方的搜索框
# fields = ['aParent', 'atitle'] # 编辑页显示字段的顺序
# 编辑页分组的显示
fieldsets = (
('基本', {'fields': ['atitle']}),
('高级', {'fields': ['aParent']})
)
# inlines = [AreaStackedInline]
inlines = [AreaTabularInline]
# 注册的时候,将原模型和ModelAdmin的子类耦合起来
admin.site.register(AreaInfo, AreaInfoAdmin)
3.3.重写模板自定义Admin页
官方文档:Overriding admin templates
复制模板文件base_site.html到项目目录,进行修改。详见官方文档。
-----end-----