django restframework PrimaryKeyRelatedField筛选的困惑

一.在开发某运动app时,遇见以下情况

  1.部分表内容如下:

class Sports(models.Model):
    '''
    运动表
    '''
    school = models.ForeignKey(Schools, verbose_name='学校', on_delete=models.CASCADE)
    sport_name = models.CharField(max_length=30, verbose_name='运动项目')
    image = models.ImageField(upload_to='sports/%Y/%m', verbose_name='运动封面图')
    add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')

    class Meta:
        verbose_name = '运动项目'
        verbose_name_plural = verbose_name
        unique_together = ('school', 'sport_name')

    def __str__(self):
        return self.sport_name
class Schedule(models.Model):
    '''
    发起约运动
    '''
    Status = ((1, '已完成'), (2, '待人加入'), (3, '已取消'))
    user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name='发起人')
    join_type = models.BooleanField(verbose_name='是否发起人')
    now_people = models.IntegerField(verbose_name='已有人数', null=True, blank=True)
    school = models.ForeignKey(Schools, on_delete=models.CASCADE, verbose_name='学校', default='')
    sport = models.ForeignKey(Sports, on_delete=models.CASCADE, verbose_name='运动项目')
    address = models.CharField(verbose_name='约定地点', max_length=100)
    sport_time = models.DateTimeField(verbose_name='约定运动开始时间')
    sport_end_time = models.DateTimeField(verbose_name='约定运动结束时间', default='')
    people_nums = models.IntegerField(verbose_name='人数')
    add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
    status = models.IntegerField(choices=Status, verbose_name='状态')

    class Meta:
        verbose_name = '发起约运动'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.sport.sport_name + '-' + str(self.sport_time)
class UserProfile(AbstractUser):
    '''
    用户表
    '''
    name = models.CharField(max_length=30, null=True, blank=True, verbose_name='姓名')
    image = models.ImageField(upload_to='users/', default='', null=True, blank=True, verbose_name='头像')
    birthday = models.DateField(null=True, blank=True, verbose_name='出生年月')
    mobile = models.CharField(max_length=11, verbose_name='电话', null=True, blank=True)
    student_id = models.CharField(max_length=32, verbose_name='学号', default='', null=True, blank=True)
    gender = models.CharField(max_length=6, choices=(('male', ''), ('fmale', '')), default='', verbose_name='性别')
    email = models.EmailField(verbose_name='邮箱')
    school = models.ForeignKey(Schools, verbose_name='学校', on_delete=models.CASCADE)
    institude = models.ForeignKey(Schools, verbose_name='学院', on_delete=models.CASCADE, related_name='institude')
    profession = models.ForeignKey(Schools, verbose_name='专业', on_delete=models.CASCADE, related_name='profession')
    add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')

    class Meta():
        verbose_name = '用户'
        verbose_name_plural = verbose_name

    def __str__(self):
        if self.name:
            return self.name
        else:
            return self.username

class Schedule(models.Model):
    '''
    发起约运动
    '''
    Status = ((1, '已完成'), (2, '待人加入'), (3, '已取消'))
    user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, verbose_name='发起人')
    join_type = models.BooleanField(verbose_name='是否发起人')
    now_people = models.IntegerField(verbose_name='已有人数', null=True, blank=True)
    school = models.ForeignKey(Schools, on_delete=models.CASCADE, verbose_name='学校', default='')
    sport = models.ForeignKey(Sports, on_delete=models.CASCADE, verbose_name='运动项目')
    address = models.CharField(verbose_name='约定地点', max_length=100)
    sport_time = models.DateTimeField(verbose_name='约定运动开始时间')
    sport_end_time = models.DateTimeField(verbose_name='约定运动结束时间', default='')
    people_nums = models.IntegerField(verbose_name='人数')
    add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')
    status = models.IntegerField(choices=Status, verbose_name='状态')

    class Meta:
        verbose_name = '发起约运动'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.sport.sport_name + '-' + str(self.sport_time)

   2.序列化想实现的功能:

    在序列化Schedule表时,sport是一个外键字段,想筛选sport的外键school是当前已登录的用户的school字段下的该学校所有运动项目(即筛选为queryset=Sports.objects.filter(school=user.school)),看似很简单,但该如何在字段中获取当前用户呐。

    首先想到,把该字段设计为SerializerMethodField字段,即如下(该方法确实能实现筛选,但这不是我要的结果,我想在序列化时有该字段,并且能选择筛选出的数据中的一个并添加新的数据):

#失败方法
sport = serializers.SerializerMethodField() def get_sport(self, obj): all_sport = Sports.objects.filter(school=self.context['request'].user.school).values_list('pk',flat=True) json_all = SportsSerializer(all_sport, many=True, context={'request': self.context['request']}).data return all_sport

    然后就有点麻烦,想从写view中的get_queryset(),也是只能在list或retrieve中才能筛选,而序列化添加仍是一个问题;又想到从写一个类或者函数专门筛选,但是又增大了获取当前用户的难度,有点恼火,咋办,看看源码它写了哪些方法,真看不太懂,只能了解一点。

    看到了RelatedField中的get_queryset()方法,好像重写它有点作用:

 def get_queryset(self):
        queryset = self.queryset
        if isinstance(queryset, (QuerySet, Manager)):
            # Ensure queryset is re-evaluated whenever used.
            # Note that actually a `Manager` class may also be used as the
            # queryset argument. This occurs on ModelSerializer fields,
            # as it allows us to generate a more expressive 'repr' output
            # for the field.
            # Eg: 'MyRelationship(queryset=ExampleModel.objects.all())'
            queryset = queryset.all()
        return queryset

三.目前最终方案: 

    于是勉强写吧,重写该方法确实有用,只能说功能完成了,但还真得改进:

 class SportPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
        def get_queryset(self):
            queryset = self.queryset
            # if isinstance(queryset, (QuerySet, Manager)):
            # Ensure queryset is re-evaluated whenever used.
            # Note that actually a `Manager` class may also be used as the
            # queryset argument. This occurs on ModelSerializer fields,
            # as it allows us to generate a more expressive 'repr' output
            # for the field.
            # Eg: 'MyRelationship(queryset=ExampleModel.objects.all())'
            queryset = queryset.filter(school=self.context['request'].user.school)
            return queryset

    sport = SportPrimaryKeyRelatedField(queryset=Sports.objects.all(), label="运动")

    

原文地址:https://www.cnblogs.com/lyq-biu/p/9769421.html