Django自定义模型查询集动态返回字段

问题描述 投票:0回答:1

我有一些可翻译的模型。

我们实现的方式是:对于每个可翻译模型,都有一个 ModelTranslation 模型。
例如。对于

Car
模型,存在一个 FK 为
CarTranslation
Car
模型。

翻译模型还具有字段:语言以及原始模型的任何文本/字符字段。
例如。如果

Car
模型看起来像这样

class Car(models.Model):
  name = models.CharField()
  description = models.TextField()

CarTranslation
然后看起来像这样:

class CarTranslation(models.Model):
  car = models.ForeignKey(to=Car)
  language = models.CharField(choices=LANGUAGE_CHOICES)
  name = models.CharField()
  description = models.TextField()

在返回可翻译数据的视图中,我们检查用户定义的语言:如果是英语(默认),我们按原样返回

Car
;如果不是英语,我们会在
CarTranslation
中返回用户语言的翻译数据(如果存在,否则默认为英语)。

我们有一个旧的实现,我想改进:现在我们在序列化器上下文中添加翻译,然后在序列化器中,我们检查是否有翻译,否则返回默认值。

但我不喜欢这个。

我认为更好的解决方案是使用自定义模型管理器/查询集。
我正在尝试这个:

from django.db.models.functions import Coalesce
from django.db.models import F, Subquery

class CarQuerySet(models.QuerySet):
  def with_translation(self, language):
    from cars.models import CarTranslation

    if language == "en":
      return self
    translation_query = CarTranslation.objects.filter(language=language, car=OuterRef("pk"))
    return self.annotate(
      name=Coalesce(Subquery(translation_query.values("name")[:1]), F("name")),
      description=Coalesce(Subquery(translation_query.values("description")[:1]), F("description")),
    )

然后在视图中使用它,如下所示:

class CarView(ListAPIView):
  def get_queryset(self):
    user = self.request.user
    language = user.language
    return Car.objects.with_translation(language)

但它会产生以下错误:

ValueError: The annotation 'name' conflicts with a field on the model.

我还尝试使用不同的名称进行注释,然后不选择冲突的列并返回带注释的值:

from django.db.models.functions import Coalesce
from django.db.models import F, Subquery

class CarQuerySet(models.QuerySet):
  def with_translation(self, language):
    from cars.models import CarTranslation

    if language == "en":
      return self
    translation_query = CarTranslation.objects.filter(language=language, car==OuterRef("pk"))
    queryset = self.annotate(
      translated_name=Coalesce(Subquery(translation_query.values("name")[:1]), F("name")),
      translated_description=Coalesce(Subquery(translation_query.values("description")[:1]), F("description")),
    )

    return queryset.values(
      *[field.name for field in self.model._meta.fields if field.name not in ["name", "description"]]
      name=F("translated_name"),
      description=F("translated_description"),
    )

但它会产生完全相同的错误。

知道如何从自定义查询集中返回默认值或翻译值吗?

django django-models django-rest-framework
1个回答
0
投票

您不能使用已存在的字段名称,这也可能导致大量名称冲突,以及对于运行时环境来说并不含糊的行为,但对程序员来说非常复杂。

from django.db.models import F, Subquery
from django.db.models.functions import Coalesce


class CarQuerySet(models.QuerySet):
    def with_translation(self, language):
        from cars.models import CarTranslation

        if language == 'en':
            return self.annotate(custom_name=F('name'), custom_description=F('description'))
        translation_query = CarTranslation.objects.filter(
            language=language, car_id=OuterRef('pk')
        )
        return self.annotate(
            custom_name=Coalesce(
                Subquery(translation_query.values('name')[:1]), F('name')
            ),
            custom_description=Coalesce(
                Subquery(translation_query.values('description')[:1]),
                F('description'),
            ),
        )

然后在序列化器中,我们可以使用

custom_name
custom_description
:

class CarSerializer(serializers.ModelSerializer):
    name = serializers.CharField(source='custom_name')
    description = serializers.CharField(source='custom_description')

    class Meta:
        model = Car
        fields = ['name', 'description']
© www.soinside.com 2019 - 2024. All rights reserved.