Django 自定义日志到数据库

本文详细介绍如何在Django框架中实现对数据库操作的详细记录,包括创建、修改和删除对象时的具体字段变更,通过重构日志记录方法,增强Django的内置日志功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文将教大家如何基于 Django 内置的日志记录更新将详细的操作记录到数据库。

内置功能介绍

大家在使用 Django 的 Admin 页面时会发现有个最近动作(Recent actions) 的列表,这就是 Django 自带的日志记录功能。
在这里插入图片描述
这个功能记录到对象的操作记录,比如创建、更改、删除等操作,但是没有记录详细的字段操作记录,比如某个字段从什么值改成什么值。

内置模块介绍

django 内置的日志记录核心模块是 django.contrib.admin.models.LogEntry,以下是这个 model 的详情。

class LogEntry(models.Model):
    action_time = models.DateTimeField(
        _('action time'),
        default=timezone.now,
        editable=False,
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        models.CASCADE,
        verbose_name=_('user'),
    )
    content_type = models.ForeignKey(
        ContentType,
        models.SET_NULL,
        verbose_name=_('content type'),
        blank=True, null=True,
    )
    object_id = models.TextField(_('object id'), blank=True, null=True)
    # Translators: 'repr' means representation (https://docs.python.org/library/functions.html#repr)
    object_repr = models.CharField(_('object repr'), max_length=200)
    action_flag = models.PositiveSmallIntegerField(_('action flag'), choices=ACTION_FLAG_CHOICES)
    # change_message is either a string or a JSON structure
    change_message = models.TextField(_('change message'), blank=True)

    objects = LogEntryManager()

    class Meta:
        verbose_name = _('log entry')
        verbose_name_plural = _('log entries')
        db_table = 'django_admin_log'
        ordering = ('-action_time',)

原始的调用方法如下:

    def log_addition(self, request, object, message):
        """
        Log that an object has been successfully added.

        The default implementation creates an admin LogEntry object.
        """
        from django.contrib.admin.models import LogEntry, ADDITION
        return LogEntry.objects.log_action(
            user_id=request.user.pk,
            content_type_id=get_content_type_for_model(object).pk,
            object_id=object.pk,
            object_repr=str(object),
            action_flag=ADDITION,
            change_message=message,
        )

    def log_change(self, request, object, message):
        """
        Log that an object has been successfully changed.

        The default implementation creates an admin LogEntry object.
        """
        from django.contrib.admin.models import LogEntry, CHANGE
        return LogEntry.objects.log_action(
            user_id=request.user.pk,
            content_type_id=get_content_type_for_model(object).pk,
            object_id=object.pk,
            object_repr=str(object),
            action_flag=CHANGE,
            change_message=message,
        )

    def log_deletion(self, request, object, object_repr):
        """
        Log that an object will be deleted. Note that this method must be
        called before the deletion.

        The default implementation creates an admin LogEntry object.
        """
        from django.contrib.admin.models import LogEntry, DELETION
        return LogEntry.objects.log_action(
            user_id=request.user.pk,
            content_type_id=get_content_type_for_model(object).pk,
            object_id=object.pk,
            object_repr=object_repr,
            action_flag=DELETION,
        )

重构方法

我们重新构建3个方法:

import json

from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
from django.contrib.contenttypes.models import ContentType


def get_content_type_for_model(obj):
    return ContentType.objects.get_for_model(obj, for_concrete_model=False)


def field_value_to_json(f_value):
    if str(type(f_value)) in [
        "<class 'django.db.models.fields.files.FieldFile'>",
        "<class 'django.db.models.fields.files.ImageFile'>",
        "<class 'django.db.models.fields.files.ImageFieldFile'>",
        "<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>"
    ]:
        value = f_value.name
    elif str(type(f_value)) in [
        "<class 'uuid.UUID'>",
        "<class 'datetime.datetime'>",
        "<class 'datetime.date'>",
        "<class 'bool'>"
    ]:
        value = str(f_value)
    elif 'models' in str(type(f_value)):
        value = f_value.pk
    elif str(type(f_value)) in [
        "<class 'decimal.Decimal'>"
    ]:
        value = str(float(f_value))
    else:
        value = str(f_value)
    return value


def create_addition_log(user_id, oj):
    lg = LogEntry.objects.log_action(
        user_id=user_id,
        content_type_id=get_content_type_for_model(oj).pk,
        object_id=oj.pk,
        object_repr=str(oj),
        action_flag=ADDITION,
    )
    # 因为 LogEntry 不会保存具体数据,需要另外将操作涉及的数据存入该条记录中
    addition_message_dict = {
        'addition': {
            'fields': list(),
            'values': list()
        }
    }
    for i in oj._meta.fields:
        fields = i.attname
        value = getattr(oj, fields)
        addition_message_dict['addition']['fields'].append(fields)
        addition_message_dict['addition']['values'].append(field_value_to_json(value))
    addition_message = json.dumps(addition_message_dict, ensure_ascii=False)
    lg.change_message = addition_message
    lg.save()
    return True


def create_change_log(user_id, oj, origin_data, new_data):
    lg = LogEntry.objects.log_action(
        user_id=user_id,
        content_type_id=get_content_type_for_model(oj).pk,
        object_id=oj.pk,
        object_repr=str(oj),
        action_flag=CHANGE,
    )
    change_message_dict = {
        'changed': {
            'fields': list(),
            'origin_values': list(),
            'new_values': list(),
        }
    }
    for fields, value in new_data.items():
        origin_value = origin_data.get(fields)
        if value != origin_value and fields in origin_data:
            change_message_dict['changed']['fields'].append(fields)
            change_message_dict['changed']['origin_values'].append(field_value_to_json(origin_value))
            change_message_dict['changed']['new_values'].append(field_value_to_json(value))
    change_message = json.dumps(change_message_dict, ensure_ascii=False)
    lg.change_message = change_message
    lg.save()
    return True


def create_delete_log(user_id, oj):
    lg = LogEntry.objects.log_action(
        user_id=user_id,
        content_type_id=get_content_type_for_model(oj).pk,
        object_id=oj.pk,
        object_repr=str(oj),
        action_flag=DELETION,
    )
    # 因为 LogEntry 不会保存具体数据,需要另外将操作涉及的数据存入该条记录中
    delete_message_dict = {
        'delete': {
            'fields': list(),
            'values': list()
        }
    }
    for i in oj._meta.fields:
        fields = i.attname
        value = getattr(oj, fields)
        delete_message_dict['delete']['fields'].append(fields)
        delete_message_dict['delete']['values'].append(field_value_to_json(value))
    delete_message = json.dumps(delete_message_dict, ensure_ascii=False)
    lg.change_message = delete_message
    lg.save()
    return True

方法重构好了以后,就可以在相应的位置进行调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值