Skip to main content
 首页 » 编程设计

python之Django Rest 框架和 Django-Hvad

2024年10月25日36langtianya

所以我需要为我的 DRF API 提供一些模型翻译支持,我开始使用 django-hvad。

它似乎适用于我的 Django 应用程序,但我在使用 DRF APi 时遇到了一些问题。

我正在尝试创建一个简单的 POST 请求,但出现错误:

访问翻译的字段需要实例加载翻译,或者可以从数据库加载当前语言 (en) 的有效翻译

这是我的模型、序列化器和 View 集:

型号:

class Mission(TranslatableModel): 
    translations = TranslatedFields( 
        mission=models.CharField(max_length=255, help_text="Mission name"), 
    ) 
 
    def __unicode__(self): 
        return self.lazy_translation_getter('mission', str(self.pk)) 

序列化器:

class MissionSerializer(serializers.ModelSerializer): 
    mission = serializers.CharField(source='mission') 
 
    class Meta: 
        model = Mission 

View 集:

class MissionViewSet(viewsets.ModelViewSet): 
    queryset = Mission.objects.language().all() 
    serializer_class = MissionSerializer 
    authentication_classes = (NoAuthentication,) 
    permission_classes = (AllowAny,) 
 
    def get_queryset(self): 
        # Set Language For Translations 
        user_language = self.request.GET.get('language') 
        if user_language: 
            translation.activate(user_language) 
        return Mission.objects.language().all() 

有谁知道我该如何解决这个问题??我也对其他已知可用的推荐应用程序持开放态度,但我真的很想让这个应用程序运行

请您参考如下方法:

多亏了这里的 Spectras https://github.com/KristianOellegaard/django-hvad/issues/211,我才让它工作

我猜这个问题是 DRF 试图对模型进行一些内省(introspection)。我确实在我的一个项目中使用 DRF,在 TranslatableModel 上。它需要一些胶水才能正常工作。我曾经建议将其添加到 hvad,但我们得出的结论是,这会过度扩展功能集。也许某天另一个模块,但我没有足够的时间来维护 hvad 和那个。

我实现它已经有一段时间了,所以这里是:

# hvad compatibility for rest_framework - JHA 
 
class TranslatableModelSerializerOptions(serializers.ModelSerializerOptions): 
    def __init__(self, meta): 
        super(TranslatableModelSerializerOptions, self).__init__(meta) 
        # We need this ugly hack as ModelSerializer hardcodes a read_only_fields check 
        self.translated_read_only_fields = getattr(meta, 'translated_read_only_fields', ()) 
        self.translated_write_only_fields = getattr(meta, 'translated_write_only_fields', ()) 
 
class HyperlinkedTranslatableModelSerializerOptions(serializers.HyperlinkedModelSerializerOptions): 
    def __init__(self, meta): 
        super(HyperlinkedTranslatableModelSerializerOptions, self).__init__(meta) 
        # We need this ugly hack as ModelSerializer hardcodes a read_only_fields check 
        self.translated_read_only_fields = getattr(meta, 'translated_read_only_fields', ()) 
        self.translated_write_only_fields = getattr(meta, 'translated_write_only_fields', ()) 
 
class TranslatableModelMixin(object): 
    def get_default_fields(self): 
        fields = super(TranslatableModelMixin, self).get_default_fields() 
        fields.update(self._get_translated_fields()) 
        return fields 
 
    def _get_translated_fields(self): 
        ret = OrderedDict() 
        trans_model = self.opts.model._meta.translations_model 
        opts = trans_model._meta 
 
        forward_rels = [field for field in opts.fields 
                        if field.serialize and not field.name in ('id', 'master')] 
 
        for trans_field in forward_rels: 
            if trans_field.rel: 
                raise RuntimeError() 
            field = self.get_field(trans_field) 
            if field: 
                ret[trans_field.name] = field 
 
        for field_name in self.opts.translated_read_only_fields: 
            assert field_name in ret 
            ret[field_name].read_only = True 
 
        for field_name in self.opts.translated_write_only_fields: 
            assert field_name in ret 
            ret[field_name].write_only = True 
 
        return ret 
 
    def restore_object(self, attrs, instance=None): 
        new_attrs = attrs.copy() 
        lang = attrs['language_code'] 
        del new_attrs['language_code'] 
 
        if instance is None: 
            # create an empty instance, pre-translated 
            instance = self.opts.model() 
            instance.translate(lang) 
        else: 
            # check we are updating the correct translation 
            tcache = self.opts.model._meta.translations_cache 
            translation = getattr(instance, tcache, None) 
            if not translation or translation.language_code != lang: 
                # nope, get the translation we are updating, or create it if needed 
                try: 
                    translation = instance.translations.get_language(lang) 
                except instance.translations.model.DoesNotExist: 
                    instance.translate(lang) 
                else: 
                    setattr(instance, tcache, translation) 
 
        return super(TranslatableModelMixin, self).restore_object(new_attrs, instance) 
 
class TranslatableModelSerializer(TranslatableModelMixin, serializers.ModelSerializer): 
    _options_class = TranslatableModelSerializerOptions 
 
class HyperlinkedTranslatableModelSerializer(TranslatableModelMixin, 
                                             serializers.HyperlinkedModelSerializer): 
    _options_class = HyperlinkedTranslatableModelSerializerOptions 

从那里,您只需从 TranslatableModelSerializer 或 HyperlinkedTranslatableModelSerializer 继承序列化程序。发布时,您应该简单地将 language_code 添加为普通字段作为 JSON/XML/其他内容的一部分。

主要技巧在于 restore_object 方法。对象创建需要包括翻译加载。