Vernon Vernon - 3 days ago 6
SQL Question

Correct Usage of Django Related Fields

One aspect of Django's models that I simply don't get my head around are the related fields, and getting their usage correct. I think this reflects a poor understanding of SQL and databases in general - so I suspect it's an issue for many django users.

In my current code I'm building a tour app, and so I have Accommodation as a model. Then I have Route. The day's route ends up being a bunch of relationship fields, but the one that's getting me confused is the fact that I need a start accommodation and an end accommodation - two separate relationships.

To me, logically, I should be able to have a OneToOneField - start_accom to accommodation, and then end_accom to accommodation. But doing this throws an error.

The code:

class Route(models.Model):
start_accom = models.OneToOneField(
Accommodation,
on_delete=models.CASCADE,
primary_key=True,
)
end_accom = models.OneToOneField(
Accommodation,
on_delete=models.CASCADE,
)
mode_of_travel = models.CharField(max_length=50)
description = models.TextField()


And the error message when I run the django runserver:

Performing system checks...

Unhandled exception in thread started by <function check_errors.<locals>.wrapper at 0x103ecdd90>
Traceback (most recent call last):
File "/Users/vernonswanepoel/.virtualenvs/tour/lib/python3.6/site-packages/django/utils/autoreload.py", line 226, in wrapper
fn(*args, **kwargs)
File "/Users/vernonswanepoel/.virtualenvs/tour/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 121, in inner_run
self.check(display_num_errors=True)
File "/Users/vernonswanepoel/.virtualenvs/tour/lib/python3.6/site-packages/django/core/management/base.py", line 431, in check
raise SystemCheckError(msg)
django.core.management.base.SystemCheckError: SystemCheckError: System check identified some issues:

ERRORS:
company.Route.end_accom: (fields.E304) Reverse accessor for 'Route.end_accom' clashes with reverse accessor for 'Route.start_accom'.
HINT: Add or change a related_name argument to the definition for 'Route.end_accom' or 'Route.start_accom'.
company.Route.end_accom: (fields.E305) Reverse query name for 'Route.end_accom' clashes with reverse query name for 'Route.start_accom'.
HINT: Add or change a related_name argument to the definition for 'Route.end_accom' or 'Route.start_accom'.
company.Route.start_accom: (fields.E304) Reverse accessor for 'Route.start_accom' clashes with reverse accessor for 'Route.end_accom'.
HINT: Add or change a related_name argument to the definition for 'Route.start_accom' or 'Route.end_accom'.
company.Route.start_accom: (fields.E305) Reverse query name for 'Route.start_accom' clashes with reverse query name for 'Route.end_accom'.
HINT: Add or change a related_name argument to the definition for 'Route.start_accom' or 'Route.end_accom'.

System check identified 4 issues (0 silenced).

Answer

The issue, as it mentions is because of the reverse relationship. You need to add the related_name attribute

class Route(models.Model):                                                                                                  
    start_accom = models.OneToOneField(                                                                                     
        Accommodation,                                                                                                      
        on_delete=models.CASCADE,                                                                                           
        primary_key=True, 
        related_name="accommodation_start"                                                                                                  
    )                                                                                                                       
    end_accom = models.OneToOneField(                                                                                                                                                                             
        Accommodation,                                                                                                      
        on_delete=models.CASCADE,                                                                                           
        related_name="accommodation_end"                                                                                                  
    )                                                                                                                       
    mode_of_travel = models.CharField(max_length=50)                                                                        
    description = models.TextField()  
Comments