django-modeltranslation: Django 1.10 default manager not MultilingualManager for model inheriting abstract class with a manager defined on objects (leading to AttributeError: 'QuerySet' object has no attribute 'rewrite')

Whilst upgrading my application to Django 1.10 I came across the following issue where the default manager for a Model Translation registered model didn’t get dynamically set to modeltranslation.manager.MultilingualManager.

This resulted in an exception being thrown in update_translation_fields.py L32

AttributeError: 'QuerySet' object has no attribute 'rewrite'

A similar issue was resolved in #123 but it seems something in the internal changes in Django 1.10 has resurfaced it in the particular scenario I have (not one covered by #381).

I haven’t yet identified what the changes are that cause it but I’ve narrowed the scenario down to one in which a model inherits from an abstract model that explicitly sets a manager via the objects attribute. In this case the resulting _default_manager is that set on objects rather than modeltranslation.manager.MultilingualManager.

The simplest example I can boil it down to is this:

# models.py

class FooAbstract(models.Model):
    class Meta:
        abstract = True

class Foo(FooAbstract):
    name = models.CharField(blank=True, max_length=1)

class Bar(models.Model):
    objects = models.Manager()
    name = models.CharField(blank=True, max_length=1)

class BazAbstract(models.Model):
    objects = models.Manager()
    class Meta:
        abstract = True

class Baz(BazAbstract):
    name = models.CharField(blank=True, max_length=1)


# translations.py

@register(Foo)
class FooTranslationOptions(TranslationOptions):
    fields = ('name',)

@register(Bar)
class BarTranslationOptions(TranslationOptions):
    fields = ('name',)

@register(Baz)
class BazTranslationOptions(TranslationOptions):
    fields = ('name',)


# shell

from modeltranslation.translator import translator
models = translator.get_registered_models(abstract=False)
for model in models:
    print(model.__name__, type(model._default_manager))

# Django 1.9 output:

Foo <class 'modeltranslation.manager.MultilingualManager'>
Bar <class 'modeltranslation.manager.MultilingualManager'>
Baz <class 'modeltranslation.manager.MultilingualManager'>

# Django 1.10 output:

Foo <class 'modeltranslation.manager.MultilingualManager'>
Bar <class 'modeltranslation.manager.MultilingualManager'>
Baz <class 'django.db.models.manager.Manager'>

I don’t have a solution yet but thought I’d submit it as an issue now in case anyone has any ideas?

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 3
  • Comments: 24

Commits related to this issue

Most upvoted comments

Now we have django 1.11 and this bug isn’t solved yet.

I need a moment of free time to take a deep look into the issue; probably will take care of it at the incoming weekend.

Please tell us when this is released! 😃

I think that documenting the add of the manager used in the subclass could be a good workaround

@Schnouki @deschler Brilliant, thank you!

A workaround that seems to work for me (without detailed testing) is to redeclare objects on the subclass. This then results in the subclass’s default manager being MultilingualManager.

E.g., using my examples from the first comment of this issue:

class BazAbstract(models.Model):
    objects = models.Manager()
    class Meta:
        abstract = True

class Baz(BazAbstract):
    objects = models.Manager()  # <- This has been added
    name = models.CharField(blank=True, max_length=1)

# Django 1.10 output:
Baz <class 'django.db.models.manager.MultilingualManager'>

(I’m redeclaring objects on Baz rather than just moving it off BazAbstract to Baz because in my real models the abstract class is being used by other non-modeltranslation enabled classes and I don’t want to touch them (and, in general, one might not be able to change the abstract class anyway).)

Fingers crossed for a fix, Model Translation is so useful!

@Ge0 I did wonder whether that and inheritance changes documented in the 1.10 release notes were the source of the change, but they are deprecations in 1.10 so the pre-1.10 behaviour should still remain:

During the deprecation period, use_for_related_fields will be honored and raise a warning, even if a base_manager_name is set. This allows third-party code to preserve legacy behavior while transitioning to the new API.