You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中创建QuestionnaireAssignment时如何自动生成带参QuestionnaireInstance?

问题描述

我有以下Django模型:

class Questionnaire(models.Model):
    questionnaire_name = models.CharField(max_length=120, default="")
    ...

class Question(models.Model):
    question_body = models.TextField()
    ...

class QuestionnaireContent(models.Model):
    questionnaire = models.ForeignKey(Questionnaire)
    question = models.ForeignKey(Question)
    ...

QuestionnaireContent 用作问卷模板的问题列表,通过下面的模型将问卷分配给用户:

class QuestionnaireAssignment(models.Model):
    user = models.ForeignKey(User)
    questionnaire = models.ForeignKey(Questionnaire)

我希望创建 QuestionnaireAssignment 实例时,自动生成 QuestionnaireInstance 实例(每个模板问题对应一条记录,带visibility字段供用户后续定制),该模型定义如下:

class QuestionnaireInstance(models.Model):
    user = models.ForeignKey(User)
    questionnaire = models.ForeignKey(Questionnaire)
    question = models.ForeignKey(Question)
    visibility = models.BooleanField()

我知道可以重写 QuestionnaireAssignment 的save方法,但想找更简洁优雅的实现方式。


解决方案

除了重写save方法,还有两种更符合Django最佳实践的优雅方案:

1. 使用Django信号(post_save)

信号是Django处理模型事件的标准方式,能实现代码解耦,适合这种"创建A时自动生成B"的场景:

from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from django.db import transaction
from .models import QuestionnaireAssignment, QuestionnaireContent, QuestionnaireInstance

@receiver(post_save, sender=QuestionnaireAssignment)
@transaction.atomic
def create_questionnaire_instance(sender, instance, created, **kwargs):
    # 仅在新建Assignment时执行,更新操作跳过
    if created:
        # 获取该问卷模板下的所有问题
        template_questions = QuestionnaireContent.objects.filter(questionnaire=instance.questionnaire)
        # 批量组装Instance对象
        instances_to_create = [
            QuestionnaireInstance(
                user=instance.user,
                questionnaire=instance.questionnaire,
                question=qc.question,
                visibility=True  # 默认设为可见,可按需调整
            )
            for qc in template_questions
        ]
        # 批量插入数据库,比逐条save更高效
        QuestionnaireInstance.objects.bulk_create(instances_to_create)

把这段代码放在models.py或专门的signals.py文件中(如果用signals.py,需在apps.py的ready方法中注册信号)。

2. 自定义模型管理器(Manager)

如果想把生成实例的逻辑封装在模型层,自定义管理器是更内聚的选择,调用起来也更直观:

from django.db import models
from django.contrib.auth.models import User
from django.db import transaction
from .models import QuestionnaireContent, QuestionnaireInstance

class QuestionnaireAssignmentManager(models.Manager):
    def assign_to_user(self, user, questionnaire):
        with transaction.atomic():
            # 先创建Assignment实例
            assignment = self.create(user=user, questionnaire=questionnaire)
            # 生成对应的Instance记录
            template_questions = QuestionnaireContent.objects.filter(questionnaire=questionnaire)
            instances = [
                QuestionnaireInstance(
                    user=user,
                    questionnaire=questionnaire,
                    question=qc.question,
                    visibility=True
                )
                for qc in template_questions
            ]
            QuestionnaireInstance.objects.bulk_create(instances)
            return assignment

# 给QuestionnaireAssignment绑定自定义管理器
class QuestionnaireAssignment(models.Model):
    user = models.ForeignKey(User)
    questionnaire = models.ForeignKey(Questionnaire)
    objects = QuestionnaireAssignmentManager()

使用时直接调用QuestionnaireAssignment.objects.assign_to_user(user_obj, questionnaire_obj),就能同时完成Assignment创建和Instance生成。

关键注意点

两种方案都加上了transaction.atomic(),用来保证原子性——避免出现"Assignment创建成功,但Instance生成失败"的数据不一致情况。

内容的提问来源于stack exchange,提问作者Greg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 05:45:33