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

时间:2013-09-22 08:12:51

标签: python django django-models

我是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')

似乎我可以挂钩到模型的干净方法来执行此操作,但我希望能够将整个事情包装在单个事务中,以便如果以后无法创建课程,它不会创建相关的Group对象。有没有办法实现这个目标?

另外,我在这里做错了吗? Django提供了更好的方法吗?

4 个答案:

答案 0 :(得分:6)

最终我决定:

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,这很好)。但最终,我完全将自动相关对象创建移出了模型。我在自定义表单的保存方法中完成了所有这一切,它更干净,并放弃在管理界面中使用此模型(这就是为什么我坚持在模型方法中首先完成所有这些)

答案 1 :(得分:5)

您可以使用models.signals.post_save信号来处理此类情况:

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')

答案 2 :(得分:1)

我在Django 1.7的类似问题中使用了wjin的解决方案。我只需进行2次更改:

  1. 必须使用commit_on_success
  2. 更改atomic
  3. self.id无效,因为代码在创建新对象时设置了id之前运行。我不得不使用别的东西作为组名。
  4. 这是我最终做的事情:

    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)
    

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

答案 3 :(得分:0)

https://chris-lamb.co.uk/projects/django-auto-one-to-one检查我的项目,这可以在创建父类时自动创建子模型实例。

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

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)

...创建Parent实例会自动创建相关内容 Child实例:

>>> p = Parent.objects.create()
>>> p.child
<Child: parent=assd>
>>> p.child.field_b
2

为创建PerUserData实例时创建实例的常见情况提供了User帮助程序。