Django Rest框架和Django-Hvad [英] Django Rest Framework and Django-Hvad
问题描述
所以我需要为我的DRF API提供一些模型翻译支持,我开始使用django-hvad。
这似乎对我的django应用程序很好我在DRF APi中遇到一些问题。
我正在尝试创建一个简单的POST请求,我收到一个错误:
访问一个已翻译的字段要求该实例具有翻译加载,或以当前语言(en)从数据库加载的有效翻译
以下是我的型号,序列化器和视图:
型号:
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))
Serializer:
class MissionSerializer(serializers ModelSerializer)
pre>
task = serializers.CharField(source ='mission')
class Meta:
model = Mission
Viewset:
class MissionViewSet(viewsets.ModelViewSet):
queryset = Mission.objects.language()。all()
serializer_class = MissionSerializer
authentication_classes =(NoAuthentication,)
permission_classes =(AllowAny,)
def get_queryset(self):
#设置语言为翻译
user_language = self.request.GET.get('language')
如果user_language:
translation.activate( user_language)
return Mission.objects.language()。all()
有没有人知道我怎么可以绕过这个?我也开放给其他建议的应用程序已知工作,但我真的很想有一个这样的工作
解决方案我得到了工作感谢这里的光谱 https://github.com/KristianOellegaard/django-hvad/issues / 211
这个问题,我想是DRF试图对模型做一些内省。我在我的一个项目中使用DRF,在TranslatableModel上。它需要一些胶水才能正常工作。我曾经建议向hvad补充说,但我们得出结论,这将会过度扩展功能集。也许是另一个模块有一天,但是我没有足够的时间来维护这个hvad。
自从我实现以来已经有一段时间了,所以这里是是:
#hvad兼容性rest_framework - JHA
class TranslatableModelSerializerOptions(serializers.ModelSerializerOptions):
def __init __(self,meta):
super(TranslatableModelSerializerOptions,self).__ init __(meta)
#我们需要这个丑陋的hack作为ModelSerializer hardcodes一个read_only_fields检查
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)
#我们n eed这个丑陋的hack作为ModelSerializer硬编码一个read_only_fields检查
self.translated_read_only_fields = getattr(meta,'converted_read_only_fields',())
self.translated_write_only_fields = getattr(meta,'translations_write_only_fields',())
class TranslatableModelMixin(object):
def get_default_fields(self):
fields = super(TranslatableModelMixin,self).get_default_fields()
fields.update(self._get_translated_fields )
返回字段
def _get_translated_fields(self):
ret = OrderedDict()
trans_model = self.opts.model._meta.translations_model
opts = trans_model._meta
forward_rels = [opts.fields中的字段字段
如果field.serialize而不是field.name('id','master')] $ b $对于forward_rels,trans_field的b
如果trans_field.rel:
raise Runti meError()
field = self.get_field(trans_field)
if field:
ret [trans_field.name] = field
for field_name in self.opts.translated_read_only_fields :
as ret中的field_name
ret [field_name] .read_only = True
在self.opts.translated_write_only_fields中的field_name:
assert中的field_name
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']
如果实例为无:
#创建一个空实例,预翻译
instance = self.opts.model()
instance.translate(lang)
else:
#检查我们正在更新正确的翻译
tcache = self.opts.model._meta.translations_cache
翻译= getattr(实例,tcache,无)
如果没有翻译或翻译.language_code!= lang:
#nope,得到翻译我们正在更新,或创建它,如果需要
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.HyperlinkedMo delSerializer):
_options_class = HyperlinkedTranslatableModelSerializerOptions
从那里,你只是从
TranslatableModelSerializer
或HyperlinkedTranslatableModelSerializer
。在POST时,您应该简单地将language_code作为正常字段添加为JSON / XML /的一部分。
主要技巧在restore_object方法中。对象创建需要包括翻译加载。
so i needed to had some model-translation support for my DRF API and i started using django-hvad.
It seems to work well with my django application but i am getting some issues with the DRF APi.
I am trying to create a simple POST request and i am getting a error:
Accessing a translated field requires that the instance has a translation loaded, or a valid translation in current language (en) loadable from the database
Here are my models, serializers and viewsets:
Model:
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))
Serializer:
class MissionSerializer(serializers.ModelSerializer): mission = serializers.CharField(source='mission') class Meta: model = Mission
Viewset:
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()
Does anyone know how i can get around this?? I am also opened to other suggested apps known to work but i would really like to have this one working
解决方案I got this to work thanks to the Spectras here https://github.com/KristianOellegaard/django-hvad/issues/211
The issue, I guess is DRF tries to do some introspection on the model. I do use DRF in a project of mine, on a TranslatableModel. It needs some glue to work properly. I once suggested adding that to hvad, but we concluded that that would be overextending the feature set. Maybe another module some day, but I don't have enough time to maintain both hvad and that.
It's been some time since I implemented it, so here it is as is:
# 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
From there, you just inherit your serializers from
TranslatableModelSerializer
orHyperlinkedTranslatableModelSerializer
. When POSTing, you should simple add language_code as a normal field as part of your JSON / XML / whatever.The main trick is in the restore_object method. Object creation needs to include translation loading.
这篇关于Django Rest框架和Django-Hvad的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!