Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto-creating related objects on model creation in Django

I'm new to Django, so excuse my ignorance :)

Say I have a model that has a couple of foreign key relations, and when I create an instance of the model I want it to automatically generate new instances for the foreign key objects as well. In this case I'm modelling course enrollment as a Group, and I am referencing the specific group as a foreign key on the model.

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

It seems like I can hook into the clean method of the model to do this, but I'd like to be able to wrap the whole thing up in a single transaction, so that if it fails to create the Course later on, it won't create the related Group objects. Is there any way to achieve this?

Also, am I doing the wrong thing entirely here? Does Django provide a better way to do this?

like image 222
wjin Avatar asked Sep 22 '13 08:09

wjin


People also ask

How do you get a related name on Django?

The related_name attribute specifies the name of the reverse relation from the User model back to your model. If you don't specify a related_name, Django automatically creates one using the name of your model with the suffix _set.

Does Django auto generate ID?

Django will create or use an autoincrement column named id by default, which is the same as your legacy column.

What is ForeignObject in Django?

class Relationship(models.ForeignObject): """ Create a django link between models on a field where a foreign key isn't used. This class allows that link to be realised through a proper relationship, allowing prefetches and select_related. """

What is the use of Unicode in Django?

Django supports Unicode data everywhere. This document tells you what you need to know if you're writing applications that use data or templates that are encoded in something other than ASCII.


2 Answers

You can use the models.signals.post_save signal to handle such case:

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')
like image 70
Maciej Gol Avatar answered Sep 19 '22 08:09

Maciej Gol


Ultimately I settled on:

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 is correct, the above code does not really work due to id not existing yet. You have to do related object creation after you call save (so you call super.save, create the related object, update this object and call super.save again -- not ideal. That or you omit the id from the Group name and it's fine). Ultimately though, I moved the automated-related-object creation out of the model entirely. I did it all in the save method of a custom form which was much cleaner, and gave up on using this model in the admin interface (which was why I insisted on doing all of this in the model method in the first place)

like image 38
wjin Avatar answered Sep 19 '22 08:09

wjin