Reputation: 16010
I'm trying to update from Django 1.7
to Django 1.8
One of my models uses CurrentSiteManager
from django.contrib.sites.managers
like so:
from django.contrib.sites.managers import CurrentSiteManager
class NewsQuerySet(models.QuerySet):
#...
class News(models.Model):
#...
objects = NewsQuerySet.as_manager()
on_site = CurrentSiteManager.from_queryset(NewsQuerySet)()
When I try to run migrations (makemigrations or migrate) I get:
ValueError: Could not find manager CurrentSiteManagerFromNewsQuerySet in django.db.models.manager. Please note that you need to inherit from managers you dynamically generated with 'from_queryset()'.
If I remove the on_site
manager, everything works fine.
Any ideas how to overcome this?
Upvotes: 3
Views: 2543
Reputation: 1051
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.
Upvotes: 1
Reputation: 16010
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()
Upvotes: 3
Reputation: 2555
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.
Upvotes: 3
Reputation: 4074
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()
Upvotes: 1