django 如何实现数据的用户操作记录?

2019-09-24 16:44:01 +08:00
 huazhaozhe
这里操作记录就是数据库里一条记录的增删改查的记录了(包括有外键关系的字段), 改的话记录得包括修改之前的值和修改之后的值, 全局的, 不要每个模型都要写逻辑或者类 /方法或者装饰器, 因为有的地方不一定能得到 request, 也不可能都把 request 传进去或者全都加一遍装饰器, 而且有的逻辑会更改那些表提前并不知道, 比如有 ContentType 这种

目前自己使用 django 的信号实现了全局的增删改的记录, 查没有实现, 第三方插件也只有增删改
与用户关联的目前还没有实现
更进一步, 需要记录每个用户在每一个请求的过程中所有的操作记录, 也就是每个操作记录是和一个用户的某一次请求相关联的

另外思否那边貌似人好少, 提个问都没几个人看, 不知道这里用 django 的多不多呀
7581 次点击
所在节点    Python
26 条回复
Vegetable
2019-09-24 17:25:07 +08:00
threadlocal 可破
a719114136
2019-09-24 17:50:28 +08:00
继承 Manager 重写他的 filter 等方法,然后你的 model 使用你重写的 Manager。具体细节记不清了,好久不写 Django 了。

不过最好还是在你要记录的地方手动写代码。
huazhaozhe
2019-09-24 19:27:06 +08:00
@Vegetable 全局对象,可以,有点像 flask 的 LocalStuck
n329291362
2019-09-24 19:35:30 +08:00
huazhaozhe
2019-09-24 21:57:14 +08:00
@n329291362 恩,已经了解到了全局对象
huazhaozhe
2019-09-24 21:59:26 +08:00
@n329291362 emmm Manager 有 filter,我不知道...每个地方都手动写代码也太不好了, 设计的目地就是一个全局的用户操作记录, 而不是某几个应用
omri
2019-09-24 22:02:52 +08:00
没太明白,到底是记录数据库记录的增删改查记录,还是记录用户的操作记录呢?还有我还挺好奇什么样的地方拿不到 request。
我这边设计了一套记录用户操作记录的方法,前提是采用前后端分离的架构,前端请求“改”的时候要把当前数据(即改前数据)也传过来,django 这边用自定义的 middleware 来解析 request 并把相应的操作存到数据库,然后在展示时设计一个全局的 dict 来将原始数据转换成可读的中文内容
huazhaozhe
2019-09-24 22:28:51 +08:00
@omri 应该都包括吧, 用户的一个操作一般对应了一个请求嘛, 但是不同情况下同一个 URL 请求到后台具体会引起那些表的那些记录的增删改查的那个一个操作都是不确定的, 所以记录了 request 的 url, 还记录增删改查个中的哪一个, 如果是改的话得记录改前改后的值分别是啥, 并且这些记录都和这个用户关联起来

这个操作记录是要全局有效的, 每个应用都会用到, 并且新增应用(也就是新的 model 的增删改查)的时候, 不再关心也不需要再写操作记录方面的任何逻辑, 就像这个操作记录不存在一样

request 大部分时候是能够拿到的, 但是为了上面的要求, 在操作记录的实现逻辑代码里面不能同时得到 request 和这个 request 到底引起那些模型的那些实例的那些字段的改变

全局的 dict 是啥, python 的全局可用嘛, 还是借助外部比如 redis 啥的
huazhaozhe
2019-09-24 22:35:50 +08:00
@omri 让前端传改之前的数据也不太好, 本来改之前的数据数据库中都有, 而且前端传回来的数据也不完全可信, 如果有逻辑要依赖这个之前的数据的话更不好了
huazhaozhe
2019-09-24 22:49:12 +08:00
其实就是感觉 django 缺个像 flask 的 4 个全局对象, flask 的全局 request 在很多地方都可以用
omri
2019-09-24 23:01:18 +08:00
@huazhaozhe 我大概理解你的意思了。我说的方法是记录 request 的方法,而你想要的是记录 request 的同时,记录这个 request 增删改查了哪些数据?
如果是这样的话:
1.django.db.connections 存放了 django 运行过程中的所有 raw sql 语句。
https://docs.djangoproject.com/en/2.2/faq/models/#how-can-i-see-the-raw-sql-queries-django-is-running
2.django 的 middleware 可以在每个请求前和请求后记录上面的 raw sql,比较差异就知道该 request 进行了哪些 sql 操作( process_request 和 process_response )
https://docs.djangoproject.com/en/2.2/topics/http/middleware/
3.如何标识唯一的 request ?我想到的为每个 request 生成唯一的 uuid,上面大佬们提到的 threadlocals 或许更好,这个我没试过
不知道能否给你提供一些思路
omri
2019-09-24 23:10:26 +08:00
好吧。。还要记录改之前的数据。。那我想到的除了前端回传就是上面大佬提到的覆写 filter 方法,在每个 filter 执行之前先记录当前的数据
huazhaozhe
2019-09-24 23:21:50 +08:00
@omri
单独记录 request 或者单独记录一条数据的操作记录不难的,就是 2 个关联起来。。。
然后单独记录一条数据的操作记录其实已经有第三方现成的插件了,我自己的话是用的信号机制实现的,没有深入到涉及 SQL
真的有重写 manager filter 方法嘛,是啥意思我完全没懂,没有搜到文档哎😄
omri
2019-09-25 00:07:39 +08:00
sivacohan
2019-09-25 00:58:11 +08:00
你提出的需求实际上包含两个问题。
1. 数据变更日志
2. 用户操作日志

问题 1 有框架级解决方案,一般叫做 ActiveLog。Django 有三个以上的库来解决这个问题。

问题 2 本质上是基于操作审计产生的需求,搜索关键词用 AuditLog 或者 OperationLog。但是因为这种日志往往与实际接口有关,所以还是需要逐个接口配置。尤其像你说的查询操作,这里查询往往会有多表连表的情况,直接记录表名不太合适。

综上所述,数据变更是对应数据库表的,一般框架都能处理。操作日志是对应接口的,因为框架很难确定你使用的接口规范,所以这里需要你单独处理。
操作日志的一个简易形式是字段形式式为 id operator api api_version request response。这个部分直接输出到日志文件里。而后配置日志文件的监听器,把需要展示给用户的部分再抓出来,这样就可以完整满足用户需求了。
freakxx
2019-09-25 01:59:08 +08:00
拆成 2 个部分

记录的话用信号机制,用 init 事先 copy (性能的话会有损耗)
搜索关键词 django signal field change
参考
https://stackoverflow.com/questions/36719566/identify-the-changed-fields-in-django-post-save-signal
https://stackoverflow.com/questions/1197674/actions-triggered-by-field-change-in-django


记录访问操作,考虑做一个中间件
流过 process request 或者 view request 做一下记录
搜索关键词 django middleware log
参考
https://djangosnippets.org/snippets/428/
https://stackoverflow.com/questions/862522/django-populate-user-id-when-saving-a-model/12977709#12977709
ohhe
2019-09-25 07:11:07 +08:00
huazhaozhe
2019-09-25 10:17:21 +08:00
@ohhe 这个做的比较好了
这个必须在每个模型中定义一个 history, 不能抽象类继承, 并且一个每个模型都会创建一张记录表
与用户关联的话, 依赖 django 的中间件, 如果使用 rest_framework 自定义用户验证的话, 得不到用户的, 所以必须每次更改的时候传入 user
huazhaozhe
2019-09-25 10:46:32 +08:00
@freakxx
恩,是拆成 2 个部分, 不过现在要把 2 个部分关联起来, 也就是一个 request 引起的多个记录关联起来, 这些都要和用户关联

跟踪一个实例的 field change 的话我是直接改__init__和__setattr__方法添加一个属性就可以实现, 并且只记录了更改的 field. 没有变的 field 不会记录, 在实例更改保存的时候使用了信号来保存更改之前之后的值, 直接 copy 整个实例 emmm 貌似太伤了
huazhaozhe
2019-09-25 10:56:33 +08:00
@sivacohan
我这里其实是需要把 2 个部分关联起来的, 一边是 ORM 操作, 另一边是视图, 大部分情况下其实可以关联起来, 不过这里要做一个全局的, 也不要每个接口单独配置, 每个视图都要写逻辑的这种
目前看来使用 threadlocals 应该可以

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/603768

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX