Django 获取具有链式外键的对象查询集

2 投票
3 回答
1921 浏览
提问于 2025-04-17 23:56

我有一个项目,结构大致是这样的:

mysite/
|-- books
|   |-- __init__.py
|   `-- models.py
`-- org
    |-- __init__.py
    `-- models.py

在books/models.py文件里:

from django.db import models

from mysite.org.models import Team

class BookSet(models.Model):
    name = models.CharField(max_length=10)
    team = models.ForeignKey(Team, related_name='booksets')

class Book(models.Model):
    name = models.CharField(max_length=10)
    book_set = models.ForeignKey(BookSet, related_name='books')

在org/models.py文件里:

from django.db import models

class Department(models.Model):
    name = models.CharField(max_length=20)

class Team(models.Model):
    name = models.CharField(max_length=20)
    department = models.ForeignKey(Department, related_name='teams')

我想要实现的目标是获取某个部门的所有书籍,类似于下面这样:

class Department(models.Model):
    name = models.CharField(max_length=20)

    @property
    def books(self):
        # this won't work, is there any possible do it like this?
        return self.teams.booksets.books()

我知道一种可能的方法是通过以下方式来实现:

from mysite.books.models import Book

class Department(models.Model):
    name = models.CharField(max_length=20)

    @property
    def books(self):
        return Book.objects.filter(book_set__team__department=self)

但是用这种方法,我需要做一些复杂的操作,才能让这两个文件(books.models和org.models)相互导入。

所以我在想,是否可以通过外键查询,来找到某个部门的所有书籍,像这样:

    @property
    def books(self):
        # this won't work, is there any possible do it like this?
        return self.teams.booksets.books()

3 个回答

1

你可以使用Django加载外键的方式,比如这样:

user = models.ForeignKey('User')

怎么做呢:

from django.db.models.loading import get_model
Book = get_model('books', 'Book')

# which would achieve the same thing as
# from books.models import Book

get_model的定义是:

def get_model(self, app_label, model_name, seed_cache=True): ...

2

我觉得像这样应该可以工作:

class Department(models.Model):
    name = models.CharField(max_length=20)

    @property
    def books(self):
        booksets = list(team.booksets.all() for team in self.teams.all())
        return list(bookset.books.all() for bookset in booksets)

不过我没有简单的方法来测试它。

编辑:我忘了补充一个替代方案:

你可以用字符串来引用外键定义中的模型,像这样:

class BookSet(models.Model):
    name = models.CharField(max_length=10)
    team = models.ForeignKey('org.Team', related_name='booksets')

然后在你的其他应用中,你可以随意导入 books.models。

想了解更多信息,可以查看 文档

3

避免循环导入的一个简单方法是这样设计你的 Department 模型:

class Department(models.Model):
    name = models.CharField(max_length=20)

    @property
    def books(self):
        from mysite.books.models import Book
        return Book.objects.filter(book_set__team__department=self)

撰写回答