Django 如何在api浏览器中自定义选择选项的文本?

Django 如何在api浏览器中自定义选择选项的文本?,django,python-3.x,django-rest-framework,Django,Python 3.x,Django Rest Framework,我正在django 1.8中使用rest_框架v3.1.3。我对django很陌生 以下是相关的模型定义 @python_2_unicode_compatible class UserFitbit(models.Model): user = models.OneToOneField(User, related_name='fituser') fitbit_user = models.CharField(max_length=32) auth_token = mod

我正在django 1.8中使用rest_框架v3.1.3。我对django很陌生

以下是相关的模型定义

    @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浏览器进行挑战时,我需要能够根据用户的姓名选择用户,但选择仅显示其用户id属性和超链接url。我希望成员是用户对象,但我不知道如何更改select选项的文本,因为我认为我无法更改内置用户对象。更改“选择框”选项以显示用户对象而不是用户名字段和超链接中的用户名的最佳方法是什么

这是一张图片:

我不确定这是否是最好的方法,但在阅读了DRF的源代码之后,我会尝试一下

对超链接的最新字段进行子类化,并覆盖
选项
属性

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或更高版本,将有一个可用的
显示值
方法

你能行

class UserHyperLinkedRelatedField(serializers.HyperLinkedRelatedField):

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

因为这是一个多相关字段,所以我还必须扩展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, 
)

现在有一张改进的公开票:接受,因为这让我找到了解决方案。我还需要对ManyRelatedField进行子类化,以使其在这种情况下工作。我在另一个答案中发布了我的代码,以防其他人遇到此问题