Автоматическое создание связанных объектов при создании модели в Django
Я новичок в 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')
Кажется, что я могу подключиться к чистому методу модели, чтобы сделать это, но Я хотел бы иметь возможность обернуть все это в одну транзакцию, так что если он не сможет создать курс позже, он не будет создавать связанные объекты группы. Есть ли способ достичь этого?
кроме того, я делаю что-то неправильно здесь? Предоставляет ли Django лучший способ сделать это?
4 ответов
В конце концов я остановился на:
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)
Edit (2014/12/01): @Shasanoglu правильно, приведенный выше код на самом деле не работает из-за id еще не существует. Вы должны сделать Связанный объект creation после вы называете save (так что вы называете super.сохраните, создайте Связанный объект, обновите этот объект и вызовите super.сохранить снова - не идеально. Это или вы опускаете идентификатор из имени группы, и все в порядке). В конечном счете, я переместил создание автоматического связанного объекта из модели полностью. Я сделал все это в методе сохранения пользовательской формы, которая была намного чище, и отказался от использования этой модели в интерфейсе администратора (именно поэтому я настоял на том, чтобы сделать все это в методе модели в первую очередь)
можно использовать модели.сигналы.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')
я использовал решение wjin в аналогичной проблеме в Django 1.7. Мне просто нужно было внести 2 изменения:
- пришлось менять
commit_on_success
Сatomic
-
self.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)
Я знаю, что это может вызвать проблемы, если имя слишком длинное или кто-то умудряется использовать то же имя, но я позабочусь о них позже.
проверьте мой проект для этого в 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
A PerUserData
helper предоставляется для общего случая создания экземпляров, когда User
создается экземпляр.