Python 在Django中创建模型时自动创建相关对象

Python 在Django中创建模型时自动创建相关对象,python,django,django-models,Python,Django,Django Models,我是Django的新手,请原谅我的无知:) 假设我有一个具有两个外键关系的模型,当我创建该模型的实例时,我希望它也自动为外键对象生成新实例。在本例中,我将课程注册建模为一个组,并将特定组作为模型上的外键引用 class Course(models.Model): student_group = models.OneToOneField(Group, related_name="course_taken") teacher_group = models.OneToOneField(

我是Django的新手,请原谅我的无知:)

假设我有一个具有两个外键关系的模型,当我创建该模型的实例时,我希望它也自动为外键对象生成新实例。在本例中,我将课程注册建模为一个组,并将特定组作为模型上的外键引用

class Course(models.Model):
    student_group = models.OneToOneField(Group, related_name="course_taken")
    teacher_group = models.OneToOneField(Group, related_name="course_taught")

    def clean(self):
        if self.id:
            try:
                self.student_group
            except Group.DoesNotExist:
                self.student_group, _ = Group.objects.get_or_create(name='_course_' + self.id + '_student')

            try:
                self.teacher_group
            except Group.DoesNotExist:
                self.teacher_group, _ = Group.objects.get_or_create(name='_course_' + self.id + '_teacher')
看起来我可以挂接到模型的clean方法中来实现这一点,但是我希望能够将整个过程打包到一个事务中,这样,如果它以后无法创建课程,它就不会创建相关的组对象。有没有办法做到这一点

还有,我在这里做的事情完全错了吗?Django提供了更好的方法吗?

您可以使用信号来处理这种情况:

from django.db import models

class Course(models.Model):
    student_group = models.OneToOneField(Group, related_name="course_taken")
    teacher_group = models.OneToOneField(Group, related_name="course_taught")


def create_course_groups(instance, created, raw, **kwargs):
    # Ignore fixtures and saves for existing courses.
    if not created or raw:
        return

    if not instance.student_group_id:
        group, _ = Group.objects.get_or_create(name='_course_' + self.id + '_student')
        instance.student_group = group

    if not instance.teacher_group_id:
        teacher_group, _ = Group.objects.get_or_create(name='_course_' + self.id + '_teacher')
        instance.teacher_group = teacher_group

    instance.save()

models.signals.post_save.connect(create_course_groups, sender=Course, dispatch_uid='create_course_groups')

最终我决定:

from django.db import models, transaction
class Course(models.Model):
    student_group = models.OneToOneField(Group, related_name="course_taken")

    @transaction.commit_on_success
    def save(self, *args, **kwargs):
        if not self.student_group_id:
            self.student_group, _ = Group.objects.get_or_create(name='_course_' + self.id + '_student')

        super(Course, self).save(*args, **kwargs)

编辑(2014/12/01):@Shasanoglu是正确的,由于id不存在,上述代码实际上不起作用。您必须在调用save之后创建相关对象(因此,您可以调用super.save,创建相关对象,更新此对象,然后再次调用super.save,这并不理想。否则,您可以从组名中省略id,这样就可以了)。但最终,我将自动相关对象创建完全移出了模型。我在自定义表单的save方法中完成了这一切,该方法更加简洁,并放弃了在管理界面中使用该模型(这就是为什么我坚持首先在model方法中完成所有这一切)

我在Django 1.7中的一个类似问题中使用了wjin的解决方案。我只需要做两个改变:

  • 成功时必须更改
    commit\u
    使用
    atomic
  • self.id
    无法工作,因为在创建新对象时,代码在设置id之前运行。我不得不使用其他名称作为组名
  • 以下是我最后做的:

    from django.db import models
    from django.contrib.auth.models import Group
    
    class Audit(models.Model):
    
        @transaction.atomic
        def save(self, *args, **kwargs):
            if not hasattr(self,"reAssessmentTeam"):
                self.reAssessmentTeam, _ = Group.objects.get_or_create(name='_audit_{}_{}'.format(self.project.id,self.name))
    
            super(Audit, self).save(*args, **kwargs)
    
        project = models.ForeignKey(Project, related_name = 'audits')
        name = models.CharField(max_length=100)
        reAssessmentTeam = models.OneToOneField(Group)
    

    我知道,如果名称太长或有人设法使用相同的名称,这可能会导致问题,但我稍后会处理这些问题。

    请查看我的项目,在该项目中,可以在创建父类时自动创建子模型实例

    例如,给定以下模型定义:

    from django.db import models
    from django_auto_one_to_one import AutoOneToOneModel
    
    class Parent(models.Model):
        field_a = models.IntegerField(default=1)
    
    class Child(AutoOneToOneModel(Parent)):
        field_b = models.IntegerField(default=2)
    
    。。。创建父实例会自动创建相关的
    Child
    实例:

    >>> p = Parent.objects.create()
    >>> p.child
    <Child: parent=assd>
    >>> p.child.field_b
    2
    
    p=Parent.objects.create() >>>p.儿童 >>>p.child.field_b 2.
    为创建
    用户
    实例时创建实例的常见情况提供了
    PerUserData
    帮助程序。

    调用实例.save的可能重复将触发另一个post\u save信号,该信号将再次调用函数?我认为这也要求字段可为空。对于已创建的对象,
    created
    关键字被设置为True,并且由于回调在db save之后运行,因此该对象已经创建。您可以将代码移动到
    pre_save
    ,但必须使用
    try:except:
    子句来检查组和处理异常。由于您创建的组与课程是一体的,因此我认为在post_save中根本不需要进行检查。我最终在pre_save中进行了验证,并启用了事务中间件将每个请求包装在单个事务中,因为我不想使字段为空。但您的回答在其他方面是有帮助的。我认为,当您想要确保保存过程的原子性时,这种方法效果很差。您可以将
    保存
    包装到
    原子
    装饰器中,但它有两个问题。首先,
    原子的
    装饰器存在的原因并不明显。第二,当您有另一个信号处理程序时,例如发送电子邮件,如果该处理程序引发异常,您的整个事务将回滚,这可能不是您想要的。我有一个类似的问题,我正在尝试用表单解决它。你能链接到你的表格答案吗?我知道这是4年前的事了,但也许你有一些答案。
    AttributeError:“module”对象没有属性“commit\u on\u success”