micgeronimo
micgeronimo

Reputation: 2149

How to re-assign ForeignKey in Django

I have two following models:

 class Student(models.Model):
     name = models.CharField(max_length=255, help_text='Name of student')

 class Group(models.Model):
         name = models.CharField(max_length=255, help_text="Groups' name")
         elder = models.ForeignKey(Student, default=None, blank=True, null=True)

The case is that I have form(created from modelForm) which is able to update entries in database. My problem is following: User can create two groups with ForeignKey that will point to one entry in Student table.

I've found db_constraint in documentation but it seems like not enough. It will be great to get following behavior: When user assigns ForeignKey which is already assigned to another model, I want to delete old assignment and create new. So Student Foreign key could be assigned only to one model at a time.

Upvotes: 0

Views: 65

Answers (2)

knbk
knbk

Reputation: 53719

The OneToOneField is specifically designed for unique relations:

class Group(models.Model):
    name = models.CharField(max_length=255, help_text="Groups' name")

class Student(models.Model):
    name = models.CharField(max_length=255, help_text='Name of student')
    group = models.OneToOneField(Group, default=None, blank=True, null=True, related_name='elder')

Note how I moved the relationship field to the Student model, this will give you the behaviour that you want. If it was on the Group model, and you assign a student to a new group while he already has one, it would raise a DatabaseError for violating the unique constraint. In this case, the (unique) FOREIGN KEY column is in the Student's table, so if you assign a student to another group and save that group, the student table will be updated and the old group replaced.

Upvotes: 1

user4367525
user4367525

Reputation: 36

override the save method of Group:

def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
     Group.objects.filter(elder.self.elder).update(elder=None)
     super(Group, self).save(force_insert, force_update, using, update_fields)

but you must use transactions to avoid inconsistency

Upvotes: 2

Related Questions