2

поэтому мне нужно было иметь поддержку перевода модели для моего API-интерфейса DRF, и я начал использовать django-hvad.Django Rest Framework и 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

Вот мои модели, сериализаторов и viewsets:

Модель:

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 

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

Кто-нибудь знает, как я могу обойти эту проблему ?? Я также открыт для других предлагаемых приложений, известных на работу, но я очень хотел бы, чтобы этот рабочий

ответ

2

Я получил эту работу благодаря к 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 

Оттуда вы просто наследуют ваши сериализаторы от TranslatableModelSerializer или HyperlinkedTranslatableModelSerializer. Когда POSTing, вы должны просто добавить language_code как нормальное поле как часть вашего JSON/XML/что угодно.

Основной трюк в методе restore_object. Для создания объекта необходимо включить загрузку перевода.

+2

Кстати, если кто-то сталкивается с этим, полная поддержка REST framework 3.1 была включена непосредственно в django-hvad. – spectras