django-modeltrans
django-modeltrans copied to clipboard
non-lazyness of the <field>_i18n lookup transformation
Lookups are tranformed when the queryset definition is executed, which might be unexpected behavior with querysets.
An problematic use case might be defining the choices for a ModelChoiceField
.
from django.utils.translation import override
with override('en'):
qs = Blog.objects.filter(title_i18n='foo')
print(str(qs.query))
# SELECT "app_blog"."id", "app_blog"."title", "app_blog"."body", "app_blog"."category_id", "app_blog"."i18n"
# FROM "app_blog"
# WHERE "app_blog"."title" = 'foo'
with override('nl'):
print(str(qs.query))
# same query
with override('nl'):
qs = Blog.objects.filter(title_i18n='foo')
print(str(qs.query))
# SELECT "app_blog"."id", "app_blog"."title", "app_blog"."body", "app_blog"."category_id", "app_blog"."i18n", COALESCE(("app_blog"."i18n" ->> 'title_nl'), "app_blog"."title") AS "title_i18n_annotation"
# FROM "app_blog"
# WHERE COALESCE(("app_blog"."i18n" ->> 'title_nl'), "app_blog"."title") = 'foo'
Using a callable helps, but I'm not sure if the queryset
argument of ModelChoiceField supports that:
qs = lambda: Blog.objects.filter(title_i18n='foo')
with override('nl'):
print(str(qs().query))
# SELECT "app_blog"."id", "app_blog"."title", "app_blog"."body", "app_blog"."category_id", "app_blog"."i18n", COALESCE(("app_blog"."i18n" ->> 'title_nl'), "app_blog"."title") AS "title_i18n_annotation"
# FROM "app_blog"
# WHERE COALESCE(("app_blog"."i18n" ->> 'title_nl'), "app_blog"."title") = 'foo'
with override('en'):
print(str(qs().query))
# SELECT "app_blog"."id", "app_blog"."title", "app_blog"."body", "app_blog"."category_id", "app_blog"."i18n"
# FROM "app_blog"
# WHERE "app_blog"."title" = 'foo'
Hello,
I've noticed also a strange behavior using django.db.models.Q filters, I'm not sure if it's due to the same issue or not:
products = models.Product.objects.filter(
Q(category__slug_i18n=category_slug) | Q(category__parent__slug_i18n=category_slug),
is_active=True
)
Result KO
But different queries without OR operator work fine:
products = models.Product.objects.filter(Q(category__slug_i18n=category_slug) , is_active=True)
Result OK.
products = models.Product.objects.filter(Q(category__parent__slug_i18n=category_slug), is_active=True)
Result OK
So alternative that works:
products = models.Product.objects.filter(category__slug_i18n=category_slug, is_active=True) |
models.Product.objects.filter(category__parent__slug_i18n=category_slug, is_active=True)
Result OK
@pjburon thanks for the extra info, I'll have a look next week.