Book Image

Mastering Django: Core

By : Nigel George
Book Image

Mastering Django: Core

By: Nigel George

Overview of this book

Mastering Django: Core is a completely revised and updated version of the original Django Book, written by Adrian Holovaty and Jacob Kaplan-Moss - the creators of Django. The main goal of this book is to make you a Django expert. By reading this book, you’ll learn the skills needed to develop powerful websites quickly, with code that is clean and easy to maintain. This book is also a programmer’s manual that provides complete coverage of the current Long Term Support (LTS) version of Django. For developers creating applications for commercial and business critical deployments, Mastering Django: Core provides a complete, up-to-date resource for Django 1.8LTS with a stable code-base, security fixes and support out to 2018.
Table of Contents (33 chapters)
Mastering Django: Core
Credits
About the Author
www.PacktPub.com
Preface
Free Chapter
1
Introduction to Django and Getting Started

Relationships


Django also defines a set of fields that represent relations.

ForeignKey

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 a manufacturer_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.

limit_choices_to

Sets a limit to the available choices for this field when this field is rendered using a ModelForm or the admin (by default, all objects in the queryset are available to choose). Either a dictionary, a Q object, or a callable returning a dictionary or Q object can be used. 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 have is_staff=True. This may be helpful in the Django admin. The callable form can be helpful, for instance, when used in conjunction with the Python datetime module to limit selections by date range. For example:

def limit_pub_date_choices(): 
    return {'pub_date__lte': datetime.date.utcnow()} 
limit_choices_to = limit_pub_date_choices 

If limit_choices_to is or returns a Q object, which is useful for complex queries, 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 ModelAdmin for the model.

related_name

The 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='+') 
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.objects.filter(tag__name="important") 
to_field

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

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 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.

If this is set to False, accessing a related object that doesn't exist will raise its DoesNotExist exception.

on_delete

When 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 the on_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:

  • CASCADE: Cascade deletes; the default
  • PROTECT: Prevent deletion of the referenced object by raising ProtectedError, a subclass of django.db.IntegrityError
  • SET_NULL: Set the ForeignKey null; this is only possible if null is True
  • SET_DEFAULT: Set the ForeignKey to its default value; a default for the ForeignKey must be set
swappable

Controls the migration framework's reaction if this ForeignKey is pointing at a swappable model. If it is True-the default-then if the ForeignKey is pointing at a model which matches the current value of settings.AUTH_USER_MODEL (or another swappable model setting) the relationship will be stored in the migration using a reference to the setting, not to the model directly.

You only want to override this to be False if you are sure your model should always point towards the swapped-in model-for example, if it is a profile model designed specifically for your custom user model. Setting it to False does not mean you can reference a swappable model even if it is swapped out-False just means that the migrations made with this ForeignKey will always reference the exact model you specify (so it will fail hard if the user tries to run with a User model you don't support, for example). If in doubt, leave it to its default of True.

ManyToManyField

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, including recursive 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.

related_name

Same as ForeignKey.related_name.

related_query_name

Same as ForeignKey.related_query_name.

limit_choices_to

Same 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.

symmetrical

Only 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, the ManyToManyField 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, set symmetrical to False. This will force Django to add the descriptor for the reverse relationship, allowing ManyToManyField relationships to be non-symmetrical.

through

Django will automatically generate a table to manage many-to-many relationships. However, if you want to manually specify the intermediary table, you can use the through 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.

through_fields

Only used when a custom intermediary model is specified. Django will normally determine which fields of the intermediary model to use in order to establish a many-to-many relationship automatically.

db_table

The 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.

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.

swappable

Controls the migration framework's reaction if this ManyToManyField is pointing at a swappable model. If it is True--the default-then if the ManyToManyField is pointing at a model which matches the current value of settings.AUTH_USER_MODEL (or another swappable model setting) the relationship will be stored in the migration using a reference to the setting, not to the model directly.

You only want to override this to be False if you are sure your model should always point towards the swapped-in model-for example, if it is a profile model designed specifically for your custom user model. If in doubt, leave it to its default of True. ManyToManyField does not support validators. null has no effect since there is no way to require a relationship at the database level.

OneToOneField

A one-to-one relationship. Conceptually, this is similar to a ForeignKey with unique=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 regarding recursive and lazy relationships. If you do not specify 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.conf import settings 
from django.db import models 
 
class MySpecialUser(models.Model): 
    user = models.OneToOneField(settings.AUTH_USER_MODEL) 
    supervisor = models.OneToOneField(settings.AUTH_USER_MODEL, 
      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:

parent_link

When 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 extra OneToOneField which would normally be implicitly created by subclassing. See One-to-one relationships in the next chapter for usage examples of OneToOneField.