Django models relationship

In Django models you could define a set of fields that represent database object relations.

There are three main type of relations: Many to One, Many to Many and One to One.

Содержание

ForeignKey

class ForeignKey(othermodel[, **options])A many-to-one relationship. Requires a positional argument: the class to which the model is related.

To create a recursive relationship – an object that has a many-to-one relationship with itself – use models.ForeignKey(‘self’).

If you need to create a relationship on a model that has not yet been defined, you can use the name of the model, rather than the model object itself:

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey('Manufacturer')
    # ...

class Manufacturer(models.Model):
    # ...
    pass

To refer to models defined in another application, you can explicitly specify a model with the full application label. For example, if the Manufacturer model above is defined in another application called production, you’d need to use:

class Car(models.Model):
    manufacturer = models.ForeignKey('production.Manufacturer')

This sort of reference can be useful when resolving circular import dependencies between two applications.

A database index is automatically created on the ForeignKey. You can disable this by setting db_index to False. You may want to avoid the overhead of an index if you are creating a foreign key for consistency rather than joins, or if you will be creating an alternative index like a partial or multiple column index.

Database Representation

Behind the scenes, Django appends “_id” to the field name to create its database column name. In the above example, the database table for the Car model will have amanufacturer_id column. (You can change this explicitly by specifying db_column) However, your code should never have to deal with the database column name, unless you write custom SQL. You’ll always deal with the field names of your model object.

Arguments

ForeignKey accepts an extra set of arguments – all optional – that define the details of how the relation works.

ForeignKey.limit_choices_toA dictionary of lookup arguments and values (see Making queries) that limit the available admin or ModelForm choices for this object. For example:

staff_member = models.ForeignKey(User, limit_choices_to={'is_staff': True})

causes the corresponding field on the ModelForm to list only Users that haveis_staff=True.

Instead of a dictionary this can also be a Q object for more complex queries. However, if limit_choices_to is a Q object then it will only have an effect on the choices available in the admin when the field is not listed in raw_id_fields in the ModelAdminfor the model.

ForeignKey.related_nameThe name to use for the relation from the related object back to this one. It’s also the default value for related_query_name (the name to use for the reverse filter name from the target model). See the related objects documentation for a full explanation and example. Note that you must set this value when defining relations on abstract models; and when you do so some special syntax is available.

If you’d prefer Django not to create a backwards relation, set related_name to ‘+’ or end it with ‘+’. For example, this will ensure that the User model won’t have a backwards relation to this model:

user = models.ForeignKey(User, related_name='+')

ForeignKey.related_query_name
The name to use for the reverse filter name from the target model. Defaults to the value of related_name if it is set, otherwise it defaults to the name of the model:

# Declare the ForeignKey with related_query_name
class Tag(models.Model):
    article = models.ForeignKey(Article, related_name="tags", related_query_name="tag")
    name = models.CharField(max_length=255)

# That's now the name of the reverse filter
article_instance.filter(tag__name="important")

ForeignKey.to_fieldThe field on the related object that the relation is to. By default, Django uses the primary key of the related object.

ForeignKey.db_constraint
Controls whether or not a constraint should be created in the database for this foreign key. The default is True, and that’s almost certainly what you want; setting this toFalse can be very bad for data integrity. That said, here are some scenarios where you might want to do this:

  • You have legacy data that is not valid.
  • You’re sharding your database.

If this is set to False, accessing a related object that doesn’t exist will raise itsDoesNotExist exception.

ForeignKey.on_deleteWhen an object referenced by a ForeignKey is deleted, Django by default emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey. This behavior can be overridden by specifying theon_delete argument. For example, if you have a nullable ForeignKey and you want it to be set null when the referenced object is deleted:

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)

The possible values for on_delete are found in django.db.models:

  • CASCADECascade deletes; the default.
  • PROTECTPrevent deletion of the referenced object by raising ProtectedError, a subclass ofdjango.db.IntegrityError.
  • SET_NULLSet the ForeignKey null; this is only possible if null is True.
  • SET_DEFAULTSet the ForeignKey to its default value; a default for the ForeignKey must be set.
  • SET()Set the ForeignKey to the value passed to SET(), or if a callable is passed in, the result of calling it. In most cases, passing a callable will be necessary to avoid executing queries at the time your models.py is imported:
    from django.db import models
    from django.contrib.auth.models import User
    
    def get_sentinel_user():
        return User.objects.get_or_create(username='deleted')[0]
    
    class MyModel(models.Model):
        user = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user))
    
  • DO_NOTHINGTake no action. If your database backend enforces referential integrity, this will cause an IntegrityError unless you manually add an SQL ON DELETE constraint to the database field (perhaps using initial sql).

ManyToManyField

class ManyToManyField(othermodel[, **options])A many-to-many relationship. Requires a positional argument: the class to which the model is related, which works exactly the same as it does for ForeignKey, includingrecursive and lazy relationships.

Related objects can be added, removed, or created with the field’s RelatedManager.

Database Representation

Behind the scenes, Django creates an intermediary join table to represent the many-to-many relationship. By default, this table name is generated using the name of the many-to-many field and the name of the table for the model that contains it. Since some databases don’t support table names above a certain length, these table names will be automatically truncated to 64 characters and a uniqueness hash will be used. This means you might see table names like author_books_9cdf4; this is perfectly normal. You can manually provide the name of the join table using the db_table option.

Arguments

ManyToManyField accepts an extra set of arguments – all optional – that control how the relationship functions.

ManyToManyField.related_nameSame as ForeignKey.related_name.

If you have more than one ManyToManyField pointing to the same model and want to suppress the backwards relations, set each related_name to a unique value ending with ‘+’:

users = models.ManyToManyField(User, related_name='u+')
referents = models.ManyToManyField(User, related_name='ref+')

ManyToManyField.related_query_name
Same as ForeignKey.related_query_name.

ManyToManyField.limit_choices_toSame as ForeignKey.limit_choices_to.

limit_choices_to has no effect when used on a ManyToManyField with a custom intermediate table specified using the through parameter.

ManyToManyField.symmetricalOnly used in the definition of ManyToManyFields on self. Consider the following model:

from django.db import models

class Person(models.Model):
    friends = models.ManyToManyField("self")

When Django processes this model, it identifies that it has a ManyToManyField on itself, and as a result, it doesn’t add a person_set attribute to the Person class. Instead, theManyToManyField is assumed to be symmetrical – that is, if I am your friend, then you are my friend.

If you do not want symmetry in many-to-many relationships with self, setsymmetrical to False. This will force Django to add the descriptor for the reverse relationship, allowing ManyToManyField relationships to be non-symmetrical.

ManyToManyField.throughDjango will automatically generate a table to manage many-to-many relationships. However, if you want to manually specify the intermediary table, you can use thethrough option to specify the Django model that represents the intermediate table that you want to use.

The most common use for this option is when you want to associate extra data with a many-to-many relationship.

If you don’t specify an explicit through model, there is still an implicit through model class you can use to directly access the table created to hold the association. It has three fields:

  • id: the primary key of the relation.
  • <containing_model>_id: the id of the model that declares the ManyToManyField.
  • <other_model>_id: the id of the model that the ManyToManyField points to.

This class can be used to query associated records for a given model instance like a normal model.

ManyToManyField.db_tableThe name of the table to create for storing the many-to-many data. If this is not provided, Django will assume a default name based upon the names of: the table for the model defining the relationship and the name of the field itself.

ManyToManyField.db_constraint
Controls whether or not constraints should be created in the database for the foreign keys in the intermediary table. The default is True, and that’s almost certainly what you want; setting this to False can be very bad for data integrity. That said, here are some scenarios where you might want to do this:

  • You have legacy data that is not valid.
  • You’re sharding your database.

It is an error to pass both db_constraint and through.

OneToOneField

class OneToOneField(othermodel[, parent_link=False, **options])A one-to-one relationship. Conceptually, this is similar to a ForeignKey withunique=True, but the “reverse” side of the relation will directly return a single object.

This is most useful as the primary key of a model which “extends” another model in some way; Multi-table inheritance is implemented by adding an implicit one-to-one relation from the child model to the parent model, for example.

One positional argument is required: the class to which the model will be related. This works exactly the same as it does for ForeignKey, including all the options regardingrecursive and lazy relationships.

If you do not specify the the related_name argument for the OneToOneField, Django will use the lower-case name of the current model as default value.

With the following example:

from django.db import models
from django.contrib.auth.models import User

class MySpecialUser(models.Model):
    user = models.OneToOneField(User)
    supervisor = models.OneToOneField(User, related_name='supervisor_of')

your resulting User model will have the following attributes:

>>> user = User.objects.get(pk=1)
>>> hasattr(user, 'myspecialuser')
True
>>> hasattr(user, 'supervisor_of')
True

A DoesNotExist exception is raised when accessing the reverse relationship if an entry in the related table doesn’t exist. For example, if a user doesn’t have a supervisor designated by MySpecialUser:

>>> user.supervisor_of
Traceback (most recent call last):
    ...
DoesNotExist: User matching query does not exist.

Additionally, OneToOneField accepts all of the extra arguments accepted by ForeignKey, plus one extra argument:

OneToOneField.parent_linkWhen True and used in a model which inherits from another (concrete) model, indicates that this field should be used as the link back to the parent class, rather than the extraOneToOneField which would normally be implicitly created by subclassing.

One-to-one relationships

To define a one-to-one relationship, use OneToOneField.

In this example, a Place optionally can be a Restaurant:

from django.db import models

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

    # On Python 3: def __str__(self):
    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()

    # On Python 3: def __str__(self):
    def __unicode__(self):
        return u"%s the restaurant" % self.place.name

class Waiter(models.Model):
    restaurant = models.ForeignKey(Restaurant)
    name = models.CharField(max_length=50)

    # On Python 3: def __str__(self):
    def __unicode__(self):
        return u"%s the waiter at %s" % (self.name, self.restaurant)

What follows are examples of operations that can be performed using the Python API facilities.

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
<Restaurant: Demon Dogs the restaurant>

p2 doesn’t have an associated restaurant:

>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
>>>     p2.restaurant
>>> except ObjectDoesNotExist:
>>>     print("There is no restaurant here.")
There is no restaurant here.

You can also use hasattr to avoid the need for exception catching:

>>> hasattr(p2, 'restaurant')
False

Set the place using assignment notation. Because place is the primary key on Restaurant, the save will create a new restaurant:

>>> r.place = p2
>>> r.save()
>>> p2.restaurant
<Restaurant: Ace Hardware the restaurant>
>>> r.place
<Place: Ace Hardware the place>

Set the place back again, using assignment in the reverse direction:

>>> p1.restaurant = r
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>

Restaurant.objects.all() just returns the Restaurants, not the Places. Note that there are two restaurants – Ace Hardware the Restaurant was created in the call to r.place = p2:

>>> Restaurant.objects.all()
[<Restaurant: Demon Dogs the restaurant>, <Restaurant: Ace Hardware the restaurant>]

Place.objects.all() returns all Places, regardless of whether they have Restaurants:

>>> Place.objects.order_by('name')
[<Place: Ace Hardware the place>, <Place: Demon Dogs the place>]

You can query the models using lookups across relationships:

>>> Restaurant.objects.get(place=p1)
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.get(place__pk=1)
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.filter(place__name__startswith="Demon")
[<Restaurant: Demon Dogs the restaurant>]
>>> Restaurant.objects.exclude(place__address__contains="Ashland")
[<Restaurant: Demon Dogs the restaurant>]

This of course works in reverse:

>>> Place.objects.get(pk=1)
<Place: Demon Dogs the place>
>>> Place.objects.get(restaurant__place__exact=p1)
<Place: Demon Dogs the place>
>>> Place.objects.get(restaurant=r)
<Place: Demon Dogs the place>
>>> Place.objects.get(restaurant__place__name__startswith="Demon")
<Place: Demon Dogs the place>

Add a Waiter to the Restaurant:

>>> w = r.waiter_set.create(name='Joe')
>>> w.save()
>>> w
<Waiter: Joe the waiter at Demon Dogs the restaurant>

Query the waiters:

>>> Waiter.objects.filter(restaurant__place=p1)
[<Waiter: Joe the waiter at Demon Dogs the restaurant>]
>>> Waiter.objects.filter(restaurant__place__name__startswith="Demon")
[<Waiter: Joe the waiter at Demon Dogs the restaurant>]

Leave a Reply

Your email address will not be published. Required fields are marked *