mirror of
https://github.com/Hopiu/django-modeltranslation.git
synced 2026-05-09 22:04:48 +00:00
Merge pull request #431 from Schnouki/fix-m2m-and-inherited-managers
Fix M2M and inherited managers
This commit is contained in:
commit
0c50dffefe
4 changed files with 118 additions and 5 deletions
|
|
@ -351,3 +351,61 @@ class MultitableConflictModelA(models.Model):
|
|||
|
||||
class MultitableConflictModelB(MultitableConflictModelA):
|
||||
title = models.CharField(ugettext_lazy('title'), max_length=255)
|
||||
|
||||
|
||||
# ######### Complex M2M with abstract classes and custom managers
|
||||
|
||||
class CustomQuerySetX(models.query.QuerySet):
|
||||
pass
|
||||
|
||||
|
||||
class CustomManagerX(models.Manager):
|
||||
def get_queryset(self):
|
||||
return CustomQuerySetX(self.model, using=self._db)
|
||||
get_query_set = get_queryset
|
||||
|
||||
|
||||
class AbstractBaseModelX(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
objects = CustomManagerX()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class AbstractModelX(AbstractBaseModelX):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class ModelX(AbstractModelX):
|
||||
pass
|
||||
|
||||
|
||||
class AbstractModelXY(models.Model):
|
||||
model_x = models.ForeignKey('ModelX')
|
||||
model_y = models.ForeignKey('ModelY')
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class ModelXY(AbstractModelXY):
|
||||
pass
|
||||
|
||||
|
||||
class CustomManagerY(models.Manager):
|
||||
pass
|
||||
|
||||
|
||||
class AbstractModelY(models.Model):
|
||||
title = models.CharField(max_length=255)
|
||||
xs = models.ManyToManyField('ModelX', through='ModelXY')
|
||||
objects = CustomManagerY()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class ModelY(AbstractModelY):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ models = translation = None
|
|||
request = None
|
||||
|
||||
# How many models are registered for tests.
|
||||
TEST_MODELS = 29 + (1 if MIGRATIONS else 0)
|
||||
TEST_MODELS = 31 + (1 if MIGRATIONS else 0)
|
||||
|
||||
|
||||
class reload_override_settings(override_settings):
|
||||
|
|
@ -3090,3 +3090,31 @@ class TestRequired(ModeltranslationTestBase):
|
|||
self.assertEqual(set(('req_reg_en', 'req_en_reg', 'req_en_reg_en')), error_fields)
|
||||
else:
|
||||
self.fail('ValidationError not raised!')
|
||||
|
||||
|
||||
class M2MTest(ModeltranslationTestBase):
|
||||
def test_m2m(self):
|
||||
# Create 1 instance of Y, linked to 2 instance of X, with different
|
||||
# English and German names.
|
||||
x1 = models.ModelX.objects.create(name_en="foo", name_de="bar")
|
||||
x2 = models.ModelX.objects.create(name_en="bar", name_de="baz")
|
||||
y = models.ModelY.objects.create(title='y1')
|
||||
models.ModelXY.objects.create(model_x=x1, model_y=y)
|
||||
models.ModelXY.objects.create(model_x=x2, model_y=y)
|
||||
|
||||
with override("en"):
|
||||
# There's 1 X named "foo" and it's x1
|
||||
y_foo = models.ModelY.objects.filter(xs__name="foo")
|
||||
self.assertEqual(1, y_foo.count())
|
||||
|
||||
# There's 1 X named "bar" and it's x2 (in English)
|
||||
y_bar = models.ModelY.objects.filter(xs__name="bar")
|
||||
self.assertEqual(1, y_bar.count())
|
||||
|
||||
# But in English, there's no X named "baz"
|
||||
y_baz = models.ModelY.objects.filter(xs__name="baz")
|
||||
self.assertEqual(0, y_baz.count())
|
||||
|
||||
# Again: 1 X named "bar" (but through the M2M field)
|
||||
x_bar = y.xs.filter(name="bar")
|
||||
self.assertIn(x2, x_bar)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from modeltranslation.tests.models import (
|
|||
RichText, RichTextPage, MultitableModelA, MultitableModelB, MultitableModelC, ManagerTestModel,
|
||||
CustomManagerTestModel, CustomManager2TestModel, GroupFieldsetsModel, NameModel,
|
||||
ThirdPartyRegisteredModel, ProxyTestModel, UniqueNullableModel, OneToOneFieldModel,
|
||||
RequiredModel, DecoratedModel)
|
||||
RequiredModel, DecoratedModel, ModelX, ModelY)
|
||||
|
||||
|
||||
class TestTranslationOptions(TranslationOptions):
|
||||
|
|
@ -207,6 +207,18 @@ class DecoratedTranslationOptions(TranslationOptions):
|
|||
fields = ('title',)
|
||||
|
||||
|
||||
# ######### Complex M2M with abstract classes and custom managers
|
||||
|
||||
class ModelXOptions(TranslationOptions):
|
||||
fields = ('name',)
|
||||
translator.register(ModelX, ModelXOptions)
|
||||
|
||||
|
||||
class ModelYOptions(TranslationOptions):
|
||||
fields = ('title',)
|
||||
translator.register(ModelY, ModelYOptions)
|
||||
|
||||
|
||||
# ######### 3-rd party with custom manager
|
||||
|
||||
if VERSION >= (1, 8) and "django.contrib.auth" in settings.INSTALLED_APPS:
|
||||
|
|
|
|||
|
|
@ -214,9 +214,24 @@ def add_manager(model):
|
|||
|
||||
manager.__class__ = NewMultilingualManager
|
||||
|
||||
managers = (model._meta.local_managers if NEW_MANAGER_API else
|
||||
(getattr(model, x[1]) for x in
|
||||
model._meta.concrete_managers + model._meta.abstract_managers))
|
||||
if NEW_MANAGER_API:
|
||||
# Inspired by django.db.models.options.Options.managers (find all
|
||||
# managers by following the normal Python MRO rules), but keeps the
|
||||
# original managers instead of making copies.
|
||||
managers = []
|
||||
seen = set()
|
||||
bases = (b for b in model.mro() if hasattr(b, '_meta'))
|
||||
for base in bases:
|
||||
for manager in base._meta.local_managers:
|
||||
if manager.name in seen:
|
||||
continue
|
||||
managers.append(manager)
|
||||
seen.add(manager.name)
|
||||
|
||||
else:
|
||||
managers = ((getattr(model, x[1]) for x in
|
||||
model._meta.concrete_managers + model._meta.abstract_managers))
|
||||
|
||||
for current_manager in managers:
|
||||
prev_class = current_manager.__class__
|
||||
patch_manager_class(current_manager)
|
||||
|
|
|
|||
Loading…
Reference in a new issue