Overwrite method add for ManyToMany related fields - python

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.

Related

How to create child django models dynamically

I want to create 73 different django models, those models will be very similar, so in theory I would inherit from a base model class and just change the name of the Model/table.
I am aware this is not the best database structure, however I believe this unconventional structure may improve other aspects of my application. The initial point is to test this hypothesis.
How can I have django create the models, without me having to define all 73 of them?
class BaseModel(models.Model):
some_field = models.CharField(max_length=255)
some_other_field = models.CharField(max_length=255)
class Model_NR_01(BaseModel):
pass
...
class Model_NR_73(BaseModel):
pass
Also, in the sample above, I believe the BaseModel would also be created. How could I prevent that, having at the end of the migration only the 73 models mentioned? (If possible, of course).
PS.: I did searched several similar questions, couldn't find an actual answer, only warnings of how bad design it is. I am aware.
The three argument form of type can be used to create classes dynamically. The only thing you need to pass in the attributes dictionary is __module__ as using type this way to create a class will not automatically populate this attribute which is required by Django
class BaseModel(models.Model):
some_field = models.CharField(max_length=255)
some_other_field = models.CharField(max_length=255)
class Meta:
abstract = True
for i in range(1, 74):
model_name = f'Model_NR_{i:02}'
globals()[model_name] = type(model_name, (BaseModel, ), {'__module__': BaseModel.__module__})

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

Model Manager in Django - No reference to model class?

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

Django, override many-to-many field ModelManager

How can i override the model manager of a many-to-many field that i have considering the following:
class TermsManager(models.Manager):
def all(self):
return super(TermsManager, self).all().filter(condition_here)
class Term(models.Model):
objects = TermsManager()
name = models.CharField(max_length=255)
class Object(models.Model):
title = models.CharField(max_length=255)
terms = models.ManyToManyField(Term, blank=True)
class Channel(Object):
class Meta:
proxy = True
I also have a class which inherits from TermManager called ChannelTermManager.
How can i override the "terms" field of the Channel model so that
mychannel.terms calls the ChannelTermManager instead of TermManager?
First of all, you shouldn't be overriding all(). If you want to change the default queryset, override get_query_set like so:
class TermsManager(models.Manager):
def get_query_set(self):
return super(TermsManager, self).get_query_set().filter(condition_here)
This is because all() is often omitted when other queryset functions are chained on, and you want your queryset to behave the same whether all() is explicitly called or not.
But even so, what you're doing is still problematic. As explained in the documentation for managers, filtering the default related queryset will affect all sorts of automatic things behind the scenes (such as when dumping data to create backups/fixtures, etc.). You almost definitely do not want this. And you really don't want your related object managers doing this either (by setting use_for_related_fields = True), because you'll be masking what's actually stored in the database, rather than simply detecting out of date data and creating alerts or whatever to clean it up. use_for_related_fields is intended for creating managers that augment the normal capabilities of the vanilla manager, not to filter.
I had a similar situation to yours however, and I handled it like so:
class FilteredTermsManager(models.Manager):
def get_query_set(self):
return super(TermsManager, self).get_query_set().filter(condition_here)
class Term(models.Model):
allTerms = models.Manger() # Establish this as the default/automatic manager
objects = FilteredTermsManager()
name = models.CharField(max_length=255)
This way, I could do all my initial querying on the model through my filtered queryset and it looks like "regular Django", but all relational and behind the scenes queries would work on the unfiltered database. And I could always access the true full set of objects by manually doing Term.allTerms.all().
As for using different managers for different related objects, there's nothing you can really do there. But why not just add Channel specific objects to your custom manager, and simply not call them from methods that operate on get Term querysets from Object?

Django model fields: reference to self on default keyword

I've been having problems to understand this and to come up with a way of doing a reference to self inside the default keyword of a model field:
Here is what I have:
class Bank(models.Model):
number = models.CharField(max_length=10)
class Account(models.Model):
bank = models.ForeignKey(Bank, related_name="accounts")
number = models.CharField(max_length=20)
created = models.DateTimeField(auto_now_add=True)
creator = models.ForeignKey(User)
# This is the guy
special_code = models.CharField(max_length=30, default='%s-%s' % (self.number, self.bank.number))
So I'm trying to access self inside the class definition, which seems to not work out because python doesn't know where self is since its not an object yet.
I've tried different things like:
special_code = models.CharField(max_length=30, default='%s-%s' % (number, bank.number))
But in this case it doesn't recognize bank.number because bank its only a property with models.ForeignKey.
I've tried also using a method inside the Account class:
def bank_number(self):
return self.bank.number
and then:
special_code = models.CharField(max_length=30, default='%s-%s' % (number, bank_number()))
That was kinda dumb because it still needs self.
Is there a way I can do this?
I need it to store the number inside the database, so using a method like this wont help:
def special_number(self):
return '%s-%s' % (self.number, self.bank.number)
I don't think there's any way to access self in the default callable. There's a couple of other approaches to set your field's value:
If you don't want the user to be able to change the value, override the model's save method and set it there.
If the default is just a suggestion, and you do want to allow the user to change it, then override the model form's __init__ method, then you can access self.instance and change set the field's initial value.
Instead of specifying a default for the field you probably want to override the save() method and populate the field right before storing the object in the database. The save() method also has access to self. Here is an example in the docs for that:
https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-model-methods
As already answered, override the save() method of your model to assign a value to special_code. The default option of a field is not meant to depend on other fields of the model, so this will not work.
Also, have a look at the editable option, if you don't want the field to be edited.
special_code = models.CharField(max_length=30, editable=False)
Will prevent the field to be rendered in ModelForms you create from the model.

Categories