ArtOfWarfare
ArtOfWarfare

Reputation: 21496

Django Relationship Name Collisions - Abstract Model has multiple relationships with another Model

My application consists of three Models: Users, Topics, and Responses.

Each Response may either be addressed at a Topic, or at another Response, but are largely identical. In the interest of not duplicating code, I thought it best to make an abstract model, Response, which ResponseResponse and TopicResponse inherit from.

from django.contrib.auth.models import User
from django.db.models           import (CASCADE, CharField, DateTimeField,
                                        ForeignKey, ManyToManyField, Model,
                                        TextField)

class Topic(Model):
    name = CharField(max_length = 100)

class Response(Model):
    body    = TextField()
    topics  = ManyToManyField(Topic)
    agreers = ManyToManyField(User)

    proposal = TextField()
    affirms  = ManyToManyField(User, related_name = 'affirmers')
    rejects  = ManyToManyField(User, related_name = 'rejectors')

    class Meta:
        abstract = True

class TopicResponse(Response):
    responseTo = ForeignKey(Topic, on_delete = CASCADE)

class ResponseResponse(Response):
    responseTo = ForeignKey(Response, on_delete = CASCADE)

The issue with this is that the User has two conflicting relationships called affirmers and two called rejectors.

If I don't give them related names, then instead TopicResponse and ResponseResponse each have three conflicting relationships, all of them called TopicResponse or ResponseResponse, respectively, (one for agreers, one for affirms, one for rejects).

An example error message is:

app.TopicResponse.rejects: (fields.E305) Reverse query name for 'TopicResponse.rejects' clashes with reverse query name for 'ResponseResponse.rejects'.
        HINT: Add or change a related_name argument to the definition for 'TopicResponse.rejects' or 'ResponseResponse.rejects'.

If I leave off the related_name argument, I get error messages like this:

app.ResponseResponse.rejects: (fields.E304) Reverse accessor for 'ResponseResponse.rejects' clashes with reverse accessor for 'ResponseResponse.affirms'.
        HINT: Add or change a related_name argument to the definition for 'ResponseResponse.rejects' or 'ResponseResponse.affirms'.

What can I do to fix all of these conflicts? I need to somehow have the related_name dynamically generated with the name of the of the concrete instance of the Model (like it is if you don't specify it) plus the name of the relationship.

Upvotes: 1

Views: 114

Answers (1)

ilse2005
ilse2005

Reputation: 11439

You have to make the related name unique. As stated in the docs you can add %(class)s or %(app_label)s. These are then replaced by the child class values:

class Topic(Model):
    name = CharField(max_length = 100)

    class Response(Model):
        body    = TextField()
        topics  = ManyToManyField(Topic)
        agreers = ManyToManyField(User)

        proposal = TextField()
        affirms  = ManyToManyField(User, related_name = '%(app_label)s_%(class)saffirmers')
        rejects  = ManyToManyField(User, related_name = '%(app_label)s_%(class)srejectors')

        class Meta:
            abstract = True

    class TopicResponse(Response):
        responseTo = ForeignKey(Topic, on_delete = CASCADE)

    class ResponseResponse(Response):
        responseTo = ForeignKey(Response, on_delete = CASCADE)

Upvotes: 2

Related Questions