根据条件选择所有的Django外键模型或部分记录

1 投票
1 回答
1031 浏览
提问于 2025-04-17 07:33

我有以下这些模型:

class Project(models.Model):
    title = models.CharField(max_length=75)
    description = models.CharField(max_length=250)
    project_collaborators = models.ManyToManyField(User)
...

class Node(models.Model):
    title = models.CharField(max_length=75)
    collaborators = models.ManyToManyField(User)
    project = models.ForeignKey(Project)

我想做的是选择所有用户作为项目合作者的项目,以及与这些项目相关的所有节点。同时还要选择用户在项目中的某个节点上作为合作者的所有项目,但在每个项目中只选择用户作为合作者的节点。

一个用户可能既是项目合作者,又是某个节点的合作者,但最终结果中每个项目或节点只出现一次。

到目前为止,我最接近的做法是 Project.objects.filter(Q(node__collaborators=user) | Q(project_collaborators=user)).distinct(),但这并没有完全达到我的要求。

编辑:我使用的解决方案

我在 views.py 中开始这样做,以获取用户关联的所有项目:

projects = Project.objects.select_related().filter(Q(project_collaborators=request.user) | Q(canvas__collaborators=request.user)).distinct()

然后在模板中我这样做:

{% for project in projects %}

    {{ project.title }}
    <ul>
    {% for node in project.node_set.all %}

        {% if request.user in project.project_collaborators.all or request.user in node.collaborators.all %}

            <li>{{ node.title }}</li>

        {% endif %}

    {% endfor %}
    </ul>
    <br />

{% endfor %}

这样可以让我打印出所有节点,如果用户是项目合作者,或者如果他们只是某个节点的合作者,则只打印出特定的节点,同时仍然打印出他们关联的所有项目。

1 个回答

3

我给你两种方法:

尽量减少查询次数

先获取 Node 对象,然后再用这些对象去获取 Project 对象,而不是反过来:

nodes = Node.objects.filter(Q(project__project_collaborators=user) | Q(collaborators=user)).select_related('project').distinct()

这样你就得到了你想要的节点,而且只有这些节点。如果你只需要节点,这没问题。如果你需要列出项目,可以用 Python 来轻松整理这些,虽然不能作为 QuerySet

projects = []
for node in nodes:
    if node.project not in projects:
        projects.append(node.project)

如果你需要项目作为 QuerySet,你可以通过多一次查询来获取它们——这里是相应的代码(替代上面的代码块):

project_ids = set([node.project.id for node in nodes])
projects = Project.objects.filter(id__in=project_ids)

注意,如果你需要将 Project 实例和它们对应的 Node 实例重新关联,你需要做这一步:

projects_and_nodes = {}
for project in projects:
     projects_and_nodes[project] = [node for node in nodes if node.project == project]

尽量写出干净的代码

你似乎已经知道如何获取你想要的 Project 实例——你还没有完全搞清楚的是如何获取正确的 Node 实例。你需要一些逻辑来处理你获取到的 Project,这段逻辑是:

# pseudocode
if the user is a collaborator on this project:
    get all the nodes
else:
    get only the nodes applicable to the user

在这种情况下,使用你提供的代码来获取项目,然后这里是获取相应节点的 Python 代码:

if request.user in project.project_collaborators.all():
    nodes = project.node_set.all()
else:
    nodes = project.node_set.filter(collaborators=request.user)

希望这对你有帮助。:)

撰写回答