⬆️ ⬇️

Translation Django Documentation: Models. Part 4 (Last)

image



Good day!



This is the last part of a series of my translations of the section on models from the Django documentation .

')

Translation Django Documentation: Models. Part 1

Translation Django Documentation: Models. Part 2

Translation Django Documentation: Models. Part 3



_____ Multiple table inheritance

_______ Meta class and multi-table inheritance

_______ Inheritance and Reverse Relations

_______ Creating a field with parent_link

_____ Proxy Models

_______ Requests return objects of the model to which they are addressed.

_______ Restrictions for base classes

_______ Proxy Model Managers

_______ Differences between proxy models and unmanaged models

_____ Multiple Inheritance

_____ Overriding field names is not allowed.









Multi-table inheritance




The second type of model inheritance supported by Django is used when each model in the hierarchy is a model in itself. That is, each model belongs to its own database table and can be used individually. Inheritance relationships are the links between the derived model and each of its parents (using the automatically generated OneToOneField field). For example:

Copy Source | Copy HTML<br/> class Place (models.Model):<br/> name = models.CharField(max_length= 50 )<br/> address = models.CharField(max_length= 80 )<br/> <br/> class Restaurant ( Place ):<br/> serves_hot_dogs = models.BooleanField()<br/> serves_pizza = models.BooleanField() <br/>


All fields of the Place class will also be available in the derived class Restaurant , but their data will be contained in different tables. So two cases are possible:

Copy Source | Copy HTML<br/>>>> Place.objects. filter (name= "Bob's Cafe" )<br/>>>> Restaurant.objects. filter (name= "Bob's Cafe" ) <br/>


If you have an object of the Place class, which is also an object of the Restaurant class, you can access the Restaurant object from the Place object using the name of the derived lowercase model:

Copy Source | Copy HTML<br/>>>> p = Place.objects. filter (name= "Bob's Cafe" )<br/> # If Bob's Cafe is a Restaurant object, this will give the child class: <br/>>>> p.restaurant<br/><Restaurant: ...> <br/>


If p in this example is not an object of the Restaurant class (it was created directly as an object of the Place class or is basic for some other classes), the expression p.restaurant will raise the exception Restaurant.DoesNotExist .







Meta class and multi-table inheritance



In a situation with multi-table inheritance, derived classes do not make sense to inherit the data of the class Meta of the base class. All Meta parameters have already been applied to the base class and their repeated use can lead to contradictory consequences (in contrast to the situation with an abstract base class, which, in fact, does not exist by itself).



So, the derived model does not have access to the parent class Meta . However, there are several cases in which the derived class inherits the behavior of the base class: if the derived class does not define the attributes django.db.models.Options.ordering or django.db.models.Options.get_latest_by , it will inherit them from the parent class.



If the base class contains these attributes, but you do not want derivatives to use them, you can forcibly disable them:

Copy Source | Copy HTML<br/> class ChildModel (ParentModel):<br/> ...<br/> class Meta :<br/> # Remove parent's ordering effect <br/> ordering = [] <br/>








Inheritance and inverse relationships



Since for multi-table inheritance, the OneToOneField field is implicitly used to create links between the derived and the base class, we can access the derived class through the base class, as shown in one of the examples above. However, it uses the name, which is the default for related_name of the django.db.models.fields.ForeignKey and django.db.models.fields.ManyToManyField fields . If you use these relationship types in a different model, you must define the related_name attribute for each such field. Otherwise, Django will report an error when performing validate or syncdb .



For example, use the Place class described above once again to create another subclass with the ManyToManeField field:

Copy Source | Copy HTML<br/> class Supplier (Place):<br/> # Must specify related_name on all relations. <br/> customers = models.ManyToManyField(Restaurant, related_name= 'provider' ) <br/>








Creating a field with parent_link



As already mentioned, Django automatically creates a OneToOneField field to associate the derived class with a non-abstract base. If you want to manage the name of an attribute that refers to a parent class, you can create your own OneToOneField field and specify parent_link = True in it to show that your field refers to the base class.







Proxy models


Added in Django version 1.1 : please read the release notes .



When using multi- table inheritance, new database tables are created for each subclass of the model. This is what you need if the subclass needs space to store additional data that is represented in the base class. However, sometimes you only need to change the behavior of the model - for example, add some method.



For this, inheritance using a proxy model is used. By creating a proxy for your model, you can create, delete, and modify instances of the proxy model, and all results will be saved as if you were using the main (non-proxy) model. The difference between them is that you can change things like standard sorting or the default proxy model manager without changing the original.



Proxy models are declared the same as normal models. You tell Django that this class is a proxy model, assigning True to the proxy attribute of the Meta class.



For example, suppose you want to add a method to the standard User model that will be used in your templates. You can do this as follows:

Copy Source | Copy HTML<br/> from django.contrib.auth.models import User<br/> <br/> class MyUser (User):<br/> class Meta :<br/> proxy = True<br/> <br/> def do_something (self):<br/> ... <br/>


The MyUser class operates on the same database table as the base User class. In particular, any instances of the User class will be available through the MyUser class, and vice versa:

Copy Source | Copy HTML<br/>>>> u = User.objects.create(username= "foobar" )<br/>>>> MyUser.objects.get(username= "foobar" )<br/><MyUser: foobar> <br/>


You can also use a proxy model to define different sorts in your model by default. The standard User model does not have its own sorting (this is done intentionally: sorting requires memory, and we don’t want to use it every time we select users). Perhaps you want to constantly sort users by name when using a proxy model. It's simple:

Copy Source | Copy HTML<br/> class OrderedUser (User):<br/> class Meta :<br/> ordering = [ "username" ]<br/> proxy = True <br/>


Now, requests for the User class will not be sorted, and requests for the OrderedUser class will be sorted by name.







Requests return objects of the model to which they are addressed.



There is no way to force Django to return, say, an object of class MyUser when we send a request to objects of class User . A query for objects of class User will return the same type of object ( User comment). The basic idea of ​​using proxy objects is that the code belonging to the User class will use this class, and your own code will be able to use the extensions that you have connected. Note also that this is not at all a way to everywhere replace the User model (or any other) with your own.







Restrictions for base classes



The proxy model should inherit only one non-abstract class. You cannot inherit several non-abstract models, since the proxy model does not provide for any connection between columns in different database tables. The proxy model can inherit any number of abstract classes if they do not have fields.



Proxy models inherit Meta parameters that they do not receive from their non-abstract base classes (models for which they are proxies).







Proxy Model Managers



If you do not create managers for the proxy model yourself, they will inherit from the base classes of your model. If you declare a manager for the proxy model, he will become the default manager, other managers defined in the base classes will also be available. Continuing with our example above, you can change the standard manager used when querying the User model:

Copy Source | Copy HTML<br/> class NewManager (models.Manager):<br/> ...<br/> <br/> class MyUser (User):<br/> objects = NewManager ()<br/> <br/> class Meta :<br/> proxy = True <br/>


If you want to add a proxy model manager so that it does not replace the already existing default manager, you can use the technique described in the documentation for your own managers : create a base class containing new managers and inherit it after the main base class:

Copy Source | Copy HTML<br/> # Create an abstract class for the new manager. <br/> class ExtraManagers (models.Model):<br/> secondary = NewManager()<br/> <br/> class Meta :<br/> abstract = True<br/> <br/> class MyUser (User, ExtraManagers ):<br/> class Meta :<br/> proxy = True <br/>


Most likely, you will not have to use it often, but know that this is possible.







Differences between proxy models and unmanaged models



Inheritance using a proxy model is rather similar to creating a unmanaged model using the managed attribute in its Meta class. These two alternatives are not the same, but they both deserve consideration so that you can decide what to use.



The first difference is that you can (and, in fact, should, if you do not want to create an empty model) define the fields in the model with Meta.managed = False . You could also, by carefully configuring Meta.db_table, create an unmanaged model that exactly matches an existing one and add methods to it. However, this would be very boring and unreliable, since you must synchronize copies with any changes.



Another difference, more important for proxy models, is how model managers are processed. Proxy models behave exactly the same as the models for which they are proxies. They inherit the managers of their base classes, including the default manager. In the case of normal multi-table inheritance, the derived model is not inherited by managers, because when using additional fields, custom managers are not always appropriate. Documentation for managers contains more information on this issue.



When these principles were created, attempts were made to combine them into one option. However, it turned out that the interaction with inheritance in general, and in particular with the use of managers, made the API very complex and potentially difficult to perceive and use. It turned out that both options are necessary in any case, so a similar separation has arisen.



Thus, the general rules are:



  1. If you want to create an exact copy of an existing model or database table, but do not want the resulting model to contain all the columns of a database table, use Meta.managed = False . This method is usually useful for modeling database images and tables outside of Django.
  2. If you want to change the model's behavior at the language level, but keep all the same fields as the original, use Meta.proxy = True . In this case, your model becomes an exact copy of the original, including the data storage structure.








Multiple inheritance




Just as with inheritance in Python, a model in Django has the ability to inherit many basic models. This means that Django maintains a naming convention. It is the base class (for example, Meta ) that is first in the definition that will be used; for example, this means that if the base classes contain the Meta class, only the first of none will be used, all others will be ignored.



As a rule, you will not need to use multiple inheritance. The main area of ​​its application is “mix-in” classes (all-in-one comment per.per.): Adding separate additional fields to each class inheriting from the mix-in . Try to create your inheritance tree as simply and unambiguously as possible, so you don’t have to constantly find out where this or that information came from.







Redefinition of field names is prohibited



In the usual Python language inheritance, a derived class can override the attributes of any base class. In Django, this is not allowed (at least for the time being) when the attributes are instances of fields . If the base class contains the author field, you cannot create a new field with the same name in any model that inherits this class.



Overriding fields in the base model leads to problems when initializing new instances (indicating which field was initialized in Model .__ init__ ) and during serialization. These features that normal Python inheritance does not encounter, so the difference in the inheritance of Django models and the inheritance of Python classes is not a fad.



This restriction applies only to attributes that are instances of fields . Regular attributes can be redefined as you wish. This only applies to attribute names, the way Python sees them: in the case of multi-table inheritance, if you manually define a database column, you can store columns with the same name in both the derivative and the base model (the columns are in different database tables).



Django will throw a FieldError exception if you override any field in the base model.







That's all. This is the last part of the translation, which, I hope, still turned out to be or will be useful to someone other than me. Anyone who is somehow interested in the continuation of the translation of the documentation, please write your suggestions with habrapost or in the comments.



Thanks for attention ;)



Translation Django Documentation: Models. Part 1

Translation Django Documentation: Models. Part 2

Translation Django Documentation: Models. Part 3

Source: https://habr.com/ru/post/80030/



All Articles