对于 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 中,我正在尝试制作一个具有模型树层次结构的应用程序,即 Books 包含 Chapters 包含 Sections 等。我希望我的 URLs 反映结构,例如 books/3/chapters/5/sections.

我想使用基于 class 的通用视图。在我的 HTML 模板中,我将使用 {% url %} 命令指定 link 个目标。

对于结构深处的 URLs,我需要提供 {% url %} [=] 中出现的所有键105=]。对于上面的例子: {% url 'pattern-name' 3 5 %}

但是,基于通用 class 的视图仅提供(如果有的话)它们在模板上下文中所关注的对象的主键(即上面的: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-用于章节列表的模板中,我想要一个link来创建一个新章节。目前,该模板如下所示:

模板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/

我想让 link 指向:

<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 的内置)方法来做到这一点吗?

在我看来,这一定是一项非常普通的任务,并且具有通用视图 classes 和 URL(反向)查找的美观和细粒度设计,我一定是漏掉了什么。

您可以使用 Book 作为模型和:

{% for chapter in book.chapter_set.all %}

我会考虑为 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)