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.
Note The previous section on Django model data types describes Django's various unique values options for model fields: unique, unique_for_date, unique_for_month and unique_for_year.
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 orobjects
reference, including CRUD operations with multiple records and some of the subtle behaviors associated withQuerySet 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.