【问题标题】:Django - Prefetch filter does not work on Many to Many modelDjango - 预取过滤器不适用于多对多模型
【发布时间】:2020-10-09 09:42:32
【问题描述】:

我想要一个用于按周和年过滤 Credits 对象的 Group 端点。组与成员身份的“通过”arg 具有多对多关系。用户的会员外键。将外键归功于用户。

我有以下型号:

class Group(models.Model):
    name = models.CharField(max_length=128, unique=True)
    members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="memberships", 
    through='Membership')
    
class Membership(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="membership", 
    on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    
class Credits(models.Model):
    credits = models.IntegerField()
    year = models.IntegerField(default=date.today().isocalendar()[0])
    week = models.IntegerField(default=date.today().isocalendar()[1])
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="credits", 
    on_delete=models.CASCADE)

class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(max_length=255, unique=True)
    username = NameField(max_length=25, unique=True,                              
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

序列化器:

class MembershipSerializer(serializers.ModelSerializer):
    username = serializers.ReadOnlyField(source='user.username')
    credits = CreditsSerializer(source='user.credits', many=True, required=False, allow_null=True)

    class Meta:
        model = Membership
        fields = ('id', 'is_admin', 'user_id', 'username', 'credits')
        read_only_fields = ('id',)

        depth = 1


class GroupSerializer(serializers.ModelSerializer):
    memberships = MembershipSerializer(source='membership_set', many=True)

    class Meta:
        model = Group
        fields = ('id', 'name', 'memberships')

我的群组视图是:

class GroupSet(generics.ListAPIView):
    lookup_field = 'name'
    serializer_class = GroupSerializer

    def get_queryset(self):

        year = self.request.query_params.get('year', None)
        week = self.request.query_params.get('week', None)
        name = self.request.query_params.get('name', None)

        if name and week and year is not None:

            prefetchCredits = Prefetch('members__credits', 
            queryset=Credits.objects.filter(year=year, week=week), to_attr='credit_objs')

            group = Group.objects.filter(name__iexact=name).prefetch_related(prefetchCredits)

            return group

学分过滤器不起作用。在 Prefetch 上的过滤器确实起作用但模型没有多对多关系之前,我使用过这种视图结构。

我尝试迭代 prefetchCredits 以过滤出正确的对象,但我收到错误:'Prefetch' object is not iterable

我不断得到下面的结构。年和周过滤器只能显示一个学分对象。

[
    {
        "id": 3,
        "name": "Group2",
        "memberships": [
            {
                "id": 3,
                "user_id": 2,
                "username": "test1",
                "credits": [
                    {
                        "credits": 800,
                        "year": 2020,
                        "week": 41
                    },
                    {
                        "credits": 1000,
                        "year": 2020,
                        "week": 40
                    },
                    {
                        "credits": 996,
                        "year": 2020,
                        "week": 39
                    }
                ],

【问题讨论】:

  • 您不能迭代 Prefetch 对象。 Prefetch 对象不存储任何数据,它只是一个预取任务的表示。
  • 您可以使用to_attr 来存储过滤后的值。可以分享GroupSerializer吗?
  • @WillemVanOnsem 感谢您的快速回复。我已经添加了序列化程序
  • @WillemVanOnsem 在这种情况下,您将如何使用 to_attr?
  • 我已添加 to_attr 并将其中的对象存储在列表中。然后添加了一个新的过滤器来过滤列表中的对象,但它仍然不起作用。

标签: python django django-rest-framework django-views django-serializer


【解决方案1】:

模型“Group”和“Credit”类之间没有直接联系,因此无法直接预取学分。

首先您需要预取相关成员(Membership)并使用多级预取来包含该成员的信用。

可以在预取内的查询集上使用预取来构建多级预取结构。

prefetchCredits = Prefetch('user__credits', queryset=Credits.objects.filter(year=year, week=week))
prefetchMembership = Prefetch(
    'membership_set', 
    queryset=Membership.objects.prefetch_related(prefetchCredits)
)
group = Group.objects.filter(name__iexact=name).prefetch_related(prefetchMembership)

Credits Prefetch 的名称也更改为“users__credits”以匹配序列化程序“source”的名称。

【讨论】:

  • 谢谢!这正是我想要的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 2011-06-09
  • 1970-01-01
  • 2011-01-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多