django 如何实现数据的用户操作记录? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
huazhaozhe
V2EX    Python

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

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

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

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

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

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

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

    全局的 dict 是啥, python 的全局可用嘛, 还是借助外部比如 redis 啥的
    huazhaozhe
        9
    huazhaozhe  
    OP
       2019-09-24 22:35:50 +08:00
    @omri 让前端传改之前的数据也不太好, 本来改之前的数据数据库中都有, 而且前端传回来的数据也不完全可信, 如果有逻辑要依赖这个之前的数据的话更不好了
    huazhaozhe
        10
    huazhaozhe  
    OP
       2019-09-24 22:49:12 +08:00
    其实就是感觉 django 缺个像 flask 的 4 个全局对象, flask 的全局 request 在很多地方都可以用
    omri
        11
    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
        12
    omri  
       2019-09-24 23:10:26 +08:00
    好吧。。还要记录改之前的数据。。那我想到的除了前端回传就是上面大佬提到的覆写 filter 方法,在每个 filter 执行之前先记录当前的数据
    huazhaozhe
        13
    huazhaozhe  
    OP
       2019-09-24 23:21:50 +08:00 via Android
    @omri
    单独记录 request 或者单独记录一条数据的操作记录不难的,就是 2 个关联起来。。。
    然后单独记录一条数据的操作记录其实已经有第三方现成的插件了,我自己的话是用的信号机制实现的,没有深入到涉及 SQL
    真的有重写 manager filter 方法嘛,是啥意思我完全没懂,没有搜到文档哎
    omri
        14
    omri  
       2019-09-25 00:07:39 +08:00
    sivacohan
        15
    sivacohan  
    PRO
       2019-09-25 00:58:11 +08:00 via iPhone
    你提出的需求实际上包含两个问题。
    1. 数据变更日志
    2. 用户操作日志

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

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

    综上所述,数据变更是对应数据库表的,一般框架都能处理。操作日志是对应接口的,因为框架很难确定你使用的接口规范,所以这里需要你单独处理。
    操作日志的一个简易形式是字段形式式为 id operator api api_version request response。这个部分直接输出到日志文件里。而后配置日志文件的监听器,把需要展示给用户的部分再抓出来,这样就可以完整满足用户需求了。
    metamask
        16
    metamask  
       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
        17
    ohhe  
       2019-09-25 07:11:07 +08:00
    huazhaozhe
        18
    huazhaozhe  
    OP
       2019-09-25 10:17:21 +08:00
    @ohhe 这个做的比较好了
    这个必须在每个模型中定义一个 history, 不能抽象类继承, 并且一个每个模型都会创建一张记录表
    与用户关联的话, 依赖 django 的中间件, 如果使用 rest_framework 自定义用户验证的话, 得不到用户的, 所以必须每次更改的时候传入 user
    huazhaozhe
        19
    huazhaozhe  
    OP
       2019-09-25 10:46:32 +08:00
    @freakxx
    恩,是拆成 2 个部分, 不过现在要把 2 个部分关联起来, 也就是一个 request 引起的多个记录关联起来, 这些都要和用户关联

    跟踪一个实例的 field change 的话我是直接改__init__和__setattr__方法添加一个属性就可以实现, 并且只记录了更改的 field. 没有变的 field 不会记录, 在实例更改保存的时候使用了信号来保存更改之前之后的值, 直接 copy 整个实例 emmm 貌似太伤了
    huazhaozhe
        20
    huazhaozhe  
    OP
       2019-09-25 10:56:33 +08:00
    @sivacohan
    我这里其实是需要把 2 个部分关联起来的, 一边是 ORM 操作, 另一边是视图, 大部分情况下其实可以关联起来, 不过这里要做一个全局的, 也不要每个接口单独配置, 每个视图都要写逻辑的这种
    目前看来使用 threadlocals 应该可以
    huazhaozhe
        21
    huazhaozhe  
    OP
       2019-09-25 11:02:54 +08:00
    @n329291362
    @omri
    还真有这个写法, 不过官方文档只介绍了 QuerySet filter, 没想到 manager, 不过跟踪实例字段变化已经有更好的实现思路了
    manager 这部分也只是在 ORM 这边, 要和 request 有关系还是要在每个视图里面写逻辑
    lowman
        22
    lowman  
       2019-09-25 11:43:23 +08:00
    如果我没理解错楼主的需求的话, 我觉得 django signal 机制 可以实现这个需求
    huazhaozhe
        23
    huazhaozhe  
    OP
       2019-09-25 12:27:58 +08:00
    @lowman
    对的, 会用到, 只是全局跟踪变更记录的话, 用信号机制就可以
    omri
        24
    omri  
       2019-09-25 15:23:20 +08:00 via iPhone
    @huazhaozhe 有最终方案了吗?能分享一下那?共同学习哈
    huazhaozhe
        25
    huazhaozhe  
    OP
       2019-09-25 16:00:05 +08:00
    @omri
    我自己想法大概是这样子:
    全局 request 用 threadlocal, 这个比较简单, 上面有人说了有现成的
    数据的增删改记录第三方也有, 比如 django-simple-history, 在每个模型里面加个字段就可以, 与用户关联的话参考 django-simple-history 文档有相关描述, 是可以满足这个需求的, 不过我打算自己用 django 的信号机制写这部分逻辑, 可以全局记录, 没全局需求的话用 django-simple-history 应该完全够了
    kerwinlv12
        26
    kerwinlv12  
       2021-01-29 09:03:50 +08:00
    @huazhaozhe 我这边有好几个应用,我需要全局使用信号,我发现只能应用到一个 app 模块,期待回复,感谢
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2606 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 12:24 PVG 20:24 LAX 05:24 JFK 08:24
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86