pythondjangoone-to-one

How does django one-to-one relationships map the name to the child object?


Apart from one example in the docs, I can't find any documentation on how exactly django chooses the name with which one can access the child object from the parent object. In their example, they do the following:

    class Place(models.Model):
        name = models.CharField(max_length=50)
        address = models.CharField(max_length=80)

        def __unicode__(self):
            return u"%s the place" % self.name

    class Restaurant(models.Model):
        place = models.OneToOneField(Place, primary_key=True)
        serves_hot_dogs = models.BooleanField()
        serves_pizza = models.BooleanField()

        def __unicode__(self):
            return u"%s the restaurant" % self.place.name

    # Create a couple of Places.
    >>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
    >>> p1.save()
    >>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
    >>> p2.save()

    # Create a Restaurant. Pass the ID of the "parent" object as this object's ID.
    >>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
    >>> r.save()

    # A Restaurant can access its place.
    >>> r.place
    <Place: Demon Dogs the place>
    # A Place can access its restaurant, if available.
    >>> p1.restaurant

So in their example, they simply call p1.restaurant without explicitly defining that name. Django assumes the name starts with lowercase. What happens if the object name has more than one word, like FancyRestaurant?

Side note: I'm trying to extend the User object in this way. Might that be the problem?


Solution

  • If you define a custom related_name then it will use that, otherwise it will lowercase the entire model name (in your example .fancyrestaurant). See the else block in django.db.models.related code:

    def get_accessor_name(self):
        # This method encapsulates the logic that decides what name to give an
        # accessor descriptor that retrieves related many-to-one or
        # many-to-many objects. It uses the lower-cased object_name + "_set",
        # but this can be overridden with the "related_name" option.
        if self.field.rel.multiple:
            # If this is a symmetrical m2m relation on self, there is no reverse accessor.
            if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model:
                return None
            return self.field.rel.related_name or (self.opts.object_name.lower() + '_set')
        else:
            return self.field.rel.related_name or (self.opts.object_name.lower())
    

    And here's how the OneToOneField calls it:

    class OneToOneField(ForeignKey):
        ... snip ...
    
        def contribute_to_related_class(self, cls, related):
            setattr(cls, related.get_accessor_name(),
                    SingleRelatedObjectDescriptor(related))
    

    The opts.object_name (referenced in the django.db.models.related.get_accessor_name) defaults to cls.__name__.

    As for

    Side note: I'm trying to extend the User object in this way. Might that be the problem?

    No it won't, the User model is just a regular django model. Just watch out for related_name collisions.