combining object with Meta and django models.Model - python

some newbie question here, I have a model like so:
from django.db import models
class SomeCommons(object):
# some fields here
# ...
class Meta:
abstract=True
class SomeDjangoModels(SomeCommons,models.Model):
pass
is it the same as the following model :
from django.db import models
class SomeModels(models.Model):
# some fields here
# ...
class Meta:
abstract=True
What I know that when doing like so SomeDjangoModels(SomeCommons,models.Model) the attribute from SomeCommons will be available in SomeDjangoModels, but the question is if the SomeCommons contains django Meta class will the Meta class also available in SomeDjangoModels? if it is, is there a way to prove it (the Meta class does exists)?
thanx

Yes, meta classes are inherited...
Meta inheritance
When an abstract base class is created, Django makes any Meta inner class you declared in the base class available as an attribute. If a child class does not declare its own Meta class, it will inherit the parent’s Meta. If the child wants to extend the parent’s Meta class, it can subclass it.
But in your case it does nothing as absrtact is set to False on the inheriting child class.
Django does make one adjustment to the Meta class of an abstract base class: before installing the Meta attribute, it sets abstract=False.
Asa result SomeModels will be abstract, but SomeDjangoModels will not.

No, those two definitions are not quite the same.
By default, a subclass will inherit its parent's Meta, but it will not inherit the abstract=True property (as the common use case is that subclasses will not be abstract).
If you do wish to inherit that, you must explicitly override the meta class, as shown in the documentation. (It appears from the question that you do wish SomeDjangoModels to also be abstract, but it's not entirely clear.)
If you do want a concrete (cf meta) subclass, then for all practical purposes the definitions are identical.

Related

How Meta classes works in python?

Origin of question I'm recently working with django and became used to of Meta class in models, Serializers, and Forms.
My Understanding so far I learned that meta classes are used for creating classes.
When one class is defined, Python will go inside the class and collect all attributes and methods and store as dictionary, after that it searches for __metaclass__ attribute. If defined, it will use that class to create the defined class else it will use default object.
Object is default class which is inherited to all classes, and this object class must have __metaclass__ which is type by default.
type class have __new__ and __init__ methods which is used to create classes.
My question
What is the flow of creating a class when we declare Meta class inside definition of class
For example
class Transformer(models.Model):
name = models.CharField(max_length=150, unique=True)
class Meta:
ordering = ('name',)
Where and When this Meta class is used?
Edit 1:
Cleared one thing that metaclasses and django Meta are different.
So Meta is just nested class of Transformer Model Class.
Question: Still my quesition is how this Meta class is used by Model Class?
As put in the comments: Python metaclasses are different from django metaclasses: Django just, for historical reasons, use the same terminology for the inner class where one annotates extra parameters about a class, where the primary members of the outer class are meant to correspond to fields in a model or form.
A Python metaclass, on the other hand, are what you are describing in your example, though you have checked some Python 2 documentation. In current Python, the metaclass is determined by passing the keyword argument "metaclas=" in the declaration of a new class, where the base classes go:
class MyClass(Base1, Base2, metaclass=MyMeta):
...
As far as I know it, the Django behavior had origin in which early versions of Django actually used a custom (Python) metaclass to annotate some of the parameters now used in the nested Meta - and in doing so, it took a shortcut of defining the metaclass inline inside the class body: instead of assigning the __metaclass__ name to an externally defined metaclass, as the usual for normal use, it would just define the class inplace: from the point of view of the language runtime, it would find the name __metaclass__ bound to a valid metaclass and use that to build the class.
Later versions, even in Python 2, modified this approach - the inner class was no longer the actual "metaclass" of the Model or Form (as the previous approach was clearly overkill).
Model Meta is basically the inner class of your model class. Model Meta is basically used to change the behavior of your model fields like changing order options,verbose_name_plural, and a lot of other options. It’s completely optional to add a Meta class to your model.
example:
class Category (models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique=True)
def __str__(self):
return self.name
class Meta:
verbose_name_plural= 'Categories'

Class inheritance python

In the case creating a model, for example
class Student(models.Model)
name=models.charfield(),roll=models.integerfield()
similarly,
In the case creating a form, class newform(forms.Form)
name=forms.charfield(),roll=forms.integerfield()
similarly,
In the case creating a serializer, class serial(serializers.Serializer)
name=serializers.charfield(),roll=serializers.integerfield()
I understood that in each classes,a base class is inherited but i am confused that if different objects of different classes are created inside a class in each scenario then what is the meaning of inheriting models.model, forms.Form,serializers.Serializer what these inherited classes do?
Django uses inheritance as well as object composition which are techniques of OOP for reusability.
Let us take your first class as example (I have only kept one field for simplicity):
Student(models.Model):
name = models.CharField(max_length=100)
Inheritance:
The first line Student(model.Model): does inheritance by inheriting from Model class using which you are getting methods like save(), delete(), clean_fields e.t.c. Now your Student class can reuse those methods.
Composition
The second line name = models.CharField(max_length=100) does object composition by creating object namely name of class CharField using which you get methods like check, get_internal_type e.t.c.
All of those Inbuilt classes (Model, CharField e.t.c) are defined in file namely models.py so when you do models.Model you are getting Model class from file models.py and models.CharField gives you CharField class from same file.
By inheriting from other classes, you have access to their methods;
Class A(object):
def _print(self):
print('Class A')
Class B(A):
def other_print(self):
print('Class B')
if __name__ == "__main__":
a, b = A(), B()
a._print()
b._print()
b.other_print()
When inheriting from model, forms, etc... You inherit from an object that is already integrated in the framework and thus has specific methods to work with the framework. For example the model will be registered to the database, the form 'knows' how to render properly, etc...
When you inherit from these classes, you already have an pre-built object with all these methods.

Share data between base class and Meta class

I have the following django model:
class Article(models.Model):
filename = models.CharField(max_length=255)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
keys = ['filename', 'collection']
class Meta:
constraints = [
models.UniqueConstraint(
fields=['filename', 'collection'],
name='article_key')
]
As you can see I've defined the same list ['filename', 'collection'] in both the base class and the Meta class. I would like to define it once. I can't define it in Meta because then I get 'Meta got an unrecognised attribute 'keys'. So I must define it in the base class and access it from Meta. I don't know how to share data between the two. I've tried doing:
self.keys
in Meta but that gives 'self is not defined'. I've also tried with just 'keys' but that's also not defined. Any tips? Thanks.
EDIT
Thank you to Willem for pointing out that I can define keys in Meta if I just call it '_keys'. If I do this, however, the question is then how do I access _keys from the base class? I've tried 'meta._keys' and 'Meta._keys'. Both not defined.
EDIT 2
For clarity, the reason that I want 'keys' defined in the base class is that I will (a) be accessing it from properties on the base class, and (b) want to be able to access it from the outside.
You can declare it before the class, then reference it from both the model class and it' Meta:
# making it a tuple since you probably don't want
# it to be mutable
_ARTICLE_KEYS = ('filename', 'collection')
class Article(models.Model):
# making it an implementation attribute since you
# probably don't want to be writeable
# (hint: provide a read-only property for access)
_keys = _ARTICLE_KEYS
class Meta:
constraints = [
models.UniqueConstraint(
fields=_ARTICLE_KEYS,
name='article_key')
]
But this is still ugly IMHO and very probably unecessary - the model's methods should be able to access those values thru self._meta.contraints[0].fields or something similar (don't have models with such constraints at hand right now so I can check how this is actually transformed by the models's metaclass but inspecting self._meta in your django shell should give you the answer).
The methods of a nested class cannot directly access the instance attributes of the outer class.
So, in your case, If you won't use the keys list in the Article class, just defined it once in the Meta class. Otherwise, you need to defined twice!

Model subclass: override CharField max_length

I have a supplied database schema for which I want to create a Django application. Many of the tables in the schema share a common set of columns, such as name and date_created. That prompted me to create an abstract Standard_model class containing those columns, and subclass the relevant models from it.
Unfortunately, some of the tables have a name column with a different max_length. I'm trying to come up with a way for the subclassed model to pass the max_length value to the abstract base class, but I'm drawing a blank.
Any ideas?
class Standard_model(models.Model):
name = models.CharField(max_length=50)
date_created = models.DateTimeField()
class Meta:
abstract = True
class MyModel(Standard_model):
name = models.CharField(max_length=80) # Can't do this.
No, you cannot override the name field definition:
In normal Python class inheritance, it is permissible for a child
class to override any attribute from the parent class. In Django, this
is not permitted for attributes that are Field instances (at least,
not at the moment). If a base class has a field called author, you
cannot create another model field called author in any class that
inherits from that base class.
See also:
In Django - Model Inheritance - Does it allow you to override a parent model's attribute?
And, FYI, according to the model naming convention, it should be called StandardModel.

Django - how to make required to implement field?

I'm buildibg some abstract model for about 10 models. I need to make, somehow, that 1 field is not declared in abstract model, but MUST be declared in inheriting models.
How to do that? Is there any way to use NotImplementedError?
I am afraid there isn't an easy way to achieve that, if possible at all, without digging deep into Django.
The main reason is that Field name "hiding" is not permitted in Django. What this means is that if you want to declare an abstract attribute in the base abstract class that is a Field instance, you will not be able to rewrite it in the child classes contrary to the normal Python class inheritance paradigm. To quote from the doc:
In normal Python class inheritance, it is permissible for a child class to override any attribute from the parent class. In Django, this is not permitted for attributes that are Field instances (at least, not at the moment). If a base class has a field called author, you cannot create another model field called author in any class that inherits from that base class.
Overriding fields in a parent model leads to difficulties in areas such as initializing new instances (specifying which field is being initialized in Model.init) and serialization. These are features which normal Python class inheritance doesn't have to deal with in quite the same way, so the difference between Django model inheritance and Python class inheritance isn't arbitrary.
This restriction only applies to attributes which are Field instances. Normal Python attributes can be overridden if you wish. It also only applies to the name of the attribute as Python sees it: if you are manually specifying the database column name, you can have the same column name appearing in both a child and an ancestor model for multi-table inheritance (they are columns in two different database tables).
Django will raise a FieldError if you override any model field in any ancestor model.
However, if the attribute is not a Field instance (very unlikely though), you will be able to achieve exactly what you want by using using #property decorator. Something like this should work:
class Person(models.Model):
def __init__(self, *args, **kwargs):
super(Person, self).__init__(*args, **kwargs)
self.last_name
first_name = models.CharField(max_length=30)
#property
def last_name(self):
raise NotImplementedError
class Meta:
abstract = True
class Student(Person):
home_group = models.CharField(max_length=5)
last_name = "Doe" # "models.CharField()" will not work!
class BadStudent(Person):
home_group = models.CharField(max_length=5)
# "NotImplmentedError" will be raised when instantiating BadStudent()
You may also want to take a look at abc.abstractproperty. I am not sure how it would work with Django's model inheritance though.
Why would you want to do it?? Which are the reasons the common field cannot be declared in the AbstractModel??
If you really want to do it, use the instructions here:
add methods in subclasses within the super class constructor

Categories