3👍
Turns out, since Django 1.8 we can serialize Managers
using use_in_migrations
.
And the CurrentSiteManager
is marked with use_in_migrations = True
So the fix is to set back use_in_migrations = False
. I did it this way:
class NewsSiteManager(CurrentSiteManager.from_queryset(NewsQuerySet)):
use_in_migrations = False
class News(models.Model):
#...
objects = NewsQuerySet.as_manager()
on_site = NewsSiteManager()
3👍
There’s a better way to do this.
from django.db import models
class NewsManager(models.Manager.from_queryset(NewsQuerySet)):
use_in_migrations = True
class News(models.Model):
...
objects = NewsManager()
Then you can do whatever additional things you want with CurrentSiteManager
objects.
- [Django]-Testing django: reason for unexpected http status code
- [Django]-How to handle an exception in Django moving from get() to filter()
1👍
The accepted answer works, but it’s not so great if you actually want to keep the managers serialised as part of the migrations (so you can use them!).
To do that, you need to follow what the error message says and inherit from the generated manager, and then use your subclass:
from django.contrib.sites.managers import CurrentSiteManager as DjangoCurrentSiteManager
class NewsQuerySet(models.QuerySet):
pass
class CurrentSiteManager(DjangoCurrentSiteManager.from_queryset(NewsQuerySet)):
pass
class News(models.Model):
# Fields...
objects = NewsQuerySet.as_manager()
on_site = CurrentSiteManager()
0👍
If you are using mypy
the accepted solution will result in the following typing error:
Unsupported dynamic base class "CurrentSiteManager.from_queryset"
I noticed that what Django is really looking for here is an importable path to your custom manager, which it reads from the class __module__
and __name__
dunder attributes.
When you create a custom manager class with BaseManager.from_queryset
these attributes point to a dynamic class name in the django.db.models.manager
module, which is not importable. You can fix it by assigning them to the appropriate location to your custom manager class:
NewsSiteManager = CurrentSiteManager.from_queryset(NewsQuerySet)()
NewsSiteManager.__module__ = __name__
NewsSiteManager.__name__ = "NewsSiteManager"
The resulting migration operation will then look something like this:
migrations.AlterModelManagers(
name="news",
managers=[
("objects", news.models.NewsSiteManager()),
],
)
It looks a bit ugly at first but it’s actually more correct than the values that these variables have by default.