对于 Django 模板 %url% 标签,我如何从通用视图上下文中找到所有 URL 参数,而不仅仅是最后一个?

Posted

技术标签:

【中文标题】对于 Django 模板 %url% 标签,我如何从通用视图上下文中找到所有 URL 参数,而不仅仅是最后一个?【英文标题】:For the Django template %url% tag, how do I find all URL arguments from generic view contexts, not just the last?对于 Django 模板 %url% 标签,我如何从通用视图上下文中找到所有 URL 参数,而不仅仅是最后一个? 【发布时间】:2018-08-15 20:23:43 【问题描述】:

在 Django 中,我正在尝试制作一个具有模型树层次结构的应用程序,即 Books 包含 Chapters 包含 Sections 等。我想要我的 URL 以反映结构,例如 books/3/chapters/5/sections

我想使用基于类的通用视图。在我的 html 模板中,我将使用 % url % 命令来指定链接目标。

对于结构深处的 URL,我需要向 % url % 提供 URL 中出现的所有键。对于上面的例子:% url 'pattern-name' 3 5 %

但是,通用的基于类的视图只提供(如果有的话)他们在模板上下文中关注的对象的主键(即上面的:5)。

如何检索父对象的外键(即上面的:3)?

我有以下几点:

我的模型在 models.py:

class Book(models.Model):
  name = models.CharField("Book Name", max_length = 80)

class Chapter(models.Model):
  name = models.CharField("Book Name", max_length = 80)
  book = models.ForeignKey('Book', on_delete = models.CASCADE)

我在 urls.py:

中的 URL 模式
urlpatterns = [
  path('books/', 
       views.BookListView.as_view(), name='book_list'),
  path('books/<int:book>/', 
       views.BookDetailView.as_view(), name='book_detail'),
  path('books/<int:book>/chapters/', 
       views.ChapterListView.as_view(), name='chapter_list'),
  path('books/<int:book>/chapters/<int:chapter>/',
       views.ChapterDetailView.as_view(), name='chapter_detail'),
  path('books/<int:book>/chapters/create/',
       views.ChapterCreateView.as_view(), name='chapter_create'),

views.py 中的我的观点:

class BookListView(generic.ListView):
  model = 'Book'

class BookDetailView(generic.DetailView):
  model = 'Book'
  pk_url_kwarg = 'book'

class ChapterListView(generic.ListView):
  model = 'Chapter'
  def get_queryset(self):
    return Chapter.objects.filter(book = self.kwargs['book'])

class ChapterDetailView(generic.DetailView):
  model = 'Chapter'
  pk_url_kwarg = 'chapter'

class ChapterCreateView(generic.CreateView):
  model = 'Chapter'
  fields = ['name', 'book']
  def get_initial(self):
    initial = super().get_initial().copy()
    initial['book'] = self.kwargs.['book']
    return initial;

在用于章节列表的 HTML 模板中,我想要一个链接来创建新章节。目前,该模板如下所示:

模板chapter_list.html:

<ul>
% for chapter in chapter_list %
  <li> chapter.name </li>
% endfor %
</ul>
<a href="% url 'chapter_create' 42 %">Add new chapter</a>

显然,我不想将所有新章节添加到 ID 为 42 的书中,而是想使用我所在的章节列表中的书籍 ID。例如,对于一个 URL

.../books/17/chapters/create/

我想让链接指向:

<a href="% url 'chapter_create' 17 %">Add new chapter</a>

我发现动态执行此操作的唯一方法是向章节列表视图添加额外的上下文:

更新了 views.py:

...

class ChapterListView(generic.ListView):
  model = 'Chapter'
  def get_queryset(self):
    return Chapter.objects.filter(book = self.kwargs['book'])

  # Additional Context
  def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['my_current_book_id'] = self.kwargs['book']
    return context

...

然后让模板说:

更新chapter_list.html:

<ul>
% for chapter in chapter_list %
  <li> chapter.name </li>
% endfor %
</ul>
<a href="% url 'chapter_create' my_current_book_id %">Add new chapter</a>

这是我的问题:

没有更简单(即更多 Django-ish 内置)的方法来做到这一点吗?

在我看来,这一定是一项非常普通的任务,而且由于通用视图类和 URL(反向)查找的精美而精细的设计,我一定遗漏了一些东西。

【问题讨论】:

【参考方案1】:

我会考虑为book_detailchapter_detail 定义get_absolute_url,并为chapter_create 等其他URL 定义额外的方法:

class Book(models.Model):
    name = models.CharField("Book Name", max_length = 80)

    def get_absolute_url(self):
        return reverse('book_detail', args=[self.pk])

    def get_create_chapter_url(self):
        return reverse('chapter_create', args=[self.pk])


class Chapter(models.Model):
    name = models.CharField("Book Name", max_length = 80)
    book = models.ForeignKey('Book', on_delete = models.CASCADE)

    def get_absolute_url(self):
        return reverse('chapter_detail', args=[self.book_pk, self.pk])

然后在模板中,你可以使用 chapter.get_absolute_url book.get_absolute_url chapter.book.get_create_chapter_url 等等,你不用担心url里面有什么参数。

在章节详细视图中,您可以使用外键访问Book 模型上的方法:

 chapter.book.get_absolute_url 

在章节列表视图中,您需要将书籍添加到上下文中(以防书籍中没有章节):

from django.shortcuts import get_object_or_404

class ChapterListView(generic.ListView):
    model = 'Chapter'

    def get_queryset(self):
        return Chapter.objects.filter(book = self.kwargs['book'])

  def get_context_data(self, **kwargs):
      context = super().get_context_data(**kwargs)
      context['book'] = get_object_or_404(pk=self.kwargs['book'])
      return context

然后你可以在上下文中访问 book

或者您可以使用DetailView 来查看章节列表视图,使用Book 模型:

class ChapterListView(generic.DetailView):
    model = 'Book'
    pk_url_kwarg = 'book'

那么book已经在模板上下文中,你可以通过以下方式循环浏览相关章节:

% for chapter in book.chapter_set.all %
   chapter 
% endfor %

顺便说一句,在创建视图中省略book 字段可能更简单。然后在保存表单之前在form_valid方法中设置。

class ChapterCreateView(generic.CreateView):
    model = 'Chapter'
    fields = ['name']

    def form_valid(self, form):
        book = get_object_or_404(Book, pk=self.kwargs['book'])
        form.instance.book = book
        return super(ChapterCreateView, self).form_valid(form)

【讨论】:

非常有趣!我现在已经为章节和书籍实现了get_absolute_url(),主要是为匹配的CreateViewEditView 专业提供success_urls。我喜欢为图书模型提供get_create_chapter_url() 的想法。但是,我如何从chapter_list.html 模板上下文中访问它,我所拥有的只是一个(可能是空的)章节列表chapter_list 您只需要确保book 在模板上下文中。我添加了更多示例。 我现在用过这个解决方案,效果很好!此外,关于form_valid() 的旁白对我来说很有价值(我对其进行了编辑以纠正一些小的语法错误)。以前我不确定如何将图书 ID 保留在向用户显示的表单之外。【参考方案2】:

您可以使用Book 作为模型来查看视图,并且:

% for chapter in book.chapter_set.all %

【讨论】:

关于第一个解决方案:如果我在chapter_list.html,我只有一个可用的列表对象chapter_list,它可能是空的。所以可能没有章节可以研究。 关于第二个解决方案,如果我有一个较深的层次结构A/B/C/D,这不会导致我只有A 作为模型的视图和像@ 这样的数据库访问的情况987654327@ 在模板中? 啊,我明白了,我在第一个解决方案中没有想到这个问题,而且我想错了,实际上,我认为这不是一个好的解决方案。关于第二个选项,我的建议是针对这个特定的视图,这并不意味着您的所有视图都必须是这样的(而且它不会嵌套,那是行不通的)。也就是说,我对您的解决方案没有任何问题。无论哪种方式,您都必须将书籍 ID 获取到模板中。使用 Book 作为模型强调这是一本书的视图,而不仅仅是章节列表。

以上是关于对于 Django 模板 %url% 标签,我如何从通用视图上下文中找到所有 URL 参数,而不仅仅是最后一个?的主要内容,如果未能解决你的问题,请参考以下文章

使用带有特殊字符的 Django 的 url 模板标签

如何替换重新“。”在 Django 模板 url 标签中

使用 django 模板标签修改 url

在视图而不是模板中生成带有 Django 静态 url 的图像标签

如何将文字字符串值传递给 django url 模板标签

django'url'模板标签错误