2015-08-03 3 views
1

Я использую rest_framework v3.1.3 в django 1.8. Я довольно новичок в django.Как настроить текст опций выбора в браузере api?

Вот соответствующая модель определений

@python_2_unicode_compatible 
class UserFitbit(models.Model): 
    user = models.OneToOneField(User, related_name='fituser') 
    fitbit_user = models.CharField(max_length=32) 
    auth_token = models.TextField() 
    auth_secret = models.TextField() 

    #this is a hack so that I can use this as a lookup field in the serializers 
    @property 
    def user__userid(self): 
     return self.user.id 

    def __str__(self): 
     return self.user.first_name + ' ' + self.user.last_name 

    def get_user_data(self): 
     return { 
      'user_key': self.auth_token, 
      'user_secret': self.auth_secret, 
      'user_id': self.fitbit_user, 
      'resource_owner_key': self.auth_token, 
      'resource_owner_secret': self.auth_secret, 
      'user_id': self.fitbit_user, 
     } 

    def to_JSON(self): 
     return json.dumps(self, default=lambda o: o.__dict__, 
      sort_keys=True, indent=4) 



class Challenge(models.Model): 
    name=models.TextField() 
    status=models.TextField() #active, pending, ended, deleted 
    start_date=models.DateField() 
    end_date=models.DateField() 
    #members=models.ManyToManyField(UserFitbit) 
    members=models.ManyToManyField(User) 
    admin=models.ForeignKey(UserFitbit,related_name='admin') 

    #for each member get stats between the start and end dates 
    def memberstats(self): 
     stats = [] 
     for member in self.members.all(): 
      fbu = UserFitbit.objects.filter(user__id=member.id) 
      fu = UserData.objects.filter(userfitbit=fbu) 
      fu = fu.filter(activity_date__range=[self.start_date,self.end_date]) 
      fu = fu.annotate(first_name=F('userfitbit__user__first_name')) 
      fu = fu.annotate(user_id=F('userfitbit__user__id')) 
      fu = fu.annotate(last_name=F('userfitbit__user__last_name')) 
      fu = fu.values('first_name','last_name','user_id') 
      fu = fu.annotate(total_distance=Sum('distance'),total_steps=Sum('steps')) 
      if fu: 
       stats.append(fu[0]) 
     return stats 

    def __str__(self): 
     return 'Challenge:' + str(self.name) 

    class Meta: 
     ordering = ('-start_date','name') 

А вот сериализатору на вызов

class ChallengeSerializer(serializers.ModelSerializer): 
    links = serializers.SerializerMethodField(read_only=True) 
    memberstats = MemberStatSerializer(read_only=True,many=True) 
    #these are user objects 
    #this should provide a hyperlink to each member 
    members = serializers.HyperlinkedRelatedField(
    #queryset defines the valid selectable values 
    queryset=User.objects.all(), 
    view_name='user-detail', 
    lookup_field='pk', 
    many=True, 
) 

    class Meta: 
    model=Challenge 
    fields = ('id','name','admin','status','start_date','end_date','members','links','memberstats',) 
    read_only_fields = ('memberstats','links',) 


    def get_links(self, obj) : 
    request = self.context['request'] 
    return { 
     'self': reverse('challenge-detail', 
     kwargs={'pk':obj.pk},request=request), 
    } 

Как вы можете видеть, что вызов имеет отношения многие ко многим с пользователем. Это встроенная модель User от django, а не UserFitBit, определенного здесь.

С этими определениями, когда я перехожу к браузеру api для вызова, мне нужно иметь возможность выбирать пользователей на основе их имени, но в этом выборе отображается только их идентификатор пользователя и гиперссылка. Я хотел бы, чтобы члены были объектами User, но я не знаю, как изменить текст для параметров выбора, так как я не думаю, что могу изменить встроенный объект User. Каков наилучший способ изменить параметры окна выбора, чтобы показать имя пользователя из объекта User, а не поля имени пользователя и гиперссылки?

Вот изображение: enter image description here

+2

Там теперь открытый билет для улучшения этого: https://github.com/tomchristie/django-rest-framework/issues/3254 –

ответ

2

Я не уверен, если это лучший способ, но после прочтения исходного кода ФПИ, я хотел бы попробовать это.

Подкласс HyperlinkedRelatedField и переопределить choices.

import six 
from collections import OrderedDict 

class UserHyperLinkedRelatedField(serializers.HyperLinkedRelatedField): 

    @property 
    def choices(self): 
     queryset = self.get_queryset() 
     if queryset is None: 
      return {} 

     return OrderedDict([ 
      (
       six.text_type(self.to_representation(item)), 
       six.text_type(item.get_full_name()) 
      ) 
      for item in queryset 
     ]) 

затем просто заменит поле в сериализаторе.

members = UserHyperlinkedRelatedField(
    queryset=User.objects.all(), 
    view_name='user-detail', 
    lookup_field='pk', 
    many=True, 
) 

DRF документы также отметил, что есть план, чтобы добавить общий API для поддержки настраивая формообразования HTML в будущих версиях.

Обновление

Для DRF 3.2.2 или выше, то будет доступен display_value метод.

Вы можете сделать

class UserHyperLinkedRelatedField(serializers.HyperLinkedRelatedField): 

    def display_value(self, instance): 
     return instance.get_full_name() 
+0

Принимается, потому что это привело меня к решению. Мне также нужно было подклассифицировать ManyRelatedField, чтобы он работал в этой ситуации. Я опубликовал свой код в другом ответе на случай, если кто-то еще столкнется с этим – Lee

1

Поскольку это множество смежных областях, я также должен был расширить ManyRelatedField и переопределить метод many_init в RelatedField использовать этот класс. Не могу сказать, что я все это понимаю, но он работает.

class UserManyRelatedField(serializers.ManyRelatedField): 

    @property 
    def choices(self): 
     queryset = self.child_relation.queryset 
     iterable = queryset.all() if (hasattr(queryset, 'all')) else queryset 
     items_and_representations = [ 
      (item, self.child_relation.to_representation(item)) 
      for item in iterable 
     ] 
     return OrderedDict([ 
      (
       six.text_type(item_representation), 
       item.get_full_name() , 
      ) 
      for item, item_representation in items_and_representations 
     ]) 


class UserHyperlinkedRelatedField(serializers.HyperlinkedRelatedField): 

    @classmethod 
    def many_init(cls, *args, **kwargs): 
     list_kwargs = {'child_relation': cls(*args, **kwargs)} 
     for key in kwargs.keys(): 
      if key in MANY_RELATION_KWARGS: 
       list_kwargs[key] = kwargs[key] 
     return UserManyRelatedField(**list_kwargs) 


members = UserHyperlinkedRelatedField(
    queryset=User.objects.all(), 
    view_name='user-detail', 
    lookup_field='pk', 
    many=True, 
)