Sbrown995
Sbrown995

Reputation: 148

How do you force only one relationship in django when multiple are possible?

I am creating a web application to manage robotics teams for our area. In the application I have a django model that looks like this:

class TeamFormNote(models.Model):   
    team = models.ForeignKey(Team, blank=True, null=True)
    member = models.ForeignKey(TeamMember, blank=True, null=True)
    notes = models.TextField()
    def __unicode__(self):
        if self.team:
            return "Team Form Record: " + unicode(self.team)
        if self.member:
            return "Member Form Record: " + unicode(self.member)

Essentially, I want it to have a relationship with team or a relationship with member, but not both. Is there a way to enforce this?

Upvotes: 3

Views: 430

Answers (2)

OBu
OBu

Reputation: 5187

This sounds like a malformed object model under the hood... How about an abstract class which defines all common elements and two dreived classes, one for team and one for member? If you are running into trouble with this because you want to have both referenced in the same table, you can use Generic Relations.

Upvotes: 0

miki725
miki725

Reputation: 27861

I can only see two viable solutions. First is actually the same as @mariodev suggested in the comment which is to use Genetic foreign key. That will look something like:

# make sure to change the app name
ALLOWED_RELATIONSHIPS = models.Q(app_label = 'app_name', model = 'team') | models.Q(app_label = 'app_name', model = 'teammember')
class TeamFormNote(models.Model):
    content_type = models.ForeignKey(ContentType, limit_choices_to=ALLOWED_RELATIONSHIPS)
    relation_id = models.PositiveIntegerField()
    relation = generic.GenericForeignKey('content_type', 'relation_id')

What that does is it sets up a generic foreign key which will allow you to link to any other model within your project. Since it can link to any other model, to restrict it to only the models you need, I use the limit_choices_to parameter of the ForeignKey. This will solve your problem since there is only one generic foreign key hence there is no way multiple relationships will be created. The disadvantage is that you cannot easily apply joins to generic foreign keys so you will not be able to do things like:

Team.objects.filter(teamformnote_set__notes__contains='foo')

The second approach is to leave the model as it and manually go into the database backend and add a db constaint. For example in postgres:

ALTER TABLE foo ADD CONSTRAINT bar CHECK ...;

This will work however it will not be transparent to your code.

Upvotes: 2

Related Questions