django框架有自带的日志管理工具,在数据库中表名是django_admin_log,模型名称是LogEntry。下面以django v2.2版本进行分析。
分析django_admin_log表的结构
使用SQL命令DESC查看django_admin_log表的结构
DESC django_admin_log;
Field |Type |Null|Key|Default|Extra |
---------------+-----------------+----+---+-------+--------------+
id |int |NO |PRI| |auto_increment|
action_time |datetime |NO | | | |
object_id |text |YES | | | |
object_repr |varchar(200) |NO | | | |
change_message |text |NO | | | |
content_type_id|int |YES |MUL| | |
user_id |int |NO |MUL| | |
action_flag |smallint unsigned|NO | | | |
主要业务字段如下:
- action_time:操作时间
- object_id:对象id,用于存放对象的主键
- object_repr:对象显示名称
- change_message:修改信息,一般是json格式
- content_type_id:内容类型id,django特有
- user_id:操作的用户id
- action_flag:操作标志位,在代码中标识如下:1是新增,2是修改,3是删除
分析LogEntry模型
在django框架中,操作日志的模型名称是LogEntry。位置在django/contrib/admin/models.py,
LogEntry类的主要方法如下:
- is_addition:是否是新增操作
- is_change:是否是修改操作
- is_deletion:是否是删除操作
以上三个,主要是去判断action_flag的标识位。
- get_change_message:生成change_message信息,这是最复杂的方法
- get_edited_object:获取已经编辑的对象
- get_admin_url:获取admin的URL
get_change_message方法分析
get_change_message方法的完整代码如下:
def get_change_message(self):
"""
If self.change_message is a JSON structure, interpret it as a change
string, properly translated.
"""
if self.change_message and self.change_message[0] == '[':
try:
change_message = json.loads(self.change_message)
except json.JSONDecodeError:
return self.change_message
messages = []
for sub_message in change_message:
if 'added' in sub_message:
if sub_message['added']:
sub_message['added']['name'] = gettext(sub_message['added']['name'])
messages.append(gettext('Added {name} "{object}".').format(**sub_message['added']))
else:
messages.append(gettext('Added.'))
elif 'changed' in sub_message:
sub_message['changed']['fields'] = get_text_list(
sub_message['changed']['fields'], gettext('and')
)
if 'name' in sub_message['changed']:
sub_message['changed']['name'] = gettext(sub_message['changed']['name'])
messages.append(gettext('Changed {fields} for {name} "{object}".').format(
**sub_message['changed']
))
else:
messages.append(gettext('Changed {fields}.').format(**sub_message['changed']))
elif 'deleted' in sub_message:
sub_message['deleted']['name'] = gettext(sub_message['deleted']['name'])
messages.append(gettext('Deleted {name} "{object}".').format(**sub_message['deleted']))
change_message = ' '.join(msg[0].upper() + msg[1:] for msg in messages)
return change_message or gettext('No fields changed.')
else:
return self.change_message
逻辑如下:
1、判断change_message字段存储的数据,第一个字符是不是方括号;
1.1 如果是,则使用json提取内容,如果提取失败,则直接返回change_message数据
1.2 如果提取成功,遍历change_message字段,分别判断key是added,changed,还是deleted;
1.2.1 如果是added,获取added中的name,拼接成Added {name} "{object}".的消息;
1.2.2 如果是changed,获取changed中的每一个fields,拼接成Changed {fields} for {name} "{object}".的消息,如果fields没有名称,则直接拼接Changed {fields}.;
1.2.3 如果是,获取deleted中的name,拼接成Deleted {name} "{object}".的消息;
2、如果不是,则直接返回change_message字段的数据。
在哪调用LogEntry
那么django框架是在哪生成这些系统日志呢?查阅源码,是在ModelAdmin类里面,主要设计三个方法:
- log_addition:添加新增日志
- log_change:添加修改日志
- log_deletion:添加删除日志
这几个方法分别又被_changeform_view、changelist_view、_delete_view等方法调用。当有表单提交数据过来的时候,就会被调用一次。
其中log_change的message生成非常复杂,需要调用construct_change_message方法来生成message,construct_change_message方法是在django\contrib\admin\utils.py,会对比提交的表单和字段formsets生成change_message。
changelog
- 20221224:初稿