This is a question prompted by another question from me.
Django provides Abstract base classes functionality (which are not to same as ABC classes in Python?) so that one can make a Model (Django's models.Model) from which one can inherit, but without that Model having an actual table in the database. One triggers this behavior by setting the 'abstract' attribute in the Model's Meta class.
Now the question: why does Django solve it this way? Why the need for this special kind of 'Abstract base class' Model? Why not make a Model mixin by just inheriting from the object class and mixing that in with an existing Model? Or could this also by a task for Python ABCs? (mind you I'm not very familiar with ABC classes in Python, my ignorance might show here)
I'll try to be reasonably brief, since this can easily turn into a lengthy diatribe:
ABCs are out because they were only introduced in Python 2.6, and the Django developers have a set roadmap for Python version support (2.3 support was only dropped in 1.2).
As for object-inheriting mixins, they would be less Pythonic in more ways than just reducing readability. Django uses a ModelBase metaclass for Model objects, which actually analyses the defined model properties on initialisation, and populates Model._meta with the fields, Meta options, and other properties. It makes sense to reuse that framework for both types of models. This also allows Django to prevent abstract model fields from being overriden by inheriting models.
There's plenty more reasons I can think of, all of them minor in themself, but they add up to make the current implementation much more Pythonic. There's nothing inherently wrong with using object-inheriting mixins though.
One of the reasons is because of the way fields are defined on a model.
Fields are specified declaratively, in a way that a normal class would treat as class attributes. Yet they need to become instance attributes for when the class is actually instantiated, so that each instance can have its own value for each field. This is managed via the metaclass. This wouldn't work with a normal abstract base class.
Related
Can we say that Django models are considered dataclasses? I don't see #dataclass annotation on them or on their base class model.Models. However, we do treat them like dataclasses because they don't have constructors and we can create new objects by naming their arguments, for example MyDjangoModel(arg1= ..., arg2=...).
On the other hand, Django models also don't have init methods (constructors) or inherit from NamedTuple class.
What happens under the hood that I create new Django model objects?
A lot of the magic that happens with models, if not nearly all of it, is from its base meta class.
This can be found in django.db.models.ModelBase specifically in the __new__ function.
Regardless of an __init__ method being defined or not (which actually, it is as per Abdul's comment), doesn't mean it can or should be considered a dataclass.
As described very eloquently in this SO post by someone else;
What are data classes and how are they different from common classes?
Despite django models quite clearly and apparently seeming to have some kind of data stored in them, the models are more like an easy to use (and reuse) set of functions which leverage a database backend, which is where the real state of an object is stored, the model just gives access to it.
It's also worth noting that models don't store data, but simply retrieves it.
Take for example this simple model:
class Person(models.Model):
name = models.CharField()
And then we did something like this in a shell:
person = Person.objects.get(...)
print(person.name)
When we access the attribute, django is actually asking the database for the information and this generates a query to get the value.
The value isn't ACTUALLY stored on the model object itself.
With that in mind, inherently, django models ARE NOT dataclasses. They are plain old regular classes.
Django does not work with data classes. You can define a custom model field. But likely this will take some development work.
I currently have an abstract base class that several of my models inherit from. I have run into an issue trying to filter on it.
I was told in Django you can NOT filter on an abstract model and Django ORM can only search tables. So the alternative I have found is concrete (multi-table) inheritance. But after reading a post on how this creates bad performance in Django I'm hoping there is another alternative?
Is it possible to search on a abstract base class in Django i.e. filter? I'm using Django 1.6.1 there has to be an option other than filtering on every table.
Bad performance is rather relative imo. Do you expect high db concurrency? There's nothing wrong with leaf inheritance if it solves your problem. Afaik Jacob is just making people aware what's going on beneath the hood in his article.
Heads up: https://djangosnippets.org/snippets/1034/
I am trying to write an alternative way of creating models, other from the one Django uses (metaclasses that is).
Currently, Django uses a metaclass to put all field declarations from a user-declared model in a fields dictionary and then uses this dictionary to assign user-declared model attributes from the given kwarg list provided when instantiating this model.
What other methods of doing this are available? I thought about using decorators, but then I cannot subclass the base Model class (upon which the decorator acts) to create user-declared models. Thank you for pointing me in any direction!
In my application i have the requirement of keppling logs of all models changes and delete.
So i have created baseclass Audit and extended all classes from it.
I have overridden save , delete methods in it so that i keep old chnages as well when we do some updation.
I want to know that is there any better way of doing that rather than extending all classes fron base class. Or is it all right like that.
For this use case, you may be able to write a generic function that could be used with django signals.
https://docs.djangoproject.com/en/dev/topics/signals/
Suppose you have to model several classes that should have composite properties like dimensions (width and height) or phone number (prefix, number and extension).
In Java (using JPA 2) I'd create a Dimensions class and annotate it with #Embeddable. This causes Dimension's fields (e.g. width and height) to be embedded into every class that declares a property of type Dimensions.
How do you model these with Django while avoiding code duplication? It doesn't make sense to create a separate Dimensions model and reference it with a ForeignKey field. And the classes do not have enough in common to justify model inheritance.
I think you might be over-thinking inheritance. Inheritance is and is actually the recommended method to composite models. The following is an example of how properly use model inheritance in Django:
class PhoneModelBase(model.Model):
phone = models.CharField(max_length=16)
...
class Meta:
abstract = True
class PhoneModel(PhoneModelBase):
# phone is here without typing it
# the only restriction is that you cannot redefine phone here
# its a Django restriction, not Python restriction
# phone = models.CharField(max_length=12) # <= This will raise exception
pass
So what this does is it creates a model PhoneModelBase, however the key here is that it uses class Meta with abstract=True.
Here is more of behind the scenes of what is going on and some explanation of some of the Python concepts. I assume that you are not aware of them since you mentioned Java in the question. These Python concepts are actually rather confusing concepts so my explanation is probably not full, or even confusing, so if you will not follow, don't mind. All you have to know is to use abstact = True. Here is the official doc: https://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes.
Meta attribute within PhoneModelBase is just that, an attribute. It is the same as any other attribute within the class, except its a class instance (remember that in Python classes and functions are first order members). In addition, Python has this thing called __metaclass__ which you can add to you classes. __metaclass__ defines a way of how an instance of a class is build. More about that here. Django uses these in how it creates model class instances.
So to create the PhoneModelBase class, the following is a rough outline:
When an instance of the PhoneModelBase class (the class itself, not instance of class - PhoneModelBase()) is being created, the __metaclass__ which comes from model.Model due to inheritance takes over the creation process
Within the __metaclass__, Python calls the function which creates the actual class instance and passes to it all of the fields from the class you are trying to create - PhoneModelBase. That will include phone, Meta and any other fields you define
It sees the Meta attribute and then it starts analyzing its attributes. According to the values of these attributes, Django will change the behavior of the model
It sees the abstract attribute and then changes the logic of the class its trying to create - PhoneModelBase by not storing it in db
So then the PhoneModelBase, even though its definition looks very similar to a regular model, its not a regular model. It is just an abstract class which is meant to be used as composite in other models.
When other models inherit from PhoneModelBase, their __metaclass__ will copy the attributes from the base model as if you manually typed those attributes. It will not be a foreign key on anything like that. All of the inherited attributes are going to be a part of the model and will be in the same table.
Hopefully all of this makes some sense. If not, all you have to remember is just to use Meta class with abstract = True.
EDIT
As suggested in the comment, you can also inherit from multiple base classes. So you can have PhoneModelBase, DimensionsModelBase, and then you can inherit from both of these (or more), and all of the attributes from all base classes will be present in your model.