accessing custom managers via a _set option in django - django

I am having two models and a custom Manager:
class ActiveModelAManager(models.Manager):
def get_query_set(self):
return super(ActiveModelAManager,self).get_query_set().filter( active = True)
class ModelA(models.Model):
name = CharField(....)
active = BooleanField()
active_models = ActiveModelAManager()
objects = models.Manager()
class ModelB(models.Model):
modelA = ForeignKey(ModelA)
in my view I am passing ModelA to the template and I would like to access ModelB_set but using my active_models manager instead of objects ?
so if I am doing this :
{{ ModelB.modelb_set.all }}
I am accessing the objects manager but I want to access the active_models.
Same problem goes when I am trying to access the custom manager via my view.
Any idea how can I accomplish this ?

Not sure whether I got your question right, but in order to query for the modelB objects which reference to an active ModelA object, in your view you can just write.
active_modelA_objects = ModelA.active_models.all().values_list('pk', flat=True)
modelB_objects_with_active_modelA = ModelB.objects.filter(modelA__in=active_modelA_objects)
Related docs:
values-list
__in

Related

Overrding get_queryset in the model file without having to create a new model manager in django?

I have a model and I have used it at multiple places now and changing it all places is cumbersome. Could you please let me know how to do that? I already know the I can create a custom model manager but it's not feasible to change the model manager at all places.
class Slot(models.Model):
state = models.CharField(max_length=20, choices=SLOT_STATE, default=SLOT_STATE[0][0])
According to 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.
I want to retain the same default manager but I want to exclude all the NULL slot. P.S. slot can have null state.
Following the docs, you could add a custom manager which returns only objects where state is not NULL:
class SlotNoNullStateManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(state__isnull=False)
class Slot(models.Model):
...
objects = models.Manager() # the default manager
no_null_state_objects = SlotNoNullStateManager()
Now your model has 2 managers; and the default behaviour has not changed because it still uses the same default manager for .objects.
It is importante to NOT use .filter() in the .get_queryset() method of the default manger, see https://docs.djangoproject.com/en/3.2/topics/db/managers/#don-t-filter-away-any-results-in-this-type-of-manager-subclass
If you really want to change the default behaviour, despite the warnings, the simply interchange the 2 managers:
class Slot(models.Model):
...
# this has the be placed before the other manager, so that
# it becomes the default manager
objects = SlotNoNullStateManager()
all_objects = models.Manager()
If you still want the rows that have NULL, then you use the manager all_objects.

Access related manager method from another manager

I am trying to access a manager method from a manager of a related model but I'm having no success.
Let's say these are my models:
class ModelA(models.Model):
description = models.TextField(blank=True)
objects = ModelAQuerySet.as_manager()
class ModelB (models.Model):
a = models.ForeignKey(ModelA, related_name='a_objs')
objects = ModelBQuerySet.as_manager()
And I am defining the managers as QuerySets, that is:
class ModelAQuerySet(models.QuerySet):
def description_starts_with(self, desc):
return self.filter(description__startswith=desc)
class ModelBQuerySet(models.QuerySet):
pass
What I would to do is to create a ModelBQuerySet method that prefetch modelA objects starting with a certain description.
Here's a WRONG example of what I am trying to do:
class ModelBQuerySet(models.QuerySet):
def prefetch_a_objs_starts_with(self, desc):
return self.prefetch_related(
Prefetch('a_objs',
queryset=ModelAQuerySet.description_starts_with(desc),
to_attr='a_objs_with_desc'))
This of course doesn't work because description_starts_with is an instance method, and here I am invoking it from the class.
Even this is completely wrong I hope it gets the idea of what I'm trying to do.
Question is: how to create a models.QuerySet method with a prefetch that can access its related models.QuerySet instance methods?

How to create a duplicate of existing User model - django admin

How to create a duplicate of the existing User model which display objects only if a condition is satisfied?
Here in this pic, pending users should show all the users with is_active = True:
In the documentation, there was a similar statement:
pendinguser = models.ForeignKey(User, limit_choices_to={'is_staff': True})
How and where to add this to make this work?
See this example:
you have your user model 'User'
Now create a new model and inherit User model like this
class ProxyUser(User):
objects = ProxyUserManagaer()
class Meta:
proxy = True
Now create you custom definition of this Proxy model in manager function:
class ProxyUserManager(models.Manager):
def get_queryset(self):
return super(FeatureManager, self).get_queryset().filter(<add your custom definiton her>)

How to make a custom manager work with non-default database in django?

I want to make a custom manager for my models stored in my database 'db2' so that i don't have to do model.objects.using('db2').all().
The code for my models and custom manager is
class ViewerProfileManager(models.Manager):
def get_query_set(self):
return super(ViewerProfileManager,self).using('db2').get_query_set() # results in recursion error
class ViewerProfile(models.Model):
name = models.CharField(max_length=32)
count = models.IntegerField(blank=True,null=True)
objects = models.Manager()
profiles = ViewerProfileManager()
-------------
>>> ViewerProfile.profiles.all() # maximum recursion depth exceeded error
What's wrong with what i am doing here and how to make it work?
SOLUTION : If you just want to use another database 'db2' for every query just use this
class ViewerProfile(models.Model):
name = models.CharField(max_length=32)
count = models.IntegerField(blank=True,null=True)
objects = models.Manager()
profiles = objects.db_manager('db2') # manager for evaluating querysets on database db2
Later on if you want to add custom querysets override objects manager by making a class inheriting from models.Manager and define your querysets in it.
You can call the method on the parent (using super()) or do the appropriate handling of the _db attribute on the manager (a string containing the name of the database to use).
If you want to return a custom QuerySet class from the get_query_set method :
class ViewerProfileManager(models.Manager):
def get_query_set(self):
qs = CustomQuerySet(self.model)
if self._db is not None:
qs = qs.using(self._db)
return qs
or use this:
class ViewerProfileManager(models.Manager):
using="db"
def get_query_set(self,request):
return super(ViewerProfileManager,self).queryset(request).using(self.using)
class ViewerProfile(models.Model):
name = models.CharField(max_length=32)
count = models.IntegerField(blank=True,null=True)
objects = models.Manager()
profiles = ViewerProfileManager()
self._db is going to be affected by the name of your database.

Django: making relationships in memory without saving to DB

I have some models with relationships like this:
class Item(model.Model):
name = models.CharField()
class Group(models.Model):
item = models.ManyToManyField(Item)
class Serie(models.Model):
name = models.CharField()
chart = models.ForeignKey(Chart)
group = models.ForeignKey(Group)
class Chart(models.Model):
name = models.CharField()
I need to create a Chart object on the fly, without saving to the DB. But I can't do it because Django tries to use the objects primary keys when assigning the relationships.
I just want Group.add(Item()) to work without having to save the objects to the DB.
Is there any simple way around this?
Reviving here for the sake of future readers:
I've gotten around this use case by defining a private attribute that represents the relationship inside the classes and a property to inspect wether the object can be retrieved from the DB or resides in memory.
Here is a simple example:
class Parent(models.Model):
_children = []
name = models.CharField(max_length=100)
#property
def children(self):
if _children:
return self._children
else:
return self.children_set.all()
def set_virtual_children(self, value): # could use a setter for children
self._children = value # Expose _children to modification
def some_on_the_fly_operation(self):
print(','.join([c.name for c in self.children]))
class Children(models.Model):
parent = models.ForeignKey(Parent)
name = models.CharField(max_length=100)
This way, I can set the "virtual children" and use all the defined methods "on the fly"
EDIT: It seems that approach described here isn't enough for django to allow adding to the ManyToMany relationship.
Have you tried to add primary_key=True and unique=True to the name attribute of the Item model. Then doing Group.add(Item("item_name_here")) should work if you have the possibility to create the name on the fly.
I didn't test it, but I think your way failed because add() wants to use the primary-key which by default is the autoincrementing id that is assigned when it is saved to the database.