当前位置 : 主页 > 操作系统 > centos >

Django自带的Admin后台中如何获取当前登录用户

来源:互联网 收集:自由互联 发布时间:2023-08-21
需求背景 在使用Django快速开发一个IT 电脑、显示器资产管理小系统的时候,遇到一个问题是,当变更资产设备(新增、修改、删除)的时候,能记录是谁在什么时间进行的变更。 确认

需求背景

在使用Django快速开发一个IT 电脑、显示器资产管理小系统的时候,遇到一个问题是,当变更资产设备(新增、修改、删除)的时候,能记录是谁在什么时间进行的变更。

确认的是肯定是登录状态,但是在使用Django的signal中获取不到当前登录的用户

问题演示

1、定义资产设备模型和 自定义日志模型

class Device(models.Model):
    """
    IT资产设备,主要是电脑和显示器
    """
    pass


class CustomLogEntry(models.Model):
    """日志模型,记录其他模型上的变化记录"""
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
    action_time = models.DateTimeField(auto_now_add=True)
    model_name = models.CharField(max_length=100)
    model_pk = models.CharField(max_length=100)
    action = models.CharField(max_length=10)
    data = models.TextField()

    def __str__(self):
        return f"{self.user} {self.model_name} {self.action}"

2、定义信号

from django.contrib.auth.models import User
from django.contrib.admin.models import LogEntry
from django.db.models.signals import post_save
from django.dispatch import receiver

# 这里只记录Device模型的变化
# @receiver(post_save, sender=Device)
@receiver(post_save)
def log_save(sender, instance, created, **kwargs):
    print("sender: ", sender)
    action = 'CREATE' if created else 'UPDATE'
    print("current request in kwargs: ", kwargs)
    # current request in kwargs:  {'signal': <django.db.models.signals.ModelSignal object at 0x10b73d490>, 'update_fields': None, 'raw': False, 'using': 'default'}
    
    request = kwargs.get('requqest')
    print("instance: ", instance)
    print("current request in kwargs: ", request)

3、Device模型注册到admin后台,然后创建超级管理员账号,登录后台对 Device模型进行新增、修改的操作

过程日志输出如下

sender:  <class 'demoapp.models.Device'>
current request in kwargs:  {'signal': <django.db.models.signals.ModelSignal object at 0x103ec9730>, 'update_fields': None, 'raw': False, 'using': 'default'}
instance:  华为 x14 (2)
current request in kwargs:  None
sender:  <class 'django.contrib.admin.models.LogEntry'>
current request in kwargs:  {'signal': <django.db.models.signals.ModelSignal object at 0x103ec9730>, 'update_fields': None, 'raw': False, 'using': 'default'}
instance:  Changed “华为 x14 (2)” — Changed 状态.
current request in kwargs:  None

从上面的日志分析

3.1、从上面的log_save信号函数中知道,除了 sender/instance/created 之外的参数都在 kwargs 中

3.2、输出kwargs尝试获取request 我们发现是request是None,所以Django的信号中是没有request的参数的,那么就无法通过request来获取当前登录的用户

3.3、同时我们发现在未明确指定sender的情况,除了我们明确操作的Device模型之外,还多出来个 django.contrib.admin.models.LogEntry

3.4、查看 django_admin_log 表中的数据发现,Django默认是帮忙记录了各个模型的变化(可以尝试新增用户、修改用户,发现变更操作也记录到了 django_admin_log 表中去)

这里主要是研究自定义logEntry的情况下如果通过signal信号获取当前登录用户,关于 django_admin_log 记录的逻辑,我们下篇文章介绍

问题修复

查找了相关文档之后发现,django-threadlocals 模块可以很好的帮我们解决该问题

1、安装第三方模块

pip install django-threadlocals

2、配置对应的中间件

MIDDLEWARE = [
    # ... Other midddleware
    'threadlocals.middleware.ThreadLocalMiddleware',
]

3、在然后使用 threadlocals中的 `get_current_request` 

可以获取Django 后台的request,这个request中包括当前登录的user 

```python
from django.db.models.signals import post_save
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnoymousUser
from django.dispatch import receiver

from threadlocals.threadlocals import get_current_request


@receiver(post_save, sender=Device)
def log_save(sender, instance, created, **kwargs):
    if sender in [SysUser, Device]:
        request = get_current_request()
        if request:
            user = request.user
        else:
            user = AnonymousUser()
        action = 'CREATE' if created else 'UPDATE'
        data = str(instance.__dict__)
        CustomLogEntry.objects.create(user=user, model_name=sender.__name__, model_pk=instance.pk, action=action, data=data)

然后操作 Device的数据变更,然后查询 CustomLogEntry 记录,发现获取到了当前登录用户

Django自带的Admin后台中如何获取当前登录用户_django-threadlocals


网友评论