If model has a customized manager, how can I use default? - python

If model defined with custom manager, but I need to query database with default Django manager django.db.models.manager.Manager - how can I do that?
UPDATE
Main issue is that I don't have access to the code, and the model does not have standard manager available, only customized one.

Keep your custom managers after the default manager. Like this:
# 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.
You are free to use both Book.objects.<...> and Book.dahl_objects.<...> for filtering. However, there are a few things to note with managers, esp. default ones. From managers documentation:
If you use custom Manager objects, take note that the first Manager Django encounters (in the order in which they’re defined in the model) has a special status. Django interprets the first Manager defined in a class as the “default” Manager, and several parts of Django (including dumpdata) will use that Manager exclusively for that model. As a result, it’s a good idea to be careful in your choice of default manager in order to avoid a situation where overriding get_queryset() results in an inability to retrieve objects you’d like to work with.
A reminder which has caused me headaches in the past:
Do not filter away any results in this type of manager subclass
One reason an automatic manager is used is to access objects that are related to from some other model. In those situations, Django has to be able to see all the objects for the model it is fetching, so that anything which is referred to can be retrieved.

Related

Should my soft-deletion-honouring model manager be my model's default manager?

I am building soft-deletion functionality for my Django project. I have implemented this using a custom model manager (i.e. performing an initial filter on get_queryset(), plus overriding Model / Manager / QuerySet delete().
Django documentation (1.11):
If you use custom Manager objects, take note that the first Manager Django encounters (in the order in which they’re defined in the model) has a special status. Django interprets the first Manager defined in a class as the “default” Manager, and several parts of Django (including dumpdata) will use that Manager exclusively for that model. As a result, it’s a good idea to be careful in your choice of default manager in order to avoid a situation where overriding get_queryset() results in an inability to retrieve objects you’d like to work with.
My soft delete-honouring manager is currently my model's default manager (first manager declared on the model class). It's also assigned to objects.
This is convenient for me as a lot of Django code uses the default model manager (e.g. MultipleObjectMixin.get_queryset() if your MultipleObjectMixin-inheriting View just has the model attribute defined).
However the fact that dumpdata also uses the custom model manager has scared me off, and has me thinking about other unknown unintended consequences of setting the default model manager. In the event that I perform a manage.py dumpdata, I'd want my soft-deleted models to be contained in the dump. So I am beginning to doubt myself regarding my choice in overriding the default model manager to filter down the available records.
At the same time, I appreciate the convenience that setting the default model manager is giving me (zero-effort support for generic CBVs.etc), which I want to maintain if possible.
What's the best way to approach this?
As per this documentation, if you run ./manage.py dumpdata -a or ./manage.py dumpdata --all, then it will dump data using default manager instead of custom manager. If you want to use your default manager instead of custom manager(without changing in models) then you can try like this:
objects = YourModel._base_manager
objects.all()

ModelManager used in ForeignKey relations

I'm using Django 1.9 on my site and I need an effective way of completely ignore the inactive users, so I don't send them any notifications, emails, etc.
I've tried using a custom Model Manager that only returns the active ones, like this:
class ActiveAccountsManager(models.Manager):
use_for_related_fields = True
def get_queryset(self):
return super(ActiveAccountsManager, self).get_queryset().filter(user__is_active=True)
class Account(models.Model):
class Meta:
verbose_name = _('Account')
verbose_name_plural = _('Accounts')
objects = ActiveAccountsManager()
all_accounts = models.Manager() # Enabling the obtention of all the users, instead of only the active ones
user = models.OneToOneField(User)
type = models.IntegerField(choices=ACCOUNT_TYPES, default=-1)
And, while it works exactly as I want when I directly try to query Account objects, it doesn't when they are referenced through an object that has a ForeignKey relation with it. For example, if I had a Comment model like the following one:
class Comment(models.Model):
author = models.ForeignKey(Account)
I'd like that, when I query for Comment objects, the ones whose author is a inactive user (i.e. an user that the default Manager of the Account model won't return) aren't returned either, instead of the current behavior that returns the comment but says that the account related to it does not exist
Is there any way of achieving this without specifically defining a custom ModelManager on the Comment class? Because the thing is that I want to achieve this in a lot of classes related to the Account one and I can't think of a way of doing it without being extremely repetitive.
Please, don't hesitate to ask if any part of my question isn't clear enough or if I need to provide more details about my code. Thank you so much in advance.
It should already work in your case, as the docs (https://docs.djangoproject.com/en/1.9/topics/db/managers/#default-managers) say:
Default managers
If you use custom Manager objects, take note that the first Manager
Django encounters (in the order in which they’re defined in the model)
has a special status. Django interprets the first Manager defined in a
class as the “default” Manager, and several parts of Django (including
dumpdata) will use that Manager exclusively for that model. As a
result, it’s a good idea to be careful in your choice of default
manager in order to avoid a situation where overriding get_queryset()
results in an inability to retrieve objects you’d like to work with.
Since the ActiveAccountsManager declaration comes first in your model, it should be already marked as the "default" one.
If you were to update to Django >= 1.10, you can set up explicitly the default manager for your model (see https://docs.djangoproject.com/en/1.11/ref/models/options/#default-manager-name).

Why use custom Querysets instead of custom model Managers?

It seems like much of what can be done by a custom Queryset can be accomplished by a model Manager. So why use custom Querysets instead of model Managers?
Custom querysets allow for chaining methods, while custom managers only let you access defined methods directly from the manager. If you need to expose methods from the manager and the queryset, you can use Queryset.as_manager.
Take a look at: https://docs.djangoproject.com/en/1.9/topics/db/managers/#create-manager-with-queryset-methods

In Django, ok to set Model.objects to another manager outside of model definition?

Suppose, in Django 1.6, you have the following model code:
class FooManager(models.Manager):
def get_queryset():
return ... # i.e. return a custom queryset
class Foo(models.Model):
foo_manager = FooManager()
If, outside the Foo model definition (e.g. in view code or in the shell), you do:
Foo.objects = FooManager()
Foo.objects.all()
you'll get an exception in the Django internal code on Foo.objects.all() due to a variable named lookup_model being `None'.
However, if you instead do:
Foo.objects = Foo.foo_manager
Foo.objects.all()
The Foo.objects.all() will work as expected, i.e. as if objects had been defined to be FooManager() in the model definition in the first place.
I believe this behavior is due to Django working its "magic" in creating managers during model definition (just as it works magic in creating model fields).
My question: is there any reason NOT to assign objects to an alternate manager in this way outside of the model definition? It seems to work fine, but I don't fully understand the internals so want to make sure.
In case you are wondering, the context is that I have a large code base with many typical references to objects. I want to have this code base work on different databases dynamically, i.e. based on a request URL parameter. My plan is to use middleware that sets objects for all relevant models to managers that point to the appropriate database. The rest of the app code would then go on its merry way, using objects without ever having to know anything has changed.
The trouble is that this is not at all thread safe. Doing this will change the definition for all requests being served by that process, until something else changes it again. That is very likely to have all sorts of unexpected effects.

Django get_query_set override is being cached

I'm overriding Django's get_query_set function on one of my models dynamically. I'm doing this to forcibly filter the original query set returned by Model.objects.all/filter/get by a "scenario" value, using a decorator. Here's the decorator's function:
# Get the base QuerySet for these models before we modify their
# QuerySet managers. This prevents infinite recursion since the
# get_query_set function doesn't rely on itself to get this base QuerySet.
all_income_objects = Income.objects.all()
# Figure out what scenario the user is using.
current_scenario = Scenario.objects.get(user=request.user, selected=True)
# Modify the imported income class to filter based on the current scenario.
Expense.objects.get_query_set = lambda: all_expense_objects.filter(scenario=current_scenario)
# Call the method that was initially supposed to
# be executed before we were so rudely interrupted.
return view(request, **arguments)
I'm doing this to DRY up the code, so that all of my queries aren't littered with an additional filter. However, if the scenario changes, no objects are being returned. If I kill all of my python processes on my server, the objects for the newly select scenario appear. I'm thinking that it's caching the modified class, and then when the scenario changes, it's applying another filter that will never make sense, since objects can only have one scenario at a time.
This hasn't been an issue with user-based filters because the user never changes for my session. Is passenger doing something stupid to hold onto class objects between requests? Should I be bailing on this weird design pattern and just implement these filters on a per-view basis? There must be a best practice for DRYing filters up that apply across many views based on something dynamic, like the current user.
What about creating a Manager object for the model which takes the user as an argument where this filtering is done. My understanding of being DRY w/ Django querysets is to use a Model Manager
#### view code:
def some_view(request):
expenses = Expense.objects.filter_by_cur_scenario(request.user)
# add additional filters here, or add to manager via more params
expenses = expenses.filter(something_else=True)
#### models code:
class ExpenseManager(models.Manager):
def filter_by_cur_scenario(self, user):
current_scenario = Scenario.objects.get(user=request.user, selected=True)
return self.filter(scenario=current_scenario)
class Expense(models.Model):
objects = ExpenseManager()
Also, one quick caveat on the manager (which may apply to overriding get_query_set): foreign relationships will not take into account any filtering done at this level. For example, you override the MyObject.objects.filter() method to always filter out deleted rows; A model w/ a foreignkey to that won't use that filter function (at least from what I understand -- someone please correct me if I'm wrong).
I was hoping to have this implementation happen without having to code anything in other views. Essentially, after the class is imported, I want to modify it so that no matter where it's referenced using Expense.objects.get/filter/all it's already been filtered. As a result, there is no implementation required for any of the other views; it's completely transparent. And, even in cases where I'm using it as a ForeignKey, when an object is retrieved using the aforementioned Expense.objects.get/filter/all, they'll be filtered as well.

Categories