我有一些可翻译的模型。
我们实现的方式是:对于每个可翻译模型,都有一个 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"),
)
但它会产生完全相同的错误。
知道如何从自定义查询集中返回默认值或翻译值吗?
您不能使用已存在的字段名称,这也可能导致大量名称冲突,以及对于运行时环境来说并不含糊的行为,但对程序员来说非常复杂。
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']