You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于ManyToMany字段匹配获取指定对象?Django DRF问题求助

问题描述

开发一款食谱APP,目标是返回用户可用现有食材制作的餐品列表,使用Django RestFramework(DRF)实现。当前CraftWithUsersIngridientsListAPIView无法正常工作,报错AttributeError: 'QuerySet' object has no attribute 'ingridients'。

相关代码

models.py

# models.py
class Meal(models.Model):
    name = models.CharField(max_length=250)
    description = models.TextField(blank=True, null=True)
    recipe = models.TextField()
    is_published = models.BooleanField(default=False)
    category = ForeignKey('Category', on_delete=models.CASCADE, null=True)
    user = ForeignKey(User, verbose_name='User', on_delete= models.CASCADE)
    difficulty = ForeignKey('Difficulty', on_delete=models.PROTECT, null=True)
    ingridients = models.ManyToManyField('Ingridient')

class Ingridient(models.Model):
    name = models.CharField(max_length=100, db_index=True)
    ico = models.ImageField(upload_to="photos/%Y/%m/%d/", blank=True, null=True)
    category = ForeignKey('CategoryIngridients', on_delete=models.CASCADE, null=True)

    def __str__(self):
        return self.name

class CookBookUser(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    ingridients = models.ManyToManyField('Ingridient')

serializer.py

class MealSerializer(serializers.ModelSerializer):
    class Meta:
        model = Meal
        fields = "__all__"

views.py

class CraftWithUsersIngridientsListAPIView(generics.ListAPIView):
    serializer_class = MealSerializer

    def get_queryset(self):
       return Meal.objects.filter(ingridients=CookBookUser.objects.filter(user_id = self.request.user.id).ingridients)

解决方案

错误原因

CookBookUser.objects.filter(user_id=self.request.user.id)返回的是QuerySet对象(即使只有一条数据),而不是单个CookBookUser实例,因此无法直接通过.ingridients访问多对多字段。

修复步骤

  • 获取当前用户对应的单个CookBookUser实例(因为是OneToOne关联,每个用户最多对应一个)
  • 提取用户拥有的食材ID列表
  • 筛选出所有食材都包含在用户现有食材中的餐品(确保用户能完整制作该餐品)

修改后的views.py代码:

from django.db.models import Count, F
from rest_framework import generics

class CraftWithUsersIngridientsListAPIView(generics.ListAPIView):
    serializer_class = MealSerializer

    def get_queryset(self):
        # 获取当前用户的CookBookUser实例,处理用户未绑定的情况
        try:
            cook_user = CookBookUser.objects.get(user=self.request.user)
        except CookBookUser.DoesNotExist:
            return Meal.objects.none()
        
        # 获取用户拥有的食材ID集合
        user_ingredient_ids = cook_user.ingridients.values_list('id', flat=True)
        
        # 筛选出所有食材都在用户现有食材中的餐品
        return Meal.objects.annotate(
            # 统计餐品需要的食材总数
            total_ingredients=Count('ingridients')
        ).filter(
            # 筛选出至少有一个食材匹配的餐品
            ingridients__in=user_ingredient_ids
        ).annotate(
            # 统计匹配的食材数量
            matched_ingredients=Count('ingridients')
        ).filter(
            # 确保餐品所有食材都被匹配
            total_ingredients=F('matched_ingredients')
        ).filter(is_published=True)  # 可选:只返回已发布的餐品

额外优化建议

  • 添加is_published=True过滤,只返回已发布的餐品,避免返回草稿状态的内容
  • 若需求是“包含部分食材即可推荐”,可去掉最后一步的total_ingredients=F('matched_ingredients')过滤,同时按匹配食材数量排序:.order_by('-matched_ingredients')

内容的提问来源于stack exchange,提问作者Vleo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 16:55:22