我是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提供了更好的方法吗?
答案 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次更改:
commit_on_success
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)
我知道如果名字太长或某人设法使用相同的名称,这可能会导致问题,但我会在稍后处理这些问题。
答案 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
帮助程序。