Model inheritance in Django

Source: Internet
Author: User

The model inheritance in Django is very similar to the class inheritance in Python, but you need to select a specific implementation method: to make the parent model have an independent database, or to make the parent Model contain only basic public information, the information can only be presented by the submodel.

Django has three inheritance relationships:

1. Generally, you just want to use the parent Model to save information that you do not want to repeat in the child model. The parent class is not used, that is, it does not generate a separate data table. In this case, the abstract base class is used to inherit the abstract base classes.

2. If you want to inherit from the existing model and each model has its own data table, use multiple tables to inherit multi-Table inheritance.

3. Finally, if you only want to modify the python-level behavior in the model without modifying the field. Proxy model (proxy models) is applicable to this scenario.

Abstract base classes

Abstract base classes are very useful if you want to add some public information to many models. After you write the base class, set it in the meta embedded class.Abstract = trueThis class cannot create any data tables. However, if it is used as the base class of other models, the fields of this class will be added to the subclass. If the abstract base class and subclass contain fields with the same name, an error occurs (Django throws an exception ).

class CommonInfo(models.Model):    name = models.CharField(max_length=100)    age = models.PositiveIntegerField()    class Meta:        abstract = Trueclass Student(CommonInfo):    home_group = models.CharField(max_length=5)

Sqlall result:

CREATE TABLE "myapp_student" (    "id" integer NOT NULL PRIMARY KEY,    "name" varchar(100) NOT NULL,    "age" integer unsigned NOT NULL,    "home_group" varchar(5) NOT NULL)

Student onlyModel generates a data table, while commoninfo cannot be used as a common Django model because it is an abstract base class. It does not generate data tables, nor has a manager, and cannot be directly instantiated or saved.

For many applications, this inheritance method is exactly what you want. It provides a way to extract public information at the Python language level, but at the database level, each subclass still creates only one data table, called table_per_class in JPA. In this way, each table contains a specific class and all the fields of the parent class on the inheritance tree. Because multiple tables have repeated fields, fields are redundant throughout the inheritance tree.

Meta inheritance

When creating an abstract base class, Django regards the valid meta embedded class you declared in the base class as an attribute. If the subclass does not declare its own meta embedded class, it will inherit the meta of the parent class. Sub-class meta can also directly inherit the meta embedded class of the parent class and extend it. For example:

class CommonInfo(models.Model):    name = models.CharField(max_length=100)    age = models.PositiveIntegerField()    class Meta:        abstract = True        ordering = [‘name‘]class Student(CommonInfo):    home_group = models.CharField(max_length=5)    class Meta(CommonInfo.Meta):        db_table = ‘student_info‘

Sqlall result:

CREATE TABLE "student_info" (    "id" integer NOT NULL PRIMARY KEY,    "name" varchar(100) NOT NULL,    "age" integer unsigned NOT NULL,    "home_group" varchar(5) NOT NULL)

Table is generated based on the specified name student_info.

During inheritance, Django makes an adjustment to the meta embedded class of the base class: Django sets abstract = false before installing the meta attribute. This means that the subclass of the abstract base class will not automatically become an abstract class. Of course, you can make an abstract class inherit from another abstract base class, but you must explicitly set abstract = true each time.

For abstract base classes, it is meaningless to put some attributes in the meta embedded class. For example, containing db_table means that all subclasses (that is, those that do not specify their own meta embedded classes) use the same data table. In general, this is not what we want.

Be careful when using related_name (Be careful with related_name)

If you use the related_name attribute in the foreignkey or manytomanyfield field, you must always specify a unique reverse name for this field. But doing so on the abstract base class will lead to a very serious problem. Because Django will add the base class fields to each subclass, and the attribute values of each subclass field are identical (related_name is included here ). Note: When foreignkey or manytomanyfield is used in this way, you cannot determine which subclass it points.

When you use related_name in (and only in) abstract base classes, if you want to bypass this problem, you must include '% (app_label) s' and' % (class) in the attribute values) s 'string.

1. '% (class) s' will replace the quilt class name.

2. '% (app_label) s' will replace the name of the app where the quilt class is located.

For example, in APP common, common/models. py:

class Base(models.Model):    m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")    class Meta:        abstract = Trueclass ChildA(Base):    passclass ChildB(Base):    pass

In another app, rare/models. py:

class ChildB(Base):    pass

The reverse names of the common. childa. M2M field are common_childa_related, the reverse names of the common. childb. M2M field are common_childb_related, and the reverse names of the rare. childb. M2M field in the rare app are.

If you do not define the related_name attribute for an associated field in the abstract base class, the default reverse name is the subclass name with '_ set ', whether it works properly depends on whether you have defined fields of the same name in the subclass. For example, in the code above, if the related_name attribute is removed, the reverse name of the M2M field in childa is childa_set, and the reverse name of the M2M field in childb is childb_set.

Multi-Table inheritance)

This is the second Inheritance Method supported by Django. When using this inheritance method, each sub-model at the same level is a real and complete model. Each sub-model has an exclusive data table, which can be queried and created. The inheritance relationship adds a link between the child model and each of its parent classes (implemented through an automatically created onetoonefield ). For example:

class Place(models.Model):    name = models.CharField(max_length=50)    address = models.CharField(max_length=80)class Restaurant(Place):    serves_hot_dogs = models.BooleanField()    serves_pizza = models.BooleanField()

Sqlall:

BEGIN;CREATE TABLE "myapp_place" (    "id" integer NOT NULL PRIMARY KEY,    "name" varchar(50) NOT NULL,    "address" varchar(80) NOT NULL);CREATE TABLE "myapp_restaurant" (    "place_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_place" ("id"),    "serves_hot_dogs" bool NOT NULL,    "serves_pizza" bool NOT NULL);COMMIT;

Both the parent class and Child class generate separate data tables. The store ID in the restaurant is linked together through onetoonefield. The inheritance relationship is represented by the Join Operation of the table. Joined is called in JPA. In this way, each table only contains fields defined in the class, and there is no redundant field. However, you must operate on the table corresponding to the subclass and all parent classes at the same time.

All the fields in place are valid in the restaurant, but the data is stored in another data table. Therefore, the following two statements can be run:

>>> Place.objects.filter(name="Bob‘s Cafe")>>> Restaurant.objects.filter(name="Bob‘s Cafe")

If you have a place, it is also a restaurant. You can use the lower-case sub-model to obtain the corresponding restaurant object from the place object:

>>> p = Place.objects.filter(name="Bob‘s Cafe")# If Bob‘s Cafe is a Restaurant object, this will give the child class:>>> p.restaurant<Restaurant: ...>

However, if P in the above example is not a restaurant (for example, it is only a place object, or it is a parent class of other classes. the restaurant will be removed. doesnotexist exception:

>>> from myapp.models import Place,Restaurant>>> p=Place.objects.create(name=‘Place‘,address=‘Place‘)>>> p.restaurantDoesNotExist: Place has no restaurant.

That is to say, no restaurant will be created when you create a place instance, but a place instance will be created when you create a restaurant instance:

>>>Restaurant.objects.create(name=‘M‘,address=‘M‘,serves_hot_dogs=True,serves_pizza=True)<Restaurant: Restaurant object>>>> Place.objects.get(name=‘M‘)<Place: Place object>

Meta (Meta and multi-Table inheritance) in Multi-Table inheritance)

In multi-Table inheritance, subclass inherits the meta embedded class of the parent class. All meta options have already played a role on the parent class, and re-use will only be counterproductive. (This is the opposite of using an abstract base class, because the abstract base class does not belong to its own content)

Therefore, the child model cannot access its parent class's meta embedded class. However, in some cases, sub-classes can inherit certain Meta from the parent class: If the sub-class does not specify Django. DB. models. options. ordering attribute or Django. DB. models. options. the get_latest_by attribute inherits these attributes from the parent class.

If the parent class has a sorting setting and you do not want any sorting settings for the Child classes, you can explicitly disable sorting:

class ChildModel(ParentModel):    # ...    class Meta:        # Remove parent‘s ordering effect        ordering = []

Inheritance and reverse Association (Inheritance and reverse Relations)

Because multi-Table inheritance uses an implicit onetoonefield to link child classes and parent classes, you can use the parent class to refer to child classes as in the previous example. However, the default related_name value of the onetoonefield field is the same as that of Django. DB. Models. Fields. foreignkey and Django. DB. Models. Fields. manytomanyfield. If you have many-to-one or many-to-many relationships with the child classes of other models, you must forcibly specify related_name on each of the many-to-one and many-to-many fields. If you do not do this, Django will throw an exception when you run the verification (validate) or synchronous database (syncdb.

For example, if the above place class is still used, we create a subclass with the manytomanyfield field:

class Supplier(Place):    # Must specify related_name on all relations.    customers = models.ManyToManyField(Restaurant, related_name=‘provider‘)

Specifying the parent link Field)

As we mentioned earlier, Django will automatically create an onetoonefield field to link the subclass to a non-Abstract parent model. If you want to specify the attribute name of The Link parent class, you can create your own onetoonefield and set parent_link = true to use this field to link the parent class.

Proxy model (proxy models)

When multi-Table inheritance is used, each subclass of the model creates a new data table. In general, this is the operation we want. This is because subclass requires a space to store field data that is not included in the base class. But sometimes, you may just want to change the model's behavior implementation at the python layer. For example, you can change the default manager or add a new method.

This is exactly what the proxy model inherits: Create a proxy for the original model ). You can create, delete, and update instances of the proxy model, and all data can be saved like the original model. The difference is that you can change the default sorting settings and default manager in the proxy model, without affecting the original model.

Declaring a proxy model is no different from declaring a common model. Set the proxy value in the meta built-in class to true to complete the Declaration of the proxy model.

For example, if you want to add a method to the standard user model (which is used in your template) that comes with Django:

class Person(models.Model):    first_name = models.CharField(max_length=30)    last_name = models.CharField(max_length=30)class MyPerson(Person):    class Meta:        proxy = True    def do_something(self):        # ...        pass

Sqlall:

CREATE TABLE "myapp_person" (    "id" integer NOT NULL PRIMARY KEY,    "first_name" varchar(30) NOT NULL,    "last_name" varchar(30) NOT NULL);

The myperson class and its parent class person operate on the same data table. In particular, any instance of person can also be accessed through myperson, and vice versa:

>>> p = Person.objects.create(first_name="foobar")>>> MyPerson.objects.get(first_name="foobar")<MyPerson: foobar>

You can also use the proxy model to define different default sorting settings for the model. The user model that comes with Django does not define the sorting settings (this is intentional because the sorting overhead is huge and we don't want to waste additional resources when getting users ). You can use a proxy to sort the username attribute, which is simple:

class OrderedPerson(Person):    class Meta:        ordering = ["last_name"]        proxy = True

The results of a common user query are unordered, while the ordereduser query results are sorted by username.

The query set only returns the model (querysets still return the model that was requested) used in the request)

Django does not return the myperson object no matter when you query the person object. Only the person object is returned for the query set of the person object. The essence of the proxy object is that the Code that relies on the original user is only valid for itself, and your own code uses your extended content. No matter how you change it, you will not get myperson when querying person.

Base class restrictions)

The proxy model must inherit from a non-abstract base class. You cannot inherit from multiple non-abstract base classes because a proxy model cannot connect different data tables. The proxy model can inherit any number of abstract base classes, provided that they do not define any model fields.

The proxy model inherits the meta options not defined in the proxy model from the non-abstract base class.

Proxy model Manager)

If you do not define any manager in the proxy model, the proxy model inherits the Manager from the parent class. If you define a manager in the proxy model, it will become the default manager, but the manager defined in the parent class is still valid.

In the above example, you can change the default manager, for example:

class NewManager(models.Manager):    # ...    passclass MyPerson(Person):    objects = NewManager()    class Meta:        proxy = True

If you want to add a new manager to the proxy but do not want to replace the existing default manager, you can refer to the method mentioned in custom Manager: create a base class that contains the new manager and put it behind the primary base class for inheritance:

class ExtraManagers(models.Model):    secondary = NewManager()    class Meta:        abstract = Trueclass MyPerson(Person, ExtraManagers):    class Meta:        proxy = True

You may not need to do this frequently, but it is feasible.

Differences between a proxy model and a non-hosted model (differences between proxy inheritance and unmanaged models)

Proxy model inheritance looks very similar to an unmanaged model that uses the managed attributes in the meta embedded class. But they are not the same. Which solution should you consider.

The difference is that you can define a field in the model where meta. Managed = false (in fact, it must be specified unless you really want to get an empty model ). Exercise caution when setting meta. db_table when creating an unmanaged model. This is because the created unmanaged model maps an existing model and has its own method. Therefore, if you want to ensure that the two models are synchronized and the program is modified, it will become redundant and fragile.

Another difference is that the two have different processing methods for managers. This is very important for the proxy model. The proxy model must be similar to the model behavior it represents. Therefore, the proxy model must inherit the manager of the parent model, including its default manager. However, in normal multi-Table inheritance, child classes cannot inherit the manager of the parent class, because the manager of the parent class may not be applicable when processing non-base class fields. Detailed introduction is provided in Manager documentation.

After implementing these two features (meta. Proxy and Meta. Unmanaged), we tried to combine them. The results show that the macro inheritance relationship is combined with the micro manager, which not only makes the API complex and difficult to use, but also makes it difficult to understand. These two options may be required in any situation, so they are still used independently.

Therefore, the general rule is:

1. If you want to mirror an existing model or data table without involving all columns of the original data table, set meta. managed to false. This option is usually used when you create a model for a database view or a data table that does not need to be controlled by Django.
2. If you want to make Python-level changes to the Model and keep the fields unchanged, set meta. proxy to true. Therefore, when data is saved, the proxy model completely copies the storage structure of the original model.

Multiple inheritance)

Like python, Django's model can also be inherited in multiple ways. Remember the name parsing rules of Python. If a specific name (for example, Meta) appears in the first base class, the subclass uses the specific name of the first base class. For example, if multiple parent classes contain meta embedded classes, only the meta of the first base class will be used, and others will be ignored.

Generally, you do not need to use multi-inheritance.

Field name "hiding" is not permitted)

Normal Python class inheritance allows subclass to overwrite any attributes of the parent class. However, in Django, rewriting field instances is not allowed (at least not yet ). If the base class has an author field, you cannot create any field named author in the subclass.

Rewriting the fields of the parent class can cause a lot of trouble, such as initializing the instance (specifying the fields instantiated in model. _ init _) and serialization. The general Python class inheritance mechanism cannot handle these features well. Therefore, the Inheritance Mechanism of Django is designed to be different from that of Python.

These restrictions only apply to field instances used as attributes, not to Python attributes. The Python attributes can still be overwritten. In python, the above restrictions only apply to the name of the field instance: If you manually specify the name of the database column, in the multi-inheritance, you can use the same column name in the subclass and a parent class. (Because they use two fields of different data tables ).

If you override a model field in any parent class, Django will throw a fielderror exception.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.