Django __in, но вернуть первый соответствующий элемент - PullRequest
1 голос
/ 13 марта 2020

У меня есть эта модель

from django.db import models

class TranslatedString(models.Model):
    lang = models.CharField()
    key = models.CharField()
    value = models.CharField()

У меня есть следующие экземпляры этой модели:

a = TranslatedString(lang="en_US", key="my-string", value="hello world")
b = TranslatedString(lang="en_AU", key="my-string", value="g'day world")
c = TranslatedString(lang="ja_JP", key="my-string", value="こんにちは世界")

И у меня есть этот список языков, которые хочет пользователь

preferred_langs = ["en_CA", "en_US", "en_AU", "fr_CA"]

, который упорядочен по предпочтению. Я хотел бы вернуть значение, соответствующее первому элементу в этом списке. Даже если a и b соответствуют запросу типа

TranslatedString.objects.filter(key="my-string", lang__in=preferred_langs).first()

, я хочу, чтобы он был упорядочен по списку, чтобы я всегда получал a.

I Можно сделать запрос для каждого элемента в preferred_langs и вернуть, как только я найду подходящее значение, но есть ли лучший вариант? Я хотел бы сделать это одним запросом.

Ответы [ 2 ]

1 голос
/ 13 марта 2020

Вы можете использовать выражение генератора над preferred_langs, чтобы произвести сопоставление предпочтительных языков с их соответствующими индексами в списке как When объекты для объекта Case, который будет аннотирован как поле, чтобы вы могли упорядочить отфильтрованный результат по нему:

from django.db.models import Case, Value, When

TranslatedString.objects.filter(key="my-string", lang__in=preferred_langs).annotate(
    preference=Case(*(When(lang=lang, then=Value(i)) for i, lang in preferred_langs))
).order_by('preference').first()
0 голосов
/ 13 марта 2020

Если вы не возражаете извлечь все предпочтительные переводы из базы данных, это можно сделать кратко, отсортировав модели в Python:

preferred_langs = ["en_CA", "en_US", "en_AU", "fr_CA"]
strings = list(TranslatedString.objects.filter(key="my-string", lang__in=preferred_langs))

strings.sort(key=lambda s: preferred_langs.index(s))
first_choice = strings[0]

print(first_choice.lang)  # outputs "en_US"

Это выполнит один (но потенциально большой) запрос. Однако если последовательность предпочтительных языков довольно короткая, сортировка должна произойти за незначительное время.

...