如果是条件,则选择所有django外键模型;如果是另一个条件,则选择部分

时间:2011-12-02 17:49:42

标签: python django django-models

我有以下型号:

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_collaborator的所有项目,以及与该项目关联的所有节点,还要选择项目中某个节点上用户是协作者的所有项目,但是project仅选择用户是其协作者的节点。

用户可以是project_collaborator以及节点上的协作者,但这应该只返回项目/节点的一个实例。

我到目前为止最接近的是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 个答案:

答案 0 :(得分:2)

我会给你两种方法:

使用尽可能少的查询执行此操作

获取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)

希望有所帮助。 :)