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="運動")

 

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM