5.2 管理后台实现原理
2025-02-17
- 使用管理后台需要:先注册
admin.site.register()方法@admin.register()装饰器方法
- 使用ModelAdmin自定义管理后台的样式和功能
5.2.1 Python装饰器
- 考虑问题:如何打印函数的执行时间?
简单装饰器
- 定义一个打印函数执行时间的装饰器

import time
def exec_time(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
print('%ss elapsed for %s' % (time.time()-start, func.__name__))
return res
return wrapper
# exec_time:接受一个函数作为参数,且参数被wrapper包裹起来
# wrapper执行函数并打印时间,最后将wrapper返回
# wrapper函数的参数定义是(*args, **kwargs), 所以可以接受任意参数的调用装饰器:本质上是一个函数或类,他的返回值也是一个函数或类
上面的方式,总体来说不够方便;下面,使用Python提供的语法
糖@,把装饰器放入到函数的定义处,并执行函数
@语法糖,在使用装饰器会方便许多需要给函数添加额外的功能,可以不需要修改函数定义,也不需要修改函数的调用方式,这其实就是
面向切面编程的效果
带参数的装饰器
- 需要给装饰器传递参数,实际上需要写一个返回装饰器的函数
import time
def exec_time_with_name(name):
def decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
print('(%s): %ss elapsed for %s' % (name, time.time()-start, func.__name__))
return res
return wrapper
return decorator
上面的例子,exec_time_with_name装饰器就是对之前简单装饰器的函数封装,使用时,需要传递一个参数name
装饰器的其他问题:
- 如在打印函数的属性时,打印它的__name__属性,会显示为 wrapper
要解决上面的问题,需要使用Python内置的
functools.wraps装饰器,作用:将原函数对象的属性复制到包装函数对象如重新定义exec_time_with_name装饰器
import time
import functools
def exec_time_with_name(name):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time
res = func(*args, **kwargs)
print('(%s): %ss elapsed for %s' % (name, time.time()-start, func.__name__))
return res
return wrapper
return decorator- 在此定义其他函数的属性,并打印__name__属性,会发现正常
- 重新定义exec_time装饰器也可采用类似的方式
import time
import functools
def exec_time(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args, **kwargs)
print('%ss elapsed for %s' % (time.time()-start, func.__name__))
return res
return wrapper使用装饰器的场景:
- 打印日志,事务处理,权限校验等
- 通过装饰器,可以将原本在函数中但又与函数功能本身无关的代码抽离出来,解耦的同事也提高代码的重用性
- Django中的很多模块都使用了装饰器,如:注册Model的
@admin.register
5.2.2 contenttypes应用分析
- contenttypes是Django的内置应用,会记录项目中所有App和model的对应关系,并记录于ContentType中
- ContentType的定义(django/contrib/contenttypes/models.py)
- 创建Django项目的时候,INSTALLED_APPS就包含了contenttypes应用,经过数据库迁移后,生成了django_content_type表,并记录了Django内置应用的Model信息,加入新的Model,会自动地创建ContentType实例
- 强大的功能:通过记录的信息动态地访问Model对象
model_class
- 核心方法,获取当前ContentType实例所对应的Model对象
- model_name是对大小写不敏感的(在获取Model对象的时候传递的是
model_name.lower())
get_object_for_this_type
- 通过传递关键字参数可以获取到Model实例对象
- 先通过model_class方法获取到Model对象,在使用查询管理器get到匹配关键字参数的实例对象
get_all_object_for_this_type
根据提供的关键字参数返回QuerySet,实现原理与get_object_for_this_type类似
ContentType自己提供了查询管理器 ContentTypeManager
ContentTypeManager 继承自 models.Manager,所以如get filter等方法都直接继承自父类
ContentTypeManager的两个重要方法
- get_for_id方法
- 通过id获取ContentType实例对象,先从缓存中获取,获取不到才会从数据表中检索
- 通过id查询,尽量使用此方法
- get_for_model方法
- 通过传递的Model或Model实例获取对应的ContentType实例对象
- get_for_id方法