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

时间:2015-08-03 12:24:04

标签: django python-3.x django-rest-framework

我在django 1.8中使用rest_framework 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浏览器接受挑战时,我需要能够根据用户名选择用户,但select只显示他们的User id属性和超链接url。我希望成员是User对象,但我不知道如何更改select选项的文本,因为我认为我不能更改内置的User对象。更改选择框选项以从User对象而不是用户名字段和超链接显示用户名称的最佳方法是什么?

这是一张图片: enter image description here

2 个答案:

答案 0 :(得分: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()

答案 1 :(得分: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, 
)