Django model default and custom behaviors

When you create a Django model class it always inherits its behavior from the django.db.models.Model class, as illustrated back in listing 7-1. This Django class provides a Django model with a great deal of functionality, that includes basic operations via methods like save() and delete(), as well as naming conventions and query behaviors for the model.

In most circumstances, the default behaviors provided by the django.db.models.Model class are sufficient, but in other cases you'll want to provide custom behavior. Next, I'll enumerate a Django model's functionalities provided via the django.db.models.Model class.

Model methods

All Django models inherit a series of methods for operations that include: saving, deleting, validating, loading and applying custom logic to model data. In the next sections I'll describe the default behavior for each of these methods and how to customize their behavior.

save() method

The save() method offers one of the most common operations for Django models: to save (i.e. create or update) a record to a database. Once you create or have a reference to a Django model instance, you call the save() method to create/update the instance on a database. Listing 7-11 illustrates this process.

Listing 7-11 Django model use of the save() method

# Import Django model class
from coffeehouse.stores.models import Store

# Create a model Store instance
store_corporate = Store(name='Corporate',address='624 Broadway',
                 city='San Diego',state='CA',email='corporate@coffeehouse.com')

# Invoke the save() method to create/save the record
# No record id reference, so a create operation is made and the reference is updated with id
store_corporate.save() 

# Change field on instance
store_corporate.city='625 Broadway'

# Invoke the save() method to update/save the record
# Record has id reference from prior save() call, so operation is update 
store_corporate.save()

In listing 7-11 two calls are made to the save() method on the same reference, the first one creates a record on the database and the second one updates the record on the database.

Can you tell how Django knows when to create and when to update a record with the same save() method ? It's not in plain sight so don't worry if you can't spot it.

In the first section of this chapter when you created a Django model, I mentioned Django automatically adds an id field as a primary key to all Django models, in order to make searching for records easier and more efficient. The presence of this id primary key is what Django uses to determine if the save() method performs a create or update operation.

Although there's no trace of an explicit id primary key value in listing 7-11, a model instance gets an id value after Django creates an instance. The first time the save() method is invoked in listing 7-11, Django attempts to create a new record because it can't find an id primary key value on the instance. However, if the creation operation is successful, the database assigns the record an id primary key value that's returned to Django which updates the reference with this id primary key value.

On subsequent calls made to the save() method on the same model reference, Django detects the presence of the id primary key value and performs an update operation based on this id primary key value. In case you're wondering, if you add an explicit id primary key value to a record reference, Django also performs an update because that's the flag it looks for to determine whether to create or update a record, so be aware placing an explicit id primary key updates/overwrites the database record associated with the given id primary key.

Now that you have a firm understanding of the default behaviors of a Django model's save() method, I'll describe the various options available for the save() method. The save() method accepts a series of arguments to override its default behavior, table 7-3 illustrates these arguments, their behavior and their default value.

Table 7-3 Django model save() method arguments

Argument Default Description
force_insert force_insert=False Explicitly tells Django to force a create operation on a record (e.g. .save(force_insert=True). This is rarely used but can be helpful for cases when you don't or can't rely on Django detecting a create operation (i.e. via the id primary key)
force_update force_update=False Explicitly tells Django to force an update operation on a record (e.g. .save(force_update=True). This is rarely used but can be helpful for cases when you don't or can't rely on Django detecting an update operation (i.e. via the id primary key)
using using=DEFAULT_DB_ALIAS, where DEFAULT_DB_ALIAS is a constant with a value of default Allows save() to perform the operation against a database that's not the default value in settings.py (e.g. .save(using='oracle') performs the operation against the oracle database, where oracle is a key in the DATABASES variable in settings.py) See the later section in the chapter on multiple databases.
update_fields update_fields=None Accepts a list of fields to update (e.g..save(update_fields=['name']) only updates a record's name value). Helpful when you have large models and want to do a more efficient/granular update, because by default Django updates all model fields.
commit commit=True Ensures a record is saved to the database. In certain circumstances (e.g. model forms or relationship operations) commit is set to False to create a model instance without saving it. This allows additional operations (on forms, relationships) to be made and based on their outcome, determine to make a standard save() call to write the record to the database.

The option you're likely to use the most from table 7-3 is update_fields, since it produces a performance boost by selectively choosing which fields to update. However, table 7-3 gives you the full series of options in case you hit an edge-case with the save() method.

Finally, to close our discussion on the save() method, it's possible to define an implementation of the save() method on a Django model to execute custom logic when the method is called. Listing 7-12 illustrates this process.

Listing 7-12. Django model with custom save() method

class Store(models.Model):
    name = models.CharField(max_length=30)    
    address = models.CharField(max_length=30)
    city = models.CharField(max_length=30)
    state = models.CharField(max_length=2)
        
    def save(self, *args, **kwargs):
        # Do custom logic here (e.g. validation, logging, call third party service)
        # Run default save() method
        super(Store,self).save(*args, **kwargs)

Notice the save() method in listing 7-12 is declared inline with a Django model's fields. In this case, when a call is made to save() on a reference for this type of model (e.g.downtown.save()) Django attempts to run the model's custom save() method. This is helpful in circumstances where you want to perform other actions (e.g. log a message, call a third party service) when a model instance is created or updated. The last snippet in the custom save() method super(Store,self).save(*args, **kwargs) tells Django to run the base save() method from django.db.models.Model.

Tip See the Django signals section later in this chapter to execute logic before or after a model's save() method is run.

delete() method

The delete() method is used to eliminate a record from the database through a reference. For example, if in listing 7-11 you call store_corporate.delete() -- where store_corporate is the record reference -- Django removes the record from the database. Under the hood, the delete() method relies on the id primary key to remove the record, so it's a requirement for a reference to have the id value to invoke the delete() method.

When the delete() method is called on a reference, its id primary key value is removed but the record's remaining values remain in memory. In addition, the delete() method responds with the amount of deleted records (e.g. (4, {u'stores.Store_amenities': 3, u'stores.Store': 1}), indicates 4 overall records were deleted, with 1 Store record and 3 stores.Store_amenities -- the stores.Store represents a relationship on the main Store model.

Similar to the save() method, the delete() method also supports two arguments: using=DEFAULT_DB_ALIAS and keep_parents=False. The using argument allows you to specify an alternate database to perform the delete() operation on -- see table 7-3 for more details on this type of argument and the models with multiple database section later in this chapter. And the keep_parents arguments is useful when the delete() operation takes place on a model with a relationship and you wish to keep the parent model's data intact -- or removed which is the default. More details on the use of keep_parents are given in the Django model relationships section later in this chapter.

And finally, it's also possible to define a custom delete() method on a Django model class -- just like save() in listing 7-12 -- to execute custom logic (e.g. create an audit trail) when a record is removed.

Tip See the Django signals section later in this chapter to execute logic before or after a model's delete() method is run.

Validation methods: clean_fields(), clean(), validate_unique() and full_clean()

When you create or update a Django model instance with the save() method, Django enforces the instance values comply with those of the model definition. For example, if you use the model field name = models.CharField(max_length=30) Django enforces the name value is a text field with at most 30 characters.

The most important part to understand about Django model instance validation is that it's done on two layers: at the database layer and the Django/Python layer, both of which are enforced via the model data types you learned about in the previous section.

Let's analyze the database layer validation layer first. Once you have a Django model and create its initial migration -- as described in the first section of this chapter on 'Migrations and the Django model workflow' -- Django generates the database DDL (Data definition language) to create a database table in accordance with the model definition (e.g. the Django model field CharField(max_length=30) generates a varchar(30) NOT NULL database column type). Therefore due to this initial database DDL, all Django model values that don't comply with validation rules are guaranteed to be rejected at the database layer where it's the database that performs the validation.

Now let's analyze the Django/Python validation layer. Although the Django model data types for fields (e.g. CharField(max_length=30)) can give the impression they act on model instances automatically, the Django/Python validation layer requires you execute validation methods on the model instance to enforce validation. If you don't use these validation methods -- which are the topic of this section -- the database validation layer is the only enforcer of model field data types.

Although relying on validation at the database layer is perfectly acceptable, using model validation at the Django/Python layer has the advantage of supporting more complex validation rules, as well as reducing database load for operations that will end up being rejected by a database. However, unlike database layer validation which is automatically done after a Django model's first migration, Django/Python layer validation requires you use some of the following model methods.

Let's first explore the Django model validation clean_fields() method. Listing 7-13 illustrates a model definition, followed by a call sequence that uses the clean_fields() method.

Listing 7-13. Django model use of validation clean_fields() method

class Store(models.Model):
    name = models.CharField(max_length=30)    
    address = models.CharField(max_length=30,unique=True)
    city = models.CharField(max_length=30)
    state = models.CharField(max_length=2)

# Create a model Store instance, that violates the max_length rule
store_corporate = Store(name="""This is a very long name for the Corporate store that 
                       exceeds the 30 character limit""",address='624 Broadway',
		       city='San Diego',state='AZ',email='corporate@coffeehouse.com')

# No error yet
# You could call save() and let the database reject the instance...
# But you can also validate at the Django/Python level with the clean_fields() method 
store_corporate.clean_fields()
Traceback (most recent call last):
    raise ValidationError(errors)
ValidationError: {'name': [u'Ensure this value has at most 30 characters (it has 84).']}

First off, notice the model's name field in listing 7-13 uses the max_length=30 option to enforce values of this kind are capped to 30 characters. After the model definition, you can see the store_corporate instance breaks this rule with a value greater than 30 characters, which means Django doesn't detect broken model rules at instance creation.

While you can attempt to call save() on this last instance and let the database reject the operation via its DDL, you can call the clean_fields() method on the instance to tell Django to check the values of the instance against the model date types and raise an error.

Also notice the output of the clean_fields() method in listing 7-8 is a ValidationError data type with a dictionary. This last dictionary follows the key-value pattern '<model_field>'-'[<error_message_list>]', making it easy to identify multiple validation errors and reuse this data for other purposes (e.g. logging, presenting the error in a template).

While the clean_fields() method validates model values individually against their data types, the clean() method can be used to enforce more elaborate rules (e.g. relationships or specific values). Unlike the clean_fields() method you can invoke directly, you must define an implementation of the clean() method with validation logic, as illustrated in listing 7-14.

Listing 7-14. Django model use of validation clean() method

class Store(models.Model):
    name = models.CharField(max_length=30)    
    address = models.CharField(max_length=30,unique=True)
    city = models.CharField(max_length=30)
    state = models.CharField(max_length=2)
    def clean(self):
        # Don't allow 'San Diego' city entries that have state different than 'CA'
        if self.city == 'San Diego' and self.state != 'CA':
            raise ValidationError("""Wait San Diego is CA!, are you sure there is another
                              San Diego in %s ?""" % self.state)

# Create a model Store instance, that violates city/state rule
store_corporate = Store(name='Corporate',address='624 Broadway',
                             city='San Diego',state='AZ',email='corporate@coffeehouse.com')

# To enforce more complex rules call the clean() method implemented on a model
store_corporate.clean()
Traceback (most recent call last):
    raise ValidationError("""Wait San Diego is in CA!, are you sure there is another
                             San Diego in %s ?""" % (self.state))
ValidationError: [u"""Wait San Diego is in CA!, are you sure there is 
                    another San Diego in AZ ?"""]

Notice in listing 7-14 the Django model class defines the clean() method that enforces that if an instance city value is San Diego its state must be CA, if this condition is not met, a ValidationError error is raised. Next, when the clean() method is invoked on an instance that violates this rule, a ValidationError error is raised just like the clean_fields() method.

Another Django validation mechanism you can use is the clean_unique() method to enforce no two instances have the same value for a field that uses unique options. Listing 7-15 illustrates the use the clean_unique() method.

Listing 7-15. Django model use of validation clean_unique() method with unique* fields

class Store(models.Model):
    name = models.CharField(max_length=30)    
    address = models.CharField(max_length=30,unique=True)
    city = models.CharField(max_length=30)
    state = models.CharField(max_length=2)

# Create a model Store instance
store_corporate = Store(name='Downtown',address='624 Broadway',
                  city='San Diego',state='AZ',email='corporate@coffeehouse.com')

# Save instance 
store_corporate.save()

# Create another instance to violate uniqueness of address field 
store_uptown = Store(name='Uptown',address='624 Broadway', city='San Diego',state='CA')

# You could call save() and let the database reject the instance...
# But you can also validate at the Django/Python level with the validate_unique() method 
store_uptown.validate_unique()
Traceback (most recent call last):
    raise ValidationError(errors)
ValidationError: {'address': [u'Store with this Address already exists.']}

Look at how the address field of the Store model in listing 7-15 uses unique=True, which tells Django not to allow two Store instances with the same address value. Next, we create a Store instance with address='624 Broadway' and save it to the database. Right after, we create another Store instance with the same address='624 Broadway' value, but because the address model field has the unique option this new instance is in violation of the rule.

Therefore when you call the validate_unique() method on the store_uptown reference, Django raises a ValidationError exception indicating there's already a Store record with the same address in the database. Note that even if you didn't call the validate_unique() method, the database would end up rejecting the duplicate record since the unique=True produces the necessary DDL to enforce unique address values.

In addition to the clean_unique function performing validation on fields marked with unique options, the clean_unique method also enforces validation for the unique_together option declared in a model's Meta class. This variation is illustrated in listing 7-16.

Listing 7-16. Django model use of validation clean_unique() method with Meta unique_together

class Store(models.Model):
    name = models.CharField(max_length=30)    
    address = models.CharField(max_length=30,unique=True)
    city = models.CharField(max_length=30)
    state = models.CharField(max_length=2)
    email = models.EmailField()
    class Meta:
        unique_together = ("name", "email") 

# Create instance to show use of validate_unique() via Meta option
store_downtown_horton = Store(name='Downtown',address'Horton Plaza',
                        city='San Diego',state='CA',email='downtown@coffeehouse.com')

# Save intance to DB 
store_downtown_horton.save()

# Create additional instance that violated unique_together rule in Meta class
store_downtown_fv = Store(name='Downtown',address'Fashion Valley',
                    city='San Diego',state='CA',email='downtown@coffeehouse.com')

# You could call save() and let the database reject the instance but lets use validate_unique
store_downtown_fv.validate_unique()
Traceback (most recent call last):
ValidationError: {'__all__': [u'Store with this Name and Email already exists.']}

Notice how the class in listing 7-16 declares the Meta class followed by unique_together = ("name", "email"), which tells Django not to allow two Store instances with the same name and email value.

Next, two Store records are created with the same name and email value. Because this is in violation of the Django model meta option unique_together = ("name", "email"), after you save the first record store_downtown_horton and call the validate_unique() method on the second record store_downtown_fv, Django raises a ValidationError exception indicating there's already a Store record with the same name and email values in the database. Toward the end of this section I'll describe a Django model's Meta class options in greater detail.

Finally, the last validation method available on all Django models is the full_clean() method which is a shortcut to run the clean_fields(), clean() and validate_unique() methods -- in that order.

Data loading methods: refresh_from_db(), from_db() and get_deferred_fields() methods

The refresh_from_db() method is a helpful aid if you want to update a pre-existing model instance with data from the database, either because the database was updated by another process or you accidentally (or purposely) changed the model instance and want it to reflect the data in database once again. Using the refresh_from_db() method is as simple as executing it on a model reference (e.g. downtown.refresh_from_db() updates the downtown instance from values in the database).

Although the refresh_from_db() method is generally called without arguments, it does support two optional arguments. The using argument can be used to specify an alternate database from which to perform the refresh operation, a mechanism that works just like the option used in the save() and delete() methods and is described in table 7-3. The fields argument can be used to selectively refresh certain model fields, if no fields argument list is provided then the refresh_from_db() method refreshes all model fields.

In most circumstances, the initial loading mechanism for Django model instances is reasonable and sufficient. However, if you want to customize the default loading mechanism you can define the from_db() method. Unlike the refresh_from_db() method which can be called on a model instance, the from_db() method cannot be called directly and is intended to be part of a model class to be called every time a model instance is created from database data. So what is a good reason for the from_db method ? If you want to defer loading model data.

For example, if you start to work with large Django models (e.g. more than 10 fields) you can quickly notice a performance hit by accessing large amounts of data at once. To minimize this performance hit, you can create a from_db method to defer the loading of model field data, instead of having Django load the full field data set at once which it does by default.

Complementing the functionality of deferred model fields is the get_deferred_fields() method, which returns a list of model fields that have been deferred from loading. Although the from_db() and get_deferred_fields() methods don't have as many usage scenarios as the refresh_from_db() method, you may encounter a need for these two model methods once you work with larger and more complex models. The next chapter's section on model queries and CRUD operations describes the use of these methods in greater detail.

Custom methods

All the methods I've described up to this point come from Django's django.db.models.Model class. While it's important to learn how to use these methods and provide your own implementation for them, this doesn't necessarily mean a Django model class is restricted to using just these methods. You can use your own custom model class methods, as illustrated in listing 7-17.

Listing 7-17. Django model with custom method

class Store(models.Model):
    name = models.CharField(max_length=30)    
    address = models.CharField(max_length=30)
    city = models.CharField(max_length=30)
    state = models.CharField(max_length=2)
    
    def latitude_longitude(self):
        # Call remote service to get latitude & longitude
        latitude, longitude = geocoding_method(self.address, self.city, self.state)
        return latitude, longitude

The latitude_longitude method in listing 7-17 gives the Django model the ability to offer a common calculation on the model instance. For example, for a Store instance called downtown you could call downtown.latitude_longitude() to get a result based on the instance's address, city and state values aided by a remote service. This type of custom method is helpful because it favors encapsulation, keeping the logic that operates on a Django model where it belongs -- in the model class itself.

Model manager field: objects

The objects field -- technically known as the default Django model manager -- is available on all Django models and is charged with managing all query operations related to Django model instances. This means that when you perform Django model query operations (e.g. create, read, update or delete) you'll end up using a Django model's objects field or model manager.

A Django model's objects reference is used directly on a model class and not an instance of a model. For example, to read a Store model record with id=1 you would use the Store.objects.get(id=1) syntax and to delete all Store model records you would use the Store.objects.all().delete() syntax.

The objects field o model manager is not explicitly declared as part of Django model -- just as it was described in listing 7-1 alongside a Django model's id field -- but it's nevertheless present on all Django models. You can however customize the default model manager to use another field name (e.g. if you require a Django model field named objects, you can customize the model manager to be named mgr). Listing 7-18 illustrates a model class that renames the default model manger to use the mgr reference name.

Listing 7-18 Django default model manager renamed

class Store(models.Model):
    name = models.CharField(max_length=30)    
    address = models.CharField(max_length=30)
    city = models.CharField(max_length=30)
    state = models.CharField(max_length=2)
    mgr = models.Manager()

As you can see in listing 7-18, you explicitly declare a model field with models.Manager() to mark another field as the default model manager. If you override the default manager in this way, you would use the Store.mgr.get(id=1) syntax to read record with id=1 or the Store.mgr.all().delete() syntax to delete all store records vs. the default model manager objects syntax.

Note The next chapter on Django model queries is dedicated to exploring the functional aspects of the default model manager or objects reference, including CRUD operations with multiple records and some of the subtle behaviors associated with QuerySet classes, as well as model managers.

Model Meta class and options

In the previous section on model validation methods -- in listing 7-16 -- I made use of the Meta class on a Django model to enforce the uniqueness of model field values. In this section I'll expand on the purpose and options available for a Django model's Meta class.

The Meta class in a Django model is intended to define behaviors for a Django model as a whole, unlike Django model data types (e.g. models.CharField) which define granular behaviors on Django model fields (e.g.a model field data can be 30 or 50 characters in length).

The Django Meta class and its options are always declared after a Django model's data types, as illustrated in listing 7-19.

Listing 7-19. Django model with Meta class and ordering option

class Store(models.Model):
    name = models.CharField(max_length=30)    
    address = models.CharField(max_length=30)
    city = models.CharField(max_length=30)
    state = models.CharField(max_length=2)
        
    class Meta: 
        ordering = ['-state']

In listing 7-19 you can see the class Meta: statement declares the ordering = ['-state'] option. In this case, the ordering option tells Django that when a query is made on the model it order the results by the state field in descending order. The ordering meta option is helpful because it overrides the default model query order -- which is by a model's id -- and it avoids the need to constantly and explicitly declare a model query's sort order.

Now that you have a basic understanding of a Django model's Meta class, in the upcoming sections I'll classify the various Meta options by category so you can easily identify them and use them appropriately.

Database Definition Language (DDL) table options: db_table, db_tablespace, managed, required_db_vendor, required_db_features and unique_together

By default, the database table name for a Django model is based on the app name and model, with all lowercase letters and separated by an underscore. For example, for an app named stores, a model class named Amenity uses the stores_amenity database table by default. You can define a different database table name for a Django model with the meta db_table option.

By default, if a Django project's backing database brand (e.g. Oracle) supports the concept of a tablespace, Django uses the DEFAULT_TABLESPACE variable in settings.py as the default tablespace. It's possible to specify an alternative tablespace for a Django model through the meta db_tablespace option. Note that if a Django project's backing database doesn't support the concept of a tablespace, this option is ignored.

All Django models are subject to the life-cycle described earlier in the Django models workflow in figure 7-1. As part of this life-cycle, Django manages the DDL that creates and/or destroys the backing database table for every Django model. If you want to disable Django executing a model's default DDL against a database, you can do so with meta managed=False option. The managed=False option is useful when a model's backing database table is created by some other means and you don't want Django to interfere with the management of this table.

Because Django can work with different database back-ends (e.g. MySQL, Oracle, PostgreSQL) you can have situations where certain model definitions are designed to work with features that are not available on all database back-ends. To ensure a Django model is deployed against a certain database back-end, you can use two Meta class options.

The required_db_vendor option accepts the values sqlite, postgresql, mysql and oracle to ensure a project's underlying database connection is for a given vendor, if the connection does not match the specified vendor, the model is not migrated against the database.

The required_db_features option is used to ensure a backing database connection is enabled with a given list of features, if the connection does not have the specified feature list enabled, the model is not migrated against the database.

Django defines over 75 database features in django.db.backends.base.features.py. Once a connection is made to a database, you can get its supported Django features with:


from django.db import connection
dir(connection.features)

The previous snippet outputs the features supported by a database connected to a Django project. Most Django database features are supported across all brands, so the required_db_features option only requires to declare esoteric database features to ensure the underlying Django database can support a given model (e.g. gis_enabled ensures a database supports Django's Geographic Information Systems model feature, can_share_in_memory_db is a feature supported by SQLite but not MySQL, is_sql_auto_is_null_enabled is a feature supported in MySQL but not SQLite).

The unique_together option enforces no two model records have the same pair of values. For example, unique_together('city','state') ensures only one record has a city/state value (e.g. San Diego,CA) and rejects all other attempts to create a record with the same city/state value. The unique_together option creates the DDL to enforce this rule at the database level. The unique_together option also supports the ability to specify multiple unique field pairs through a list of tuples (e.g. unique_together(('city','state'),('city','zipcode')) enforces both city/state and city/zipcode fields are unique together).

Database Definition Language (DDL) index options: indexes & index_together

Indexes play an important roll in the efficient lookup of relational database records. In very simple terms, indexes are special structures that contain certain record column values which ensure queries are made faster than doing queries on the full record values in the main database table.

The Django meta class offers two options to generate the necessary DDL (e.g.CREATE INDEX...) to create database indexes against Django model fields: indexes and index_together.

Tip By default fields marked as primary keys and unique don't require explicit creation of indexes. See the previous section on 'db_index', 'primary_key' in model data types

The index meta options accepts a list of models.Index references. A model.Index reference accepts a fields value -- which in itself is a list of model fields on which to create an index -- and an optional name value -- which is a string to name the index. Listing 7-20 illustrates the use of the index meta option.

Listing 7-20. Django model with meta class and index option

class Store(models.Model):
    name = models.CharField(max_length=30)    
    address = models.CharField(max_length=30)
    city = models.CharField(max_length=30)
    state = models.CharField(max_length=2)
        
    class Meta: 
        indexes = [
             models.Index(fields=['city','state']),
             models.Index(fields=['city'],name='city_idx')
         ]

As you can see in listing 7-20, the Store model defines two indexes. A two-field index for the city and state fields, as well as an index for the city field named city_idx. If no name value is provided for an index, Django automatically gives the index a name.

Tip By default Django creates B*tree indexes for all relational database brands. However, if you're using PostgreSQL Django also supports Brin and Gin indexes[2].

The index_together meta option allows you to define a multi-field index on a Django model, just like the model.Index reference in the indexes meta options. The only difference being the index_together option is a top-level option of the Meta class (e.g. index_together=['city','state'] is equivalent to the first index in listing 7-20).

Naming convention options: verbose_name, verbose_name_plural, label, label_lower and app_label

By default, Django models are referenced by their class name. In most cases, relying on this name is a non-issue -- like the class Store in listing 7-20, a Store is a store anyway you see it. But on other occasions, a model class name can use an acronym or abbreviation that while reasonable for development purposes, is inexpressive for others (e.g. on a user interface(UI) element like a template or the Django admin).

Django models can use both the verbose_name and verbose_name_plural meta class options to assign more explicit model names. For example, if a model class is named SSN, you can declare the meta class option verbose_name='Social Security Number' to use this last value on UIs the rely on model instances. By default, when referring to multiple instances of a Django model class, Django pluralizes a model class by appending an s (e.g. Store becomes Stores). The verbose_name_plural meta class option allows you to define a custom plural value when the letter s is not applicable (e.g. the model class Strawberry with verbose_name_plural='Strawberries' becomes Strawberries in a plural context, instead of the default and incorrect Straberrys).

To access a Django model meta class verbose_name and verbose_name_plural values, you can use the syntax <class_name>._meta.verbose_name and <class_name>._meta.verbose_name_plural.

Tip If you want to assign a verbose name value to individual model fields, see the verbose_name in the previous section on Django model data types.

A Django model label refers to the combination of an <app_name>.<model_class>, which can sometimes be necessary when models are used ad part of forms or UI components. The Django model meta class offers the label and label_lower read-only attributes -- by read-only it means the attribute values can't be set (i.e. like other meta attributes such as verbose_name) but only read as part of the meta class. For example, if a Store model is defined inside the stores app, the statement Store._meta.label outputs stores.Store. And the label_lower attribute outputs a lower-case label value, including classes that use camel case[3] (e.g. for the model class StoreFront in the stores app, StoreFront._meta.label outputs stores.StoreFront. and Store._meta.label_lower outputs stores.storefront).

A Django app is an integral part of all Django models since it defines among other things: a model's default database table prefix, where a model's migrations are placed and a model's default reference label. The meta class app_label attribute allows you to assign an explicit app name to a Django model. The Django meta app_label takes precedence over the default app model naming mechanism. One of the last sections in this chapter on placing Django models outside of models.py contains more details on the meta app_label option.

Inheritance Meta options: abstract and proxy

The meta abstract option allows a Django model to function as a base class that doesn't have a backing database table, but serves as a foundation for other Django model classes. Listing 7-21 illustrates a set of Django models that use the abstract option.

Listing 7-21. Django model abstract option

from django.db import models

class Item(models.Model):
    name = models.CharField(max_length=30)
    description = models.CharField(max_length=100)
    class Meta:
        abstract = True

class Drink(Item):
    mililiters = models.IntegerField()

The Item model class in listing 7-21 is abstract, which means it behaves like other abstract classes across programming languages[4]. For Django model classes, it means no database table is created for this type of model, nor is it possible to create an instance out of it. However, it's possible to use an abstract model as the basis for other Django models.

In listing 7-21 you can see the Drink model class inherits its behavior from the Item class (vs. the standard Django models.Model class). Notice the Drink class declares the additional mililiters field, but since it inherits its behavior from the Item class, the Drink class also gains the model Item class fields (i.e. the Drink class becomes a three field model, with a database table with three columns -- name, description and mililiters).

The meta proxy option is also designed for Django model inheritance scenarios. But unlike the abstract option where parent classes are declared abstract and children inherit their behavior, the proxy option is designed to give child classes access to a parent class without the child becoming a full-fledged model class. Classes marked with the meta proxy option enable them to perform operations on a parent class and its database table, without having a database table of their own.

For example, a Store model class can have multiple model proxies (e.g. FranchiseStore, EmployeeOwnedStore) where each proxy class defines its own operations made against Store models, while not replicating Store data into a separate table. Django model proxies are ideal to shield a parent class against model changes (e.g. custom model managers or custom operations) while still retaining access to the database table of the parent class.

Query Meta options: ordering, order_with_respect_to, get_latest_by, default_manager_name, base_manager_name, default_related_name and select_on_save

When you make Django model queries -- which are explained in full in the next chapter -- there are many kinds of default behaviors associated with their operations. If you want to use non-default query behaviors, you can explicitly create queries with explicit arguments. However, creating explicit queries over and over can get tiresome, you can instead rely on the meta options explained in this section to assign a model's default query behaviors.

The ordering meta option is used to define the default ordering for a list of model instances. Although you can use the order_by() method to define the order for a group of model instances (e.g. Store.objects.all().order_by('-name'), to get all Store objects in descending order by name), you can use the ordering meta option (e.g. ordering=[-name] on the Store model) to ensure a list of model instances is always sorted without needing the order_by() method (e.g. Store.objects.all() uses the ordering meta option to determine query order).

The order_with_respect_to meta option is also used to define default sorting query behavior, but in the context of model relationships. If you perform a query on a model that has a relationship and you want to establish the default ordering on a field in the related model, you can use the order_with_respect_to meta option.

The get_latest_by meta option is used to specify a default model field for queries that use the latest() and earliest() methods. Both the latest() and earliest() methods specify a field by which to get the latest or earliest model record (e.g. Receipt.objects.latest('date') to get the latest Receipt model instance by date). You can use the get_latest_by meta option so queries made with the latest() and earliest() methods don't require an argument.

All models rely on the objects field as their default model manager, as described in the earlier 'Model manager field' section. The default_manager_name meta option is used in cases where a model has multiple model managers and you must specify a default manager. The base_manager_name meta option is used to specify a base model manager -- which defaults to django.db.models.Manager -- for cases in which a base model manager isn't appropriate. The next chapter describes the use custom model managers and these meta options in greater detail.

The default_related_name meta option is used to define the reverse name for a field that serves as a related object. This reverse name concept is explained in greater detail in the model relationships section later in this chapter.

The select_on_save meta option is a Django legacy option, which tells Django to use the pre-1.6 Django version of a model's save() method algorithm. This algorithm has a distinct behavior than the save() method behavior explained earlier in this chapter.

Permission Meta options: default_permissions and permissions

All Django models are given a set of permissions to manipulate object model instances. These permissions are an integral part of Django's built-in user management system, which is used extensively by the Django admin and can be integrated as part of the general permission workflow of a Django project.

By default, all Django models are given permissions to add, change and delete object instances through the meta default_permissions set to ('add','change','delete') -- who can actually add, change and delete instances is covered in the chapter on Django user management and the Django admin. You can declare an explicit meta default_permissions value on a model class to rescind one if its default permissions (e.g. an empty tuple () if you don't want to assign permissions or ('add','change') to deny a model class the delete permission).

The permissions meta attribute is designed to assign custom permissions to a Django model. For example, you can assign custom permissions to a Django model to allow tasks that are different than the generic add, change,delete operations (e.g. can_do_refunds on a Store model) . The permissions meta attribute accepts a list of tuple permissions, where each tuple is composed of a reference permission code and a verbose permission description (e.g. (('can_do_refunds','Can refund customers'),) ).A later chapter on Django user management covers the topic of custom model permissions.

  1. https://docs.djangoproject.com/en/1.11/ref/contrib/postgres/indexes/#module-django.contrib.postgres.indexes     

  2. https://en.wikipedia.org/wiki/Camel_case     

  3. https://en.wikipedia.org/wiki/Abstract_type