一.在開發某運動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="運動")