通过不同的外键进行循环迭代的 Django 模板

Posted

技术标签:

【中文标题】通过不同的外键进行循环迭代的 Django 模板【英文标题】:Django template for loop iteration by distinct foreign key 【发布时间】:2016-12-06 17:26:55 【问题描述】:

在我的应用中,我有 3 个模型:问题、系列和角色。 Issue 模型有一个 Series ForeignKey 和一个 Character ManyToManyField。在这里他们被简化了:

class Character(models.Model):
    name = models.CharField('Character name', max_length=200)
    desc = models.TextField('Description', max_length=500)

class Series(models.Model):
    name = models.CharField('Series name', max_length=200)
    desc = models.TextField('Description', max_length=500)

class Issue(models.Model):
    series = models.ForeignKey(Series, on_delete=models.CASCADE, blank=True)
    name = models.CharField('Issue name', max_length=200)
    number = models.PositiveSmallIntegerField('Issue number')
    date = models.DateField('Cover date')
    desc = models.TextField('Description', max_length=500)
    characters = models.ManyToManyField(Character, blank=True)
    cover = models.FilePathField('Cover file path', path="media/images/covers")

我有一个显示角色信息的角色模板。我还想显示角色所在的问题,按系列排序。

% extends "app/base.html" %

% block page-title % character.name % endblock page-title %

% block content %

<div class="description">
    <p> character.desc </p>
</div>

<div class="issues">
    <h3>Issues</h3>
    % for series in character.issue_set.all %
    <div>
        <a href="% url 'app:series' series.id %"> series.name </a>
        <ul>
        % for issue in character.issue_set.all %
            % if issue.series.name == series.name %
            <li>
                <a href="% url 'app:issue' issue.id %"><img src="/ issue.cover " alt = " series.name " ></a>
                <a href="% url 'app:issue' issue.id %"><p>Issue # issue.number </p></a>
            </li>
            % endif %
        % endfor %
        </ul>
    </div>
    % endfor %
</div>

% endblock content %

显然,当前的格式化方式是,对于集合中的每个问题,它会输出系列标题,然后是集合中的每个问题。

<div class="issues">
    <h3>Issues</h3>
    <div>
        <a href="/series/1">Series 1</a>
        <ul>
            <li>
                <a href="/issue/1"><img src="/media/images/covers/01.jpg" ></a>
                <a href="/issue/1"><p>Issue #1</p></a>
            </li>
            <li>
                <a href="/issue/2"><img src="/media/images/covers/02.jpg" ></a>
                <a href="/issue/2"><p>Issue #2</p></a>
            </li>
        </ul>
    </div>
    <div>
        <a href="/series/1">Series 1</a>
        <ul>
            <li>
                <a href="/issue/1"><img src="/media/images/covers/01.jpg" ></a>
                <a href="/issue/1"><p>Issue #1</p></a>
            </li>
            <li>
                <a href="/issue/2"><img src="/media/images/covers/02.jpg" ></a>
                <a href="/issue/2"><p>Issue #2</p></a>
            </li>
        </ul>
    </div>
</div>

这是我想看到的:

<div class="issues">
    <h3>Issues</h3>
    <div>
        <a href="/series/1">Series 1</a>
        <ul>
            <li>
                <a href="/issue/1"><img src="/media/images/covers/01.jpg" ></a>
                <a href="/issue/1"><p>Issue #1</p></a>
            </li>
            <li>
                <a href="/issue/2"><img src="/media/images/covers/02.jpg" ></a>
                <a href="/issue/2"><p>Issue #2</p></a>
            </li>
        </ul>
    </div>
</div>

我对模板进行了相当多的研究,但我没有看到一种基于不同值获取列表的方法。我还尝试在我的 Character 或 Issue 模型中创建一个新集合,我可以用它来替换 issue_set.all,但我还没有让它工作。

编辑:根据 marcusshep 的要求,Character 视图使用通用的 DetailView:

class CharacterView(generic.DetailView):
    model = Character
    template_name = 'app/character.html'

【问题讨论】:

我可以看到您将character 查询集传递给模板的视图吗? @marcusshep,更新了视图代码。 所以,要明确一点,您希望您的模板仅显示具有不同名称的字符正确吗? @marcusshep 对不起,我想显示角色所在的问题,按系列排序。 【参考方案1】:

我会使用基于函数的视图而不是基于类的通用视图。原因是您所需的行为超出了一般性。

在您的函数中,您可以构建您想要的查询集,而不必与generic.DetailView 提供的查询集进行斗争。

def my_view(request, *args, **kwargs):
    character = Character.objects.get(id=request.GET.get("id", None))
    issues = character.issue_set.all().order_by("series__name")
    return render(request, 'app/character.html', "issues": issues)

或者,您可以使用已有的并覆盖DetailView's get_queryset() method。

class CharacterView(generic.DetailView):
    model = Character
    template_name = 'app/character.html'

    def get_queryset():
        # return correct queryset

但最大的问题是需要使用这个集合的方面会更多。例如,我将添加 Creators、Story Arc 等。他们将拥有自己的页面,并且需要显示相关问题,并按系列排序。如果有一个解决方案可以被这些模板中的任何一个使用而无需太多代码重用,那就太好了。

这是所有编程领域中非常常见的问题。解决此问题的一种非常简单的方法是将逻辑隔离在一个函数中,并在需要时调用该函数。

def my_issues_query():
    # find the objects you need

def my_view(request, *args, **kwargs):
    issues = my_issues_query()

您还可以利用 python 的装饰器功能。 (这是我最喜欢的方法。)

def has_issues(view_function):
    def get_issues(request, *args, **kwargs):
        # find all the issues you need here
        # you'll only need to write this logic once.
        issues = Issues.objects.filter(...)
        return issues
    return get_issues

@has_issues
def my_view(request, *args, **kwargs):
    # this functions namespace now contains
    # the variable `issues`.
    # which allows for the use of the query ie.
    return render(
        request, 
        "my_templates/template.html", 
        "issues":issue
    )

【讨论】:

最大的问题是需要使用这个集合的方面会更多。例如,我将添加 Creators、Story Arc 等。他们将拥有自己的页面,并且需要显示相关问题,并按系列排序。如果有一个可以被任何这些模板使用而无需大量代码重用的解决方案,那就太好了。 是的,只需编写一个函数并在需要的地方调用它。查看更新的答案。 这不是只将Issues 对象的结果传递给没有Character 对象的模板吗?也许我的头脑还没有完全成熟,但在我看来,你会给模板Character 作为主要对象,然后通过Character 过滤拉入Issues 对象。我对 Django 很陌生,所以我很诚实地问!感谢您迄今为止的所有帮助! 您的查询可以是您需要的任何内容。那部分取决于你和摆弄。您可以在一个函数中定义两个或多个查询并将它们全部返回:return issues, characters, anything_else_i_need 我没有意识到你可以返回多个对象!所以我可以这样做吗?:return render(request, "app/template.html", "character": character, "issues": issues

以上是关于通过不同的外键进行循环迭代的 Django 模板的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Django 模板中的外键访问值

如何使唯一 = true 的外键在 Django 中成为“可选”?

如何遍历 Django 字典中的外键?

姜戈。从表单对象访问模板中的外键字段

使用 django 查询集访问不同的外键关系

最初在 django 内联表单集中设置不同的外键值