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?
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.
Django will create or use an autoincrement column named id by default, which is the same as your legacy column.
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. """
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.
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')
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With