Model Manager in Django - No reference to model class? - python

I'm having a hard time understanding how works a modelManager in Django 1.6.
I don't understand where is the magic that makes this code work.
In the get_queryset(self) method there is no reference whatsoever to the Book class, so how come the DahlBookManager knows that it needs to work with the Book instances when doing super(DahlBookManager, self) (no reference to Book model, and as far as I know, self refers to an object of type "DahlBookManager" and not Book).
So either there is some kind of magic, or I REALLY need to review my Python 101.
I'd be happy to have some help, thanks!
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
dahl_objects = DahlBookManager() # The Dahl-specific manager.

When you create a model class in django, it calls add_to_class for each attribute on the model.
https://github.com/django/django/blob/1.6.5/django/db/models/base.py#L143
if what you're trying to add the class has a contribute_to_class method, then it gets called instead of calling setattr
https://github.com/django/django/blob/1.6.5/django/db/models/base.py#L264
So when you assign the manager to the model class with
dahl_object = DahlBookManager()
contribute_to_class() is called on the manager class, which receives the model class. It saves this on self.model:
https://github.com/django/django/blob/1.6/django/db/models/manager.py#L69
get_queryset() then uses this reference to self.model:
https://github.com/django/django/blob/1.6/django/db/models/manager.py#L123

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'

Django global filter

Is there a way to filter globally a Django model? We need to set a filter in a single place so that it gets applied in all queries generated by Django ORM including related objects lookups etc. Example:
class A(Model):
n = IntegerField()
class B(Model):
a = ForeignKey(A)
We want to set on A a global filter id__gte=10 (static to make it simple). The filter must be then automatically applied also when doing related queries, e.g.
B.objects.filter(a__n=123) # this code cannot be modified
should expand somehow magically to an equivalent of
B.objects.filter(a__n=123, a__id__gte=10)
We can change models, managers, querysets but we cannot change the code where objects are actually queried (a lot of code, third party apps, generic API).
What about creating a view in the database with the filter and create a Django model pointing to the view?
You should make a custom manager and modify an initial QuerySet. Check out the docs.
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
dahl_objects = DahlBookManager() # The Dahl-specific manager.
Then you should use your custom manager (dahl_objects) instead of objects and all queries will be modified.
Or you can override objects manager itself
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = DahlBookManager() # The Dahl-specific manager

Django - Why are variables declared in Model Classes Static

I have been reading and working with Django for a bit now. One of the things that I am still confused with is why the model classes that we create in Django are made up of static variables and not member variables. For instance
class Album(models.Model):
artist = models.CharField(max_length=128, unique=True)
title = models.CharField(max_length=128, unique=True)
genre = models.CharField(max_length=128, unique=True)
def __unicode__(self):
return self.name
I read this page here which explains static and instance variables in python however i am still confused as to why Django wants the field variables in models be static ?
Django uses a metaclass to create the model class. Just as a class's __init__() method creates a new instance, the metaclass's __new__() method creates the class itself. All variables, functions, properties, etc. defined in the class body are passed to the __new__() function. Strictly speaking, just defining a variable in the class body does not create a static/class variable -- only when the __new__() function receives the variable and sets it on the class, will it be a class/static variable.
Django overrides this behavior when it comes to fields and the special Meta inner class, by providing a custom __new__() method. The options in the inner Meta class are converted to an Options instance, and that instance is stored as Model._meta rather than Model.Meta. Similarly, any fields you define are stored in Model._meta.fields rather than as class/static variables.
You will notice that in your example, the class Album does not have a artist attribute: Album.artist will simply raise an AttributeError. This is because the metaclass moves the field from the class to Album._meta.fields. Instances of Album do have an artists attribute, but this is not the field. Instead, it's the database value related to the field. The __init__ method of a model uses self._meta.fields to populate any attributes with either the value that is passed to __init__, or with the default value, to assure that the instance variable exists.
Only class variables are passed to the __new__ method. If you were to define a field as an instance variable inside __init__, the field would never be passed to Model._meta.fields, and Django simply doesn't know about the field. You will be able to access Album.artist, but this will be the actual field instance, not the related database value. Basically you would be missing the magic that makes a model into a model.

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

Overwrite method add for ManyToMany related fields

Where should I overwrite method add() for ManyToMany related fields.
Seems like it is not manager 'objects' of my model. Because when we are adding new relation for ManyToMany fields we are not writing Model.objects.add().
So what I need it overwrite method add() of instance. How can I do it?
Edit:
So i know that there is ManyRelatedManager. One thing remain how can i overwrite it?
Sorry... not overwrite, but assign it in my Model by default.
http://docs.djangoproject.com/en/1.2/topics/db/managers/#custom-managers
You can create any number of managers for a Model.
You can subclass a ManyRelatedManager and assign it to the Model.
This example may be what you're looking for
# Then hook it into the Book model explicitly.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
dahl_objects = DahlBookManager() # The Dahl-specific manager.
The objects manage is the default. Do not change this.
The dahl_objects is a customized manager. You can have any number of these.

Categories