Todor
Todor

Reputation: 16010

Django Migrations ValueError: Could not find manager in django.db.models.manager

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

Answers (4)

Federico B.
Federico B.

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

Todor
Todor

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

Craig Labenz
Craig Labenz

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

Darian Moody
Darian Moody

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

Related Questions