Django: наследование видов при создании сайта

30 марта 2011 г.

Итак, напомню, что виды в Django — основное средство формирования содержимого веб-страниц, которые представляются в виде обычных функций языка Python. Сейчас не буду вдаваться в подробности и описывать принципы работы видов, можно посмотреть официальную документацию (http://docs.djangoproject.com/en/1.3/topics/http/views/).

Допустим, стоит задача создать сайт, каждая страница которого состоит из двух частей: общей части (одинаково выглядит для всех страниц) и специальной части (разная для каждой страницы). То есть каждая специальная часть будет иметь собственный вид, который позаботится о выводе информации.

Первое, что может прийти в голову — воспользоваться средством наследования шаблона, которое описано в официальной документации Django (шаблон — это разметка страницы плюс специальные теги шаблонизатора Django).

В этом случае у Вас имеется общий шаблон, от которого нужно будет унаследовать шаблоны Ваших страниц.

Приведём для большей наглядности пример.

Вот так может выглядеть базовый шаблон:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 />
 {% block title %}Мой замечательный сайт{% endblock %}


  {% block sidebar %}
 <ul>
<li><a href="/">Главная</a></li>
<li><a href="/freebsd">FreeBSD</a></li>
</ul>
 {% endblock %}
 

 {% block content %}{% endblock %}
 

А так — один из шаблонов-наследников (страница FreeBSD):

{% extends "base.html" %}

{% block title %}Мой замечательный блог FreeBSD{% endblock %}

{% block content %}
{% for entry in blog_entries %}
 <h2>{{ entry.title }}</h2>
 {{ entry.body }}
{% endfor %}
{% endblock %}

Шаблон был унаследован с помощью тега extends.

Какой явный недостаток предлагаемого метода? В примере базовому шаблону не передаётся параметров из вида. Причина проста — Вашему шаблону-родителю ничего не известно об упомянутых параметрах. Вида, который бы эти параметры ему передал — попросту нет. Конечно же, можно передавать эти параметры из вида, который привязан к шаблону-наследнику, но это не удобно. Придётся одни и те же параметры передавать из всех видов, ведь страниц-то может быть не одна. Это нарушило бы главный принцип платформы Django «DRY» («Don’t Repeat Yourself» — проще говоря, «не повторяйся»).

Предлагается следующее решение данной проблемы.

  • Создаём родительский вид, родительский шаблон привязываем к нему.
  • Родительский вид будет представлен не функцией, а классом.
  • Дочерний вид также будет представлен классом, который наследует класс родительского вида. Кроме того, в дочернем виде будет в наличии входная функция, возвращающая контент страницы, которую можно будет прописать в обычном порядке в файле urls.py.

Вот пример данных классов.

Родительский:

from django.shortcuts import render_to_response

from django.template import Context, Template
from django.template.loader import get_template

class BaseParentView:
  def __init__( self ):
    self._baseViewTemplate = "parent.html"

  def _renderChildTemplate( self, **params ):
    template = get_template( self._childTemplate )
    return template.render( Context( params ) )

  def getView( self ):
    text = "Текст из родительского вида, который попадёт в заголовок страницы"
    return render_to_response( self._baseViewTemplate, { 'child_content': self._childContent, 'text': text } )

И сответствующий шаблон:
{{ text }}
{{ child_content }}

Дочерний:

from base_pass.parent.views import BaseParentView

class ChildView( BaseParentView ):
  def __init__( self ):
    BaseParentView.__init__( self )
    self._childTemplate = "child.html"

  def getView( self ):
    text = u"Текст из дочернего вида, который будет размещаться в теле страницы"
    self._childContent = self._renderChildTemplate( content = text )
    return BaseParentView.getView( self )

# Та самая функция, которая вернёт контент страницы на основе дочернего
# класса
def child( request ):
  child = ChildView()
   return child.getView()

И дочерний шаблон:
{{ content }}

Функция, возвращающия контент в каждом классе, имеет имя getView, а соответствующие шаблоны прописаны в членах классов _baseViewTemplate и _childTemplate.

Таким образом, мы можем создавать дочерные классы и соответствующие шаблоны для каждой страницы Вашего сайта, а о передаче параметров в общую часть позаботится наш родительский класс.

При этом мы не использовали предлагаемый платформой Django тег наследования шаблонов extends.

Вы можете попробовать данный подход, создав тестовый проект Django. Не забудьте прописать в urls.py дочернего вида кортеж для функции child.

Теги: рубрика Сайтостроение