2015-08-03 98 views
1

我在django 1.8中使用rest_framework v3.1.3。我对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), 
    } 

串行正如你所看到的挑战有很多与用户一对多的关系。这是由django内置的用户模型,而不是在此定义的UserFitBit。

有了这些定义,当我去api浏览器进行挑战时,我需要能够根据用户名选择用户,但select只显示用户ID属性和超链接url。我希望成员是User对象,但我不知道如何更改选择选项的文本,因为我不认为我可以更改内置的User对象。更改选择框选项以显示User对象中的用户名而不是用户名字段和超链接的最佳方法是什么?

这里是一个形象: enter image description here

+2

现在有改进本开放式票:https://github.com/tomchristie/django-rest-framework/issues/3254 –

回答

2

我不知道这是否是最好的方式,但阅读DRF的源代码后,我会尝试这一点。

子类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和覆盖RelatedField的many_init方法使用该类。不能说我完全理解这一切,但它正在工作。

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, 
) 
相关问题