django-modeltrans icon indicating copy to clipboard operation
django-modeltrans copied to clipboard

non-lazyness of the <field>_i18n lookup transformation

Open jieter opened this issue 7 years ago • 2 comments

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'

jieter avatar Jan 25 '18 10:01 jieter

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 avatar Feb 28 '18 19:02 pjburon

@pjburon thanks for the extra info, I'll have a look next week.

jieter avatar Mar 09 '18 20:03 jieter