【问题标题】:Django Rest Framework and Django-HvadDjango Rest 框架和 Django-Hvad
【发布时间】:2015-01-04 05:43:01
【问题描述】:

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

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

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

Accessing a translated field requires that the instance has a translation loaded, or a valid translation in current language (en) loadable from the database

这是我的模型、序列化程序和视图集:

型号:

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

视图集:

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()

有谁知道我该如何解决这个问题?我也对其他已知可以工作的建议应用开放,但我真的很想让这个工作

【问题讨论】:

    标签: python django django-rest-framework django-modeltranslation django-hvad


    【解决方案1】:

    感谢这里的 Spectras https://github.com/KristianOellegaard/django-hvad/issues/211

    我想问题是 DRF 试图对模型进行一些自省。我确实在我的一个项目中使用 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
    

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

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

    【讨论】:

    • 顺便说一句,如果有人遇到这个,对 REST framework 3.1 的完全支持已经直接包含在 django-hvad 中了。
    • 感谢@spectras 的评论。
    猜你喜欢
    • 2015-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-21
    • 2015-04-26
    相关资源
    最近更新 更多