15

I have the following in my app:

class University(models.Model):
    ...
    sister_university = models.OneToOneField('self', related_name = 
                        'university_sister_university', 
                        blank=True, null=True, 
                        on_delete=models.SET_NULL)

I only want a university to be related to one other university in both directions of that relationship.

For example, in the database, if I select university A as the sister_university of university B, I only want to be allowed to select university B as the sister_university under university A also. However, as it is, that second relationship is not enforced.

For example: Right now, under the Django Admin site, if I first select university A as the sister university of university B, I am still able to select any other university as the sister university of the university A object. I’m not constrained to only selecting university B.

Is it possible to enforce that uniqueness at the database level? Is there a better way of accomplishing what I’m trying to do?

Joe
  • 173
  • 10
  • I haven't tried this myself, so I don't have a detailed answer, but you might check out [this solution](https://stackoverflow.com/a/38261573/2715819). – RishiG May 15 '18 at 17:50
  • I can confirm that you can do this; but the constraint is not enforced; not even if you set `unique_together`. I guess this is first and foremost a SQL question; before being a django question. – rtindru May 15 '18 at 18:18
  • https://softwareengineering.stackexchange.com/questions/325284/specifying-a-bi-directional-unique-constraint-on-a-join-table-in-postgres – rtindru May 15 '18 at 18:20

2 Answers2

9

I think that what you need is to make this relationship symmetric.

You can accomplish this by overriding the save() method of the University model:

def save(self, *args, **kwargs):
    super(University, self).save()
    if self.sister_university:
        self.sister_university.sister_university = self
Peter Sobhi
  • 2,542
  • 2
  • 22
  • 28
  • @BearBrown, Does that mean that sometimes you want `UniversityA` to have `UniversityB` as a `sister_university` while `UniversityB` having a `null` as a `sister_university` ? – Peter Sobhi May 22 '18 at 13:46
  • 1
    @BearBrown, Oh okay.. As far as I understand he needs the symmetry to always happens. What do you think? – Peter Sobhi May 22 '18 at 14:00
  • Unfortunately it didn't work for me in admin , and in view I had to save both instances, any suggestions please? – Omar Hafez Apr 19 '20 at 02:52
2

I never did this kind of things, but I think that you can make this process through this way :

Method : unique_together()

You can use Options.unique_together and set your university_A and university_B as a unique pair.

unique_together = ("university_A", "university_B")

In your models.py file, you should have something like this (with maybe some issues, but the idea is there) :

class University(models.Model):
    ...
    university = models.ForeignKey('self', on_delete=models.CASCADE)
    sister_university = models.ForeignKey('self', on_delete=models.CASCADE)
    class Meta:
        unique_together     = (('university','sister_university'),)

You should find precious details there : https://docs.djangoproject.com/en/2.0/ref/models/options/

I never tried this command, but it seems to solve your issue according to your context.

Community
  • 1
  • 1
Essex
  • 6,042
  • 11
  • 67
  • 139
  • 1
    three university, `a, b, c` if `a` is sister `b` then `c` can't be sister `a`, i don't think the `unique_together` help. – Brown Bear May 22 '18 at 09:13
  • @BearBrown Yes, but if I well-understood the issue, there is just university A and B no ? He just wants to pairing universities ? – Essex May 22 '18 at 09:15
  • no two side unique. other way i didn't create the bounty. – Brown Bear May 22 '18 at 09:16
  • Hum, I think that `unique_together` or `through model` with signals are the only solutions to solve his issue. – Essex May 22 '18 at 09:20