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

Django中如何从关联外键的三张表获取特定用户的Boost等级数据

模型结构优化方案

先把冗余和不合理的字段调整,让后续查询更高效:

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


class Boost(models.Model):
    title = models.CharField(max_length=50, null=False)  # 比如point、storage这类增益名称
    expire = models.BooleanField(default=False)
    expire_date = models.DateTimeField(null=True)  # 改成下划线命名符合Django规范
    visibility = models.BooleanField(default=True)  # 修正原拼写错误(visiblity→visibility)

    def __str__(self):
        return self.title  # 直接返回标题更直观


class BoostLevel(models.Model):
    boost = models.ForeignKey(Boost, on_delete=models.CASCADE, related_name="levels")
    level = models.IntegerField(null=False)

    class Meta:
        unique_together = ("boost", "level")  # 确保同一个Boost下等级不重复
        ordering = ["level"]  # 按等级排序,方便后续取上下等级

    def __str__(self):
        return f"{self.boost.title} level: {self.level}"


class UserBoost(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="boosts")
    boost_level = models.ForeignKey(BoostLevel, on_delete=models.CASCADE)

    class Meta:
        unique_together = ("user", "boost_level__boost")  # 确保用户对同一个Boost只存当前最高等级

    def __str__(self):
        return f"{self.user.username} - {self.boost_level}"

调整点说明:

  • 删除UserBoostList里冗余的boostId:boost_level已关联BoostLevel,可间接关联Boost,无需重复存储
  • 将userId改为ForeignKey关联User:利用Django ORM的关联特性,避免手动维护ID一致性
  • 修正字段命名与拼写:统一为下划线命名风格,修正visibility拼写错误
  • 给BoostLevel加约束与排序:unique_together防止重复等级,ordering方便按等级顺序查询
  • 重命名UserBoostList为UserBoost并加约束:贴合语义,同时确保用户对每个Boost仅存储当前最高等级(用户依次升级,无需存历史等级,减少冗余)
最优查询方法

方法1:批量查询(单次DB请求,性能最优)

利用Django ORM的聚合、子查询特性,一次性获取单个用户所有Boost的状态:

from django.db.models import Max, Subquery, OuterRef, Case, When, Value, IntegerField

# 子查询:获取每个Boost的最高等级
max_boost_levels = BoostLevel.objects.filter(boost=OuterRef("pk")).values("boost").annotate(max_level=Max("level")).values("max_level")

# 查询所有Boost并标注用户的状态
user_boost_status = Boost.objects.annotate(
    # 获取用户当前拥有的该Boost最高等级,无则为0
    user_current_level=Case(
        When(boosts__user=request.user, then=Max("boosts__boost_level__level")),
        default=Value(0),
        output_field=IntegerField()
    ),
    # 获取该Boost的总最高等级
    boost_max_level=Subquery(max_boost_levels, output_field=IntegerField()),
    # 根据需求计算最终返回的等级
    current_display_level=Case(
        When(user_current_level=0, then=Value(1)),
        When(user_current_level__lt=OuterRef("boost_max_level"), then=user_current_level + 1),
        default="user_current_level",
        output_field=IntegerField()
    )
).values("title", "user_current_level", "boost_max_level", "current_display_level")

返回结果包含每个Boost的标题、用户当前拥有等级、Boost总最高等级、最终应显示的等级。

方法2:单个Boost查询(简洁直观)

在Boost模型中封装方法,用于查询单个用户对该Boost的状态:

class Boost(models.Model):
    # ... 其他字段 ...

    def get_user_display_level(self, user):
        # 获取用户该Boost的最高等级,无则为0
        user_max_level = UserBoost.objects.filter(
            user=user,
            boost_level__boost=self
        ).aggregate(max_level=Max("boost_level__level"))["max_level"] or 0
        
        # 获取该Boost的总最高等级,无则默认1
        boost_max_level = self.levels.aggregate(max_level=Max("level"))["max_level"] or 1
        
        if user_max_level == 0:
            return 1
        elif user_max_level < boost_max_level:
            return user_max_level + 1
        else:
            return user_max_level

使用示例:

for boost in Boost.objects.all():
    print(f"{boost.title}: 当前显示等级 {boost.get_user_display_level(request.user)}")

注意:方法2循环调用会产生N+1查询,批量查询建议用方法1。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 05:49:52