Django模型带条件自定义save方法实现 多用户同图标签存储方案问询
问题解答
方案合理性判断
你原计划把逻辑写在Tag模型的自定义save方法里并不合理,写在视图里也不是最优选择,核心原因如下:
Tag的save方法只有在你主动创建/更新Tag实例的时候才会触发,但你的业务逻辑触发时机是「用户提交新的标签标注(即创建Tagging实例)」,逻辑触发的节点完全不匹配,你根本没法通过Tag的save方法捕获到用户提交标注的动作。- 把逻辑写在视图里会出现业务逻辑分散的问题:如果后续你有多个入口会创建
Tagging(比如后台批量导入标注、API接口提交、异步任务处理标注等),视图里的逻辑只能覆盖Web请求场景,其他入口都会漏掉校验,导致业务规则不一致。
推荐实现方案
更合适的做法是把逻辑绑定在Tagging实例的创建动作上,两种可选实现:
方案1:重写Tagging的save方法
这是最直接的方案,所有创建Tagging的场景都会自动触发校验逻辑,不会遗漏。
示例代码:
from django.db import models from django.contrib.auth.models import User # 此处引入你项目里的Gameround、Pictures模型 class Tag(models.Model): LANGUAGES = (('en', 'english'), ('de', 'deutsch'), ('fr', 'francais') ) name = models.CharField(max_length=256) language = models.CharField(max_length=256, choices=LANGUAGES) # 可选增加联合唯一约束,避免同一个name+language重复存Tag class Meta: unique_together = ('name', 'language') class Tagging(models.Model): user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) gameround = models.ForeignKey(Gameround, on_delete=models.CASCADE, blank=True, default='') picture = models.ForeignKey(Pictures, on_delete=models.CASCADE, related_name='taggings', default='') tag = models.ForeignKey(Tag, on_delete=models.CASCADE, null=True, blank=True) # 注意这里暂时允许为空,或者你可以加tag_text字段存用户提交的原始标签内容 tag_text = models.CharField(max_length=256) # 新增字段存用户提交的原始标签内容,避免Tag未创建时没法关联 created = models.DateTimeField(editable=False, auto_now_add=True) score = models.PositiveIntegerField(default=0) def save(self, *args, **kwargs): # 只在第一次创建Tagging的时候校验,更新的时候不处理 is_new = self.pk is None super().save(*args, **kwargs) if is_new: # 统计同一张图、同一个标签文本、排除当前用户的标注数量 same_tag_count = Tagging.objects.filter( picture=self.picture, tag_text=self.tag_text ).exclude(user=self.user).count() # 满足≥1个其他用户提交过相同标签,就创建Tag(如果还没存在的话) if same_tag_count >=1: tag, created = Tag.objects.get_or_create( name=self.tag_text, # 这里language你可以根据自己的业务逻辑赋值,比如取用户设置的语言 language='en' ) # 把所有相同的Tagging都关联上正式的Tag Tagging.objects.filter( picture=self.picture, tag_text=self.tag_text ).update(tag=tag)
方案2:使用Django的post_save信号
如果你不想改动原有Tagging的save方法,也可以用信号实现,逻辑和上面完全一致,只是绑定方式不同,适合已经有大量现有代码创建Tagging的场景。
补充注意点
你当前给出的模型里Tagging直接关联了Tag的外键,如果严格按照「未达到提交数量的标签不存入Tag表」的需求,这个外键约束会导致你没法创建Tagging实例,所以建议你要么给Tag加个is_valid状态字段(未达标时先存Tag但是标记为无效),要么给Tagging加个tag_text字段存用户提交的原始标签内容,等达标后再关联正式的Tag实例。
内容的提问来源于stack exchange,提问作者CarinaTheBookworm
相关产品推荐
相关产品推荐

