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

如何在Django Recipe模型中关联符合条件的专属Favorite Review?

问题描述

我有Recipe(菜谱)和Review(评论)两个模型,菜谱的作者(chef)可以把某条评论设为自己的最爱。我已经通过约束确保每个菜谱只能有一条最爱评论,现在想在Recipe模型里直接引用这条指定的最爱评论,请问该怎么实现?

我期望的模板用法如下:

{% for recipe in recipes %}
<div>
  <h2>{{ recipe.name }}</h2>
  <p>{{ recipe.favoriteReview.content }} <small>by {{ recipe.favoriteReview.author }}</small></p>
  <p>Email the chef: {{ recipe.chef.email }}</p>
</div>
{% endfor %}

我自己写的模型示例似乎方向有误,陷入了瓶颈:

# Models as example
class User(AbstractUser):
  pass

class Recipe(models.model):
  name = models.CharField(max_length=64)
  instructions = models.TextField(max_length=1000)
  chef = models.ForeignKey("User", on_delete=models.SET_NULL, related_name="recipes")
 #favoriteReview = The review with chefsFavorite=True and recipe matches this recipe

class Review(models.model):
  author = models.ForeignKey("User", on_delete=models.SET_NULL, related_name="reviews")
  rating = models.DecimalField(max_digits=2, decimal_places=2)
  content = models.TextField(max_length=1000)
  chefsFavorite = models.Boolean(default="True")
  recipe = models.ForeignKey("Recipe", on_delete=models.CASCADE, related_name="recipes")

  class Meta:
    constraints = [
      models.UniqueConstraint(fields=['recipe'], condition=models.Q(chefsFavorite=True), name="chefs_one_favorite_recipe_review"
    ]

解决方案

首先先修正你模型里的几个基础错误:

  • 模型类继承的是models.Model(大写M),不是models.model
  • Review的related_name应该设为reviews,避免和User的recipes关联名冲突
  • chefsFavorite的默认值是布尔值True,不是字符串"True"
  • UniqueConstraint的括号需要闭合

下面提供两种可行的实现方式:

方案一:添加外键字段直接关联(性能更优,推荐)

在Recipe模型中直接存储指向最爱评论的外键,配合约束保证数据一致性,模板可直接调用关联字段。

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

class Recipe(models.Model):
    name = models.CharField(max_length=64)
    instructions = models.TextField(max_length=1000)
    chef = models.ForeignKey("User", on_delete=models.SET_NULL, related_name="recipes", null=True)
    # 添加外键指向最爱评论,允许为空(比如菜谱还无评论时)
    favorite_review = models.ForeignKey(
        "Review",
        on_delete=models.SET_NULL,
        related_name="marked_as_favorite_by_recipe",
        null=True,
        blank=True
    )

    class Meta:
        # 约束:确保favorite_review属于当前菜谱且是chef标记的最爱
        constraints = [
            models.CheckConstraint(
                check=models.Q(favorite_review__recipe=models.F('id'), favorite_review__chefs_favorite=True),
                name="favorite_review_belongs_to_recipe_and_is_favorite"
            )
        ]

class Review(models.Model):
    author = models.ForeignKey("User", on_delete=models.SET_NULL, related_name="reviews", null=True)
    rating = models.DecimalField(max_digits=2, decimal_places=2)
    content = models.TextField(max_length=1000)
    chefs_favorite = models.BooleanField(default=False)  # 默认不标记为最爱,需手动设置
    recipe = models.ForeignKey("Recipe", on_delete=models.CASCADE, related_name="reviews")

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['recipe'],
                condition=models.Q(chefs_favorite=True),
                name="chefs_one_favorite_recipe_review"
            )
        ]

补充说明

  • 字段名遵循Django规范用下划线分隔,模板中对应改为{{ recipe.favorite_review.content }}
  • 标记评论为最爱时,需同步更新Recipe的favorite_review字段,可通过业务逻辑或信号自动维护,示例逻辑:
def mark_review_as_favorite(review):
    # 取消该菜谱之前的最爱评论
    Review.objects.filter(recipe=review.recipe, chefs_favorite=True).update(chefs_favorite=False)
    # 标记当前评论为最爱
    review.chefs_favorite = True
    review.save()
    # 更新菜谱的关联字段
    review.recipe.favorite_review = review
    review.recipe.save()

方案二:用@property动态获取(无需额外存储,适合小数据量场景)

如果不想增加额外字段,可在Recipe模型中定义属性方法,每次访问时自动查询对应最爱评论。

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

class Recipe(models.Model):
    name = models.CharField(max_length=64)
    instructions = models.TextField(max_length=1000)
    chef = models.ForeignKey("User", on_delete=models.SET_NULL, related_name="recipes", null=True)

    @property
    def favorite_review(self):
        # 从当前菜谱的评论中筛选出标记为最爱的那条
        return self.reviews.filter(chefs_favorite=True).first()

class Review(models.Model):
    author = models.ForeignKey("User", on_delete=models.SET_NULL, related_name="reviews", null=True)
    rating = models.DecimalField(max_digits=2, decimal_places=2)
    content = models.TextField(max_length=1000)
    chefs_favorite = models.BooleanField(default=False)
    recipe = models.ForeignKey("Recipe", on_delete=models.CASCADE, related_name="reviews")

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['recipe'],
                condition=models.Q(chefs_favorite=True),
                name="chefs_one_favorite_recipe_review"
            )
        ]

补充说明

  • 模板中同样可用{{ recipe.favorite_review.content }},但需处理评论为空的情况:
{% if recipe.favorite_review %}
<p>{{ recipe.favorite_review.content }} <small>by {{ recipe.favorite_review.author }}</small></p>
{% else %}
<p>暂无精选评论</p>
{% endif %}

内容的提问来源于stack exchange,提问作者chris-calmatlas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 15:22:13