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

Django从外键模型获取信息的写法是否正确?如何优化?

现有代码的实现逻辑

你这段代码本质是给模型加了一个名为author的只读计算属性,@property装饰器的作用是让这个方法可以像普通实例属性一样被访问,不需要加()调用,大部分DRF序列化、模型转字典的逻辑都会自动识别这个属性,把它当成普通字段返回给前端,这也是为什么功能可以正常运行的原因。

逐行的执行逻辑:

  1. 从当前模型实例上取book_id字段的值
  2. 发起一次数据库查询,取出id匹配的Book记录
  3. 从拿到的Book实例上取author_id字段的值
  4. 再发起一次数据库查询,取出id匹配的Author记录
  5. 从Author实例上取first_name字段的值返回

这个写法目前能跑,但有几个很明显的问题:

  • 存在严重的N+1查询风险:只要你查询多条当前模型的记录,每访问一次author属性就会发起2次独立的数据库查询,查100条数据就会多打200次SQL,数据量上来之后接口响应速度会非常慢。
  • 没有异常处理:如果关联的Book记录被删了、或者Book关联的Author记录不存在,objects.get()会直接抛出DoesNotExist异常,导致接口500报错。
  • 冗余代码太多:你所有用getattr取字段的地方,字段都是模型上确定存在的,直接用self.book_id、book.author_id这种点属性的写法就行,没必要绕一层getattr。
更优的实现方案

根据你的模型关联情况选对应的方案即可:

优先方案:利用ORM的关联关系,避免手动查库

如果你的当前模型本身定义了指向Book的外键,比如:

class Record(models.Model):
    book = models.ForeignKey(Book, on_delete=models.CASCADE)
    # 其他业务字段

完全不需要手动写两次数据库查询,直接顺着关联关系取字段即可:

@property
def author(self):
    try:
        return self.book.author.first_name
    except (Book.DoesNotExist, Author.DoesNotExist):
        return ""

注意查询数据集的时候一定要用select_related提前把两层关联数据一次性查出来,从根源上避免N+1问题:

# 查列表时提前JOIN关联的book和author表,后续访问author属性不会再触发额外查询
record_list = Record.objects.select_related("book__author").all()

如果你是用Django REST Framework做序列化,连模型层的@property都不用写,直接在序列化器里指定字段来源即可:

class RecordSerializer(serializers.ModelSerializer):
    author = serializers.CharField(source="book.author.first_name", read_only=True)
    class Meta:
        model = Record
        fields = "__all__"

特殊情况处理

如果你的模型没有定义外键,只是单独存了book_id字段,建议优先补全外键定义,Django的外键会自动帮你维护关联关系、处理级联逻辑,比手动存id查库靠谱很多。
如果暂时没法修改表结构加外键,至少要补上异常捕获,不要让关联数据缺失直接打挂接口,同时尽量在查询阶段就批量把关联数据查出来缓存,不要在属性里逐条查库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:48:37