Официальная рекомендуемая поза Джанго: представление класса

Python Django
Официальная рекомендуемая поза Джанго: представление класса

автор:HelloGitHub-Dream Chaser

Пример кода, задействованный в этой статье, был синхронно обновлен доРепозиторий HelloGitHub-Team

В процессе разработки веб-сайта, хотя некоторые функции представления имеют дело с разными объектами, общая логика их кода одинакова. Например, блог и форум обычно отображают серию списков статей или списков сообщений на главной странице. Для функции просмотра, которая обрабатывает домашнюю страницу, хотя одним из обрабатываемых объектов является статья, а другим — пост, процесс его обработки очень похож: сначала список статей или постов извлекается из базы данных, а затем данные передаются в шаблон и отображают шаблон. Поэтому django извлек те же самые логические коды и написал ряд функций общего представления, а именно представление на основе общего класса.

Использование представлений классов является рекомендуемой практикой django.После того, как вы ознакомитесь с использованием представлений классов, вы сможете сократить повторяющийся код функций представления и сэкономить время разработки. Далее давайте изменим функцию представления в приложении блога на общее представление на основе классов.

ListView

В нашем блог-приложении есть несколько функций просмотра, которые получают список статей (Post) из базы данных:

blog/views.py

def index(request):
    # ...
    
def archive(request, year, month):
    # ...
    
def category(request, pk):
    # ...
    
def tag(request, pk):
    # ...

Все эти функции просмотра получают список статей (Post) из базы данных, разница лишь в том, что список получаемых статей может быть другим. НапримерindexПолучить список всех статей,categoryПолучить список статей в категории.

Перепишите функцию просмотра индекса в представление класса.

Для такого вида представления, которое получает данные списка моделей (например, список постов здесь) из базы данных, Django предоставляет специальныйListViewвид класса. Давайте посмотрим на примерListViewспособ использования. Мы сначала ставимindexФункция просмотра преобразована в функцию просмотра класса.

blog/views.py

from django.views.generic import ListView

class IndexView(ListView):
    model = Post
    template_name = 'blog/index.html'
    context_object_name = 'post_list'

要写一个类视图,首先需要继承 django 提供的某个类视图。至于继承哪个类视图,需要根据你的视图功能而定。 как здесьIndexViewФункция состоит в том, чтобы получить список статей (Post) из базы данных,ListViewЭто получение определенных данных списка моделей из базы данных, поэтомуIndexViewнаследоватьListView.

Затем через некоторые атрибуты указать, что должна делать эта функция представления Здесь мы указываем три атрибута:

  • модель: указать модель какPost, сообщая django, что модель, которую я хочу получить,Post.
  • template_name: задает шаблон для рендеринга этого представления.
  • context_object_name: указывает имя переменной для сохранения полученных данных списка моделей, эта переменная будет передана в шаблон.

Если это все еще немного сложно понять, вы можете также объединить код представления класса сindexСравните код функции просмотра:

blog/views.py

def index(request):
    post_list = Post.objects.all()
    return render(request, 'blog/index.html', context={'post_list': post_list})

indexСначала функция просмотра проходитPost.objects.all()Получите данные списка статей (Post) из базы данных и сохраните их вpost_listв переменной. И в классе посмотреть на этот процессListViewУже сделал это за нас. мы просто говоримListViewМодель, полученная из базы данных,Post, вместоCommentили любую другую модель, т.е. указаниеmodel = Post. Сохраните полученный список данных модели вpost_list, т.е. указатьcontext_object_name = 'post_list'. Затем визуализируйте файл шаблона blog/index.html,indexИспользовать функцию просмотраrenderфункция. Но этот процессListViewЭто уже сделано за нас, нам просто нужно указать, какой шаблон отображать.

Следующим шагом является преобразование представления класса в представление функции. Зачем вам нужно преобразовать представление класса в представление функции?

Давайте посмотрим на конфигурацию URL для блога:

blog/urls.py

app_name = 'blog'
urlpatterns = [
    path('', views.index, name='index'),
    ...
]

Как упоминалось ранее, каждый URL-адрес соответствует функции представления, поэтому, когда пользователь посещает URL-адрес, Django знает, какую функцию представления вызывать для обработки запроса. Шаблоны URL-адресов настраиваются в Django черезurlФункция привязывает URL к функции просмотра. Напримерpath('', views.index, name='index'), первый параметр которого — это шаблон URL, а второй — функция просмотра.index. правильноurlДля функций значение, переданное в качестве второго параметра, должно быть функцией. а такжеIndexViewявляется классом и не может быть заменен напрямуюindexфункция. К счастью, преобразовать представление класса в представление функции очень просто, просто вызовите функцию представления класса.as_view()метод (что касаетсяas_viewКак именно метод преобразует класс в функцию, в настоящее время не нужно беспокоиться, его просто нужно вызвать при настройке шаблона URL.as_viewметод подойдет. Для конкретной реализации мы разработаем колонку для анализа исходного кода представления классов в будущем, и тогда мы сможем увидеть магию, используемую django).

Теперь введите конфигурацию URLindexзаменить представление представлением классаIndexView:

blog/urls.py

app_name = 'blog'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    ...
]

Посетите домашнюю страницу, вы можете видеть, что на домашней странице по-прежнему отображается список всех статей, и используйте функцию просмотра.indexЭффект точно такой же.

Перепишите функцию представления категории в представление класса

categoryФункцией просмотра также является получение данных списка статей из базы данных, но ее иindexОтличие функции просмотра в том, что она получает все статьи определенной категории. следовательноcategoryВ функции просмотра есть еще один шаг, то есть сначала вам нужно получить категорию из базы данных на основе идентификатора категории, полученного из URL-адреса, а затем использоватьfilterФункция отфильтровывает все статьи в этой категории. Давайте посмотрим, как должно быть написано представление класса в этом случае:

blog/views.py

class CategoryView(ListView):
    model = Post
    template_name = 'blog/index.html'
    context_object_name = 'post_list'

    def get_queryset(self):
        cate = get_object_or_404(Category, pk=self.kwargs.get('pk'))
        return super(CategoryView, self).get_queryset().filter(category=cate)

а такжеIndexViewРазница в том, что мы переопределяем родительский классget_querysetметод. Этот метод по умолчанию получает все данные списка указанной модели. Чтобы получить данные списка статей в указанной категории, мы переопределяем этот метод и меняем его поведение по умолчанию.

Во-первых, получить категорию на основе идентификатора категории (также известного как pk), полученного из URL-адреса, который аналогиченcategoryПроцесс в функции просмотра такой же. Обратите внимание, однако, что в представлении класса значение параметра пути, полученное из URL-адреса, сохраняется в экземпляреkwargsВ атрибуте (который является словарем) значения параметров, не являющихся путями, хранятся в экземпляреargsсвойства (которые представляют собой список). Итак, мы сделалиself.kwargs.get('pk')чтобы получить значение идентификатора категории, полученное из URL-адреса. Затем мы вызываем родительский классget_querysetМетод получает список всех статей, а затем вызывает возвращенный результат.filterметод для фильтрации всех статей в этой категории и возврата.

Кроме того, мы можем видетьCategoryViewзначения свойств, указанные в классе иIndexViewточно такие же, поэтому, если вы хотите сохранить код дальше, вы даже можете напрямую наследоватьIndexView:

class CategoryView(IndexView):
    def get_queryset(self):
        cate = get_object_or_404(Category, pk=self.kwargs.get('pk'))
        return super(CategoryView, self).get_queryset().filter(category=cate)

Затем в конфигурации URL поместитеcategoryзаменить представление представлением классаCategoryView:

blog/urls.py

app_name = 'blog'
urlpatterns = [
    ...
    path('categories/<int:pk>/', views.CategoryView.as_view(), name='category'),
]

Посетите одну из следующих страниц категорий, вы увидите, что список всех статей в категории по-прежнему отображается, и используйте функцию просмотра.categoryЭффект точно такой же.

Перепишите функции представления архива и тегов в представления класса.

Здесь не о чем говорить, применяйте то, чему вы научились, и оставьте задачу вам.

DetailView

Помимо получения данных списка моделей из базы данных, также общим требованием является получение данных одной записи модели из базы данных. Например, для просмотра сведений о статье необходимо получить запись этой статьи из базы данных, а затем отобразить шаблон. Для этого типа требований django предоставляетDetailViewвид класса. Далее мы будемdetailФункция представления преобразуется в эквивалентное представление классаPostDetailView, код показан ниже:

blog/views.py

from django.views.generic import ListView, DetailView

# 记得在顶部导入 DetailView
class PostDetailView(DetailView):
    # 这些属性的含义和 ListView 是一样的
    model = Post
    template_name = 'blog/detail.html'
    context_object_name = 'post'

    def get(self, request, *args, **kwargs):
        # 覆写 get 方法的目的是因为每当文章被访问一次,就得将文章阅读量 +1
        # get 方法返回的是一个 HttpResponse 实例
        # 之所以需要先调用父类的 get 方法,是因为只有当 get 方法被调用后,
        # 才有 self.object 属性,其值为 Post 模型实例,即被访问的文章 post
        response = super(PostDetailView, self).get(request, *args, **kwargs)

        # 将文章阅读量 +1
        # 注意 self.object 的值就是被访问的文章 post
        self.object.increase_views()

        # 视图必须返回一个 HttpResponse 对象
        return response

    def get_object(self, queryset=None):
        # 覆写 get_object 方法的目的是因为需要对 post 的 body 值进行渲染
        post = super().get_object(queryset=None)
        md = markdown.Markdown(extensions=[
            'markdown.extensions.extra',
            'markdown.extensions.codehilite',
            # 记得在顶部引入 TocExtension 和 slugify
            TocExtension(slugify=slugify),
        ])
        post.body = md.convert(post.body)

        m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)
        post.toc = m.group(1) if m is not None else ''

        return post

PostDetailViewНемного сложнее, в основном эквивалентноdetailФункция просмотра по своей сути сложнее, давайте сравним ее пошагово.detailОбъясняется код в функции просмотра.

Сначала мыPostDetailViewВ классе указываются значения некоторых свойств, смысл этих свойств иListViewТо же самое в , и здесь повторяться не буду.

Затем мы перезаписываемgetметод. Это соответствуетdetailЧасть кода в функции просмотра, добавляющая 1 к чтению поста. На самом деле можно просто поставитьgetвызов метода какdetailПросмотр вызова функции.

Потом мы воспроизвелиget_objectметод. Это соответствуетdetailВ функции просмотра статья получается по id статьи (то есть pk), а затем выполняется кодовая часть парсинга Markdown над post.body статьи.

Вас может смутить такое количество методов, для простоты понимания можно просто поставитьgetметод какdetailфункции просмотра, как и для других, таких какget_object,get_context_dataявляются вспомогательными методами, которые заканчиваются вgetвызываются методы, причина, по которой вы не видите, что они вызываются здесь, заключается в том, что они неявноsuper(PostDetailView, self).get(request, *args, **kwargs)родительский классgetвызов метода. Окончательный HTTP-ответ, передаваемый браузеру,getметод возвращенHttpResponseобъект.

Все еще не можешь понять? Я могу сказать так много, не прибегая к исходному коду. Если вы хотите освоить и использовать class view гибко, вы должны внимательно прочитать исходный код class view.Я тоже долго выгрыз исходный код.В будущем я разработаю специальный анализ исходного кода представления класса, и тогда у вас будет более глубокое понимание представления класса. Кроме того, вот объяснение представления класса в официальном документе django, хотя я думаю, что эта часть документа не очень ясна в отношении представления класса, но она также заслуживает ссылки.Обзор представлений на основе классов.

Классовое представление деталей статьи также было написано, аналогично нужно настроить его в urls.py, чтобы преобразовать исходное функциональное представление.detailПерейдите в режим просмотра классов, я думаю, вы уже должны знать, как это сделать.

После настройки представления страницы сведений посетите сведения о статье, и вы увидите, что результат, возвращаемый страницей, точно такой же, как и представление функции.На этом этапе представление класса было преобразовано. Поскольку представление класса и представление функции полностью эквивалентны, а представление класса имеет много преимуществ, таких как повторное использование кода, поэтому, как только представление будет задействовано в будущем, мы будем использовать представление класса для его реализации.


«Объяснение серии проектов с открытым исходным кодом»——Пусть больше не боятся люди, интересующиеся проектами с открытым исходным кодом, и пусть инициаторы проектов с открытым исходным кодом больше не остаются в одиночестве. Следите за нашими статьями, и вы откроете для себя радости программирования, насколько легко им пользоваться, и узнаете, как легко участвовать в проектах с открытым исходным кодом. Добро пожаловать, чтобы оставить сообщение, чтобы связаться с нами, присоединиться к нам, позволить большему количеству людей влюбиться в открытый исходный код и внести свой вклад в открытый исходный код~