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

Django饮品配方应用:如何基于多对多标签过滤相似饮品?

问题描述

我正在开发一款饮品配方应用,每个DrinkRecipe对象通过多对多字段drinkTag和drinkTagTaste关联标签(口味描述)。现需在详情页显示3款与当前饮品标签相似的饮品。

我的视图代码如下:

def drinkdetail_view(request, slug=None):
    #Grab the correct drink
    try:
        obj = DrinkRecipe.objects.get(slug=slug)
    except:
        obj = None
    if obj is  None:
        return HttpResponse("Not found.")
   
context = {
    "drink": obj,
}

return render(request,'drinks/drinkdetail.html', context)

模型代码如下:

class Tag(models.Model):
    drinkTag = models.CharField(max_length=255, blank=False)

    def __str__(self):
        return f"{self.drinkTag}"

class TagTaste(models.Model):
    drinkTagTaste = models.CharField(max_length=255, blank=False)

    def __str__(self):
        return f"{self.drinkTagTaste}"
    
class Ingredient(models.Model):
    ingredientRum = models.ForeignKey('Bottle', on_delete=models.SET_NULL, null=True, blank=True)
    ingredientName = models.CharField(max_length=255, blank=True)
    ingredientAmount = models.DecimalField(decimal_places=1,max_digits=5)
    drink = models.ForeignKey('DrinkRecipe', on_delete=models.CASCADE)

    def __str__(self):
        if self.ingredientRum:
            return f"{self.ingredientRum}"
        else:
            return f"{self.ingredientName}"


class DrinkRecipe(models.Model):
    drinkTag = models.ManyToManyField(Tag)
    drinkTagRum = models.ForeignKey(Bottle, on_delete=models.SET_NULL, null=True)
    drinkTagTaste = models.ManyToManyField(TagTaste)
    drinkName = models.CharField(max_length=255, blank=False)
    drinkDescription = HTMLField()
    drinkImage = models.ImageField(upload_to=image_upload_handler)
    drinkActive = models.BooleanField(default=True)
    slug = models.SlugField(unique=True, blank=True, null=True)
    imgLocation = 'drinks'

    def __str__(self):
        return f"{self.drinkName}"


    def get_ingredients(self):
        return self.ingredient_set.all()
    
    def get_absolute_url(self):
        return reverse ('rhumSite:drinkDetail', kwargs={'slug':self.slug})

我尝试使用similar_drinks = DrinkRecipe.objects.filter(drinkTag__drinkTag=obj.drinkTag)来过滤,但无法生效,硬编码标签值则可以正常运行。请问如何在视图中基于当前饮品的drinkTag或drinkTagTaste过滤出相似饮品?


解决方案

你当前的过滤写法错误在于:obj.drinkTag是多对多关系的管理器对象,不是单个标签值,无法直接用于匹配。以下是正确的实现方式:

1. 基于drinkTag过滤相似饮品

通过drinkTag__in传入当前饮品的所有标签集合,匹配包含任意相同标签的饮品:

# 获取当前饮品的所有drinkTag对象
current_tags = obj.drinkTag.all()
# 过滤相似饮品:排除当前饮品,去重后取前3个
similar_drinks = DrinkRecipe.objects.filter(
    drinkTag__in=current_tags,
    drinkActive=True  # 可选:仅显示激活状态的饮品
).exclude(id=obj.id).distinct()[:3]
  • distinct()避免同一饮品因多个匹配标签重复出现
  • exclude(id=obj.id)确保不会推荐当前饮品本身

2. 基于drinkTagTaste过滤相似饮品

逻辑与上述一致,替换为口味标签字段即可:

current_taste_tags = obj.drinkTagTaste.all()
similar_drinks_by_taste = DrinkRecipe.objects.filter(
    drinkTagTaste__in=current_taste_tags,
    drinkActive=True
).exclude(id=obj.id).distinct()[:3]

3. 混合两种标签推荐(可选)

如果想同时匹配drinkTag和drinkTagTaste,使用Q对象实现或条件:

from django.db.models import Q

current_tags = obj.drinkTag.all()
current_taste_tags = obj.drinkTagTaste.all()

similar_drinks = DrinkRecipe.objects.filter(
    Q(drinkTag__in=current_tags) | Q(drinkTagTaste__in=current_taste_tags),
    drinkActive=True
).exclude(id=obj.id).distinct()[:3]

4. 优化:按相似度排序(可选)

通过计算匹配标签的数量,让重叠标签最多的饮品排在前面:

from django.db.models import Count

similar_drinks = DrinkRecipe.objects.filter(
    drinkTag__in=current_tags,
    drinkActive=True
).exclude(id=obj.id).annotate(
    matching_tags=Count('drinkTag')  # 统计匹配的标签数量
).order_by('-matching_tags').distinct()[:3]

整合到视图的完整代码

def drinkdetail_view(request, slug=None):
    try:
        obj = DrinkRecipe.objects.get(slug=slug)
    except DrinkRecipe.DoesNotExist:
        return HttpResponse("Not found.")
    
    # 获取基于drinkTag的相似饮品(可根据需求替换为其他过滤逻辑)
    current_tags = obj.drinkTag.all()
    similar_drinks = DrinkRecipe.objects.filter(
        drinkTag__in=current_tags,
        drinkActive=True
    ).exclude(id=obj.id).annotate(
        matching_tags=Count('drinkTag')
    ).order_by('-matching_tags').distinct()[:3]
    
    context = {
        "drink": obj,
        "similar_drinks": similar_drinks
    }

    return render(request,'drinks/drinkdetail.html', context)

之后在模板中遍历similar_drinks即可展示推荐的饮品。

内容的提问来源于stack exchange,提问作者Thomas Doll-Datema

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:17:06