forked from ITStudio_OUC/ProgrammeHomework
113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
from django.db import models
|
||
from django.contrib.auth.models import User
|
||
from django.db.models.signals import post_save, pre_delete
|
||
from django.dispatch import receiver
|
||
import json
|
||
|
||
|
||
class Article(models.Model):
|
||
STATUS_CHOICES = (
|
||
("draft", "草稿"),
|
||
("published", "已发布"),
|
||
("archived", "归档"),
|
||
)
|
||
title = models.CharField(max_length=200, verbose_name="标题")
|
||
content = models.TextField(verbose_name="正文")
|
||
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者")
|
||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="draft", verbose_name="状态")
|
||
views = models.PositiveIntegerField(default=0, verbose_name="浏览量")
|
||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
|
||
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
|
||
is_deleted = models.BooleanField(default=False, verbose_name="是否软删除") # 加分项软删除
|
||
|
||
def __str__(self):
|
||
return self.title
|
||
|
||
class Meta:
|
||
verbose_name = "文章"
|
||
verbose_name_plural = "文章"
|
||
ordering = ["-created_at"]
|
||
|
||
|
||
class Attachment(models.Model):
|
||
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name="attachments", verbose_name="关联文章")
|
||
file = models.FileField(upload_to="attachments/", verbose_name="文件")
|
||
filename = models.CharField(max_length=200, verbose_name="文件名")
|
||
upload_time = models.DateTimeField(auto_now_add=True, verbose_name="上传时间")
|
||
downloads = models.PositiveIntegerField(default=0, verbose_name="下载次数") # 加分项下载计数
|
||
|
||
def __str__(self):
|
||
return self.filename
|
||
|
||
class Meta:
|
||
verbose_name = "附件"
|
||
verbose_name_plural = "附件"
|
||
ordering = ["-upload_time"]
|
||
|
||
|
||
class AuditLog(models.Model):
|
||
OP_TYPE_CHOICES = (
|
||
("create", "创建"),
|
||
("update", "更新"),
|
||
("publish", "发布"),
|
||
("archive", "归档"),
|
||
("delete", "删除"),
|
||
)
|
||
article = models.ForeignKey(Article, on_delete=models.SET_NULL, null=True, verbose_name="关联文章")
|
||
operator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name="操作人")
|
||
op_type = models.CharField(max_length=20, choices=OP_TYPE_CHOICES, verbose_name="操作类型")
|
||
diff = models.JSONField(default=dict, verbose_name="变更摘要")
|
||
time = models.DateTimeField(auto_now_add=True, verbose_name="操作时间")
|
||
|
||
def __str__(self):
|
||
return f"{self.get_op_type_display()} - {self.article}"
|
||
|
||
class Meta:
|
||
verbose_name = "审计日志"
|
||
verbose_name_plural = "审计日志"
|
||
ordering = ["-time"]
|
||
|
||
|
||
# ========== Django信号:自动生成审计日志(作业硬性要求!禁止在视图手动创建AuditLog) ==========
|
||
@receiver(post_save, sender=Article)
|
||
def article_post_save(sender, instance, created,** kwargs):
|
||
if created:
|
||
# 新建文章
|
||
AuditLog.objects.create(
|
||
article=instance,
|
||
operator=instance.author,
|
||
op_type="create",
|
||
diff={"title": instance.title, "status": instance.status}
|
||
)
|
||
else:
|
||
# 更新文章,记录状态变更
|
||
# kwargs里有update_fields,用来拿到改动字段
|
||
diff_data = {}
|
||
if kwargs.get("update_fields") and "status" in kwargs["update_fields"]:
|
||
diff_data["status"] = {"new": instance.status}
|
||
if instance.status == "published":
|
||
op_type = "publish"
|
||
elif instance.status == "archived":
|
||
op_type = "archive"
|
||
else:
|
||
op_type = "update"
|
||
else:
|
||
op_type = "update"
|
||
diff_data["title"] = instance.title
|
||
AuditLog.objects.create(
|
||
article=instance,
|
||
operator=instance.author,
|
||
op_type=op_type,
|
||
diff=diff_data
|
||
)
|
||
|
||
|
||
@receiver(pre_delete, sender=Article)
|
||
def article_pre_delete(sender, instance,** kwargs):
|
||
# 删除文章前触发信号
|
||
AuditLog.objects.create(
|
||
article=instance,
|
||
operator=instance.author,
|
||
op_type="delete",
|
||
diff={"title": instance.title}
|
||
) |