Django: первые страницы

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

Далее предполагается, что у читателя установлен Python3 и Django >= 1.5: именно на них рассчитаны наши последующие телодвижения. Устанавливать необходимые для конкретного проекта приложения лучше используя virtualenw и pip. Так вы сможете избавить себя от чтения руководств по установке и захламления системных файлов.

Создание проекта и приложения

Проект — основной каталог, где будут располагаться те или иные приложения, также проект содержит основные настройки будущего сайта. Создадим корневой каталог для проекта и сам проект:

$ mkdir djcode && cd djcode
$ django-admin.py startproject pySite

Просмортим появившиеся каталоги:

$ tree pySite
pySite
├── manage.py
└── pySite
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

Вкратце:

manage.py — отвечает за управление проектом, например, командой python manage.py runserver можно запустить сервер для тестовой разработки.

__init__.py — нужен для того, чтобы python рассматривал каталог pySite как пакет.

settings.py — файл настроек проекта: настройки языка, часового пояса, соединения с базой данных, подключаемых приложений и прочего.

urls.py — настройки URL-адресов для сайта

wsgi.py — конфигурация модуля wsgi для веб-сервера

Хорошо, когда проект расширяемый. Ещё лучше, если его легко сопровождать. Оба этих условия выполняются в Django. Чтобы расширить функционал сайта, следует создать для него какое-либо приложение: галерею, форум, блоги. Затем лишь подключить его в settings.py и подправить свой urls.py, дабы при переходе по определённому адресу пользователь перенаправлялся к страницам приложения. Поэтому давайте создадим какое-нибудь приложение. Пусть оно отвечает за публикацию новостей и хранится в том же каталоге, что и основной проект.

$ django-admin.py startapp news
$ tree pySite
pySite
|-- pySite
|   |-- __init__.py
|   |-- settings.py
|   |-- urls.py
|   |-- views.py
|   |-- wsgi.py
|-- manage.py
|-- news
|   |-- admin.py
|   |-- __init__.py
|   |-- models.py
|   |-- tests.py
|   |-- urls.py
|   |-- views.py

Вкратце:

models.py — файл для взаимодействия с базой данных

urls.py — отдельный файл url'ов. Он может быть пустым, если вы решите использовать для всех адресов urls.py проекта

admin.py — создаётся вручную, нужен исключительно для удобства: настройка отображения моделей в административной панели

views.py — самое интересное, управляет логикой приложения

С приложением определились. Отлично! Пора браться за настройки.

Настройки для сайта

Заглянем в файл settings.py и настроим его под свои скромные пока нужды.

PROJECT_PATH — указание на то, где Django нужно искать корень проекта. Лучше использовать запись, приведённую ниже, чем писать полный путь вроде ~/home/user/djcode/pySite.

DEBUG = True — отладка влючена, Django будет выводить информативные сообщения об ошибках.

DATABASES — настройки для вашей базы данных. В примере это sqlite3, который не требует особого к себе отношения, но и полноценной базой данных для сайта на production-сервере быть не может. Чтобы использовать sqlite3, достаточно создать в директории проекта файл sqlite.db.

# settings.py
from os.path import abspath, join, dirname

PROJECT_PATH = abspath(join(dirname(__file__), '..'))
DEBUG = True
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME':  join(PROJECT_PATH, 'sqlite.db'),
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}

Идём дальше. Желательно сразу создать и подключить директорию, где будет храниться статика: css, javascript и изображения. Кроме того, нам понадобится директория под наши html-файлы.

STATIC_URL — относительный url для статики

STATICFILES_DIRS — директория, где расположены соответствующие файлы

TEMPLATE_LOADERS — модули для работы с шаблонами

TEMPLATE_DIRS — указание на директорию, где лежат html-шаблоны

ROOT_URLCONF — главный файл url'ов

INSTALLED_APPS — подключенные приложения, вниз добавим наш news.

# settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = (join(PROJECT_PATH, 'static'),)
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

ROOT_URLCONF = 'pySite.urls'
TEMPLATE_DIRS = (
    join(PROJECT_PATH, 'templates'),
)

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'news',
)

Теперь можно проверить сайт на работоспособность. Для этого, находясь в директории с manage.py следует ввести команду:

$ python manage.py syncdb
$ python manage.py runserver

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

По адресу 127.0.0.1:8000/ должно появиться стандартное окно приветствия Django.

Создание модели и представления, конфигурация urls.py

Следующим шагом будет настройка моделей для взаимодействия с БД, создание представления (логика приложения), настройка файла urls.py.

В примере ниже создаётся класс Post, подкласс абстрактного класса models.Model. Мы хотим видеть у нашего поста название (title), его тело (body), дату создания (timestamp) и автора (author). Обратите внимание на models.*Field. Каждая модель соответствует указанному полю.

Так, models.CharField выведет несколько символов, максимальное их значение 150, этого должно хватить для названия;

models.TextField позволит вводить неограниченное число символов: полноценный текст;

models.ForeignKey указывает на то, что у поста может быть один автор.

Эти таблицы будут созданы в базе данных при синхронизации.

# ~/djcode/pySite/news/models.py

from django.db import models
from django.contrib import admin
from django.contrib.auth.models import User

class Post(models.Model):
    title = models.CharField(max_length=150)
    body = models.TextField()
    timestamp = models.DateTimeField()
    author = models.ForeignKey(User)

    def __unicode__(self):
        return self.title
        return self.body

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

# djcode/pySite/news/views.py

from django.shortcuts import get_object_or_404, render_to_response
from django.template import RequestContext
from .models import Post

def news(request):
    ''' Show all news '''
    posts = Post.objects.all().order_by('-timestamp')
    return render_to_response('news/news.html', {'posts':posts})

Это простейшее представление, которое принимает все существующие посты и выводит их на определённой странице. Чуть ниже мы создадим эту страницу. Но сначала наполним сайт каким-нибудь содержимым. Административная панель уже подключена в нашем INSTALLED_APPS, осталось только зайти и проверить как отображаются созданные нами модели. Для отображения их в админке создадим ещё один файл (можно вписать это и в предыдущем файле views, но разделить их иногда удобнее):

# djcode/pySite/news/admin.py

from django.contrib import admin
from .models import Post

class PostAdmin(admin.ModelAdmin):
    models = Post
    list_display = ('title', 'timestamp')
    list_filter = ('timestamp',)

admin.site.register(Post, PostAdmin)

Раскомментируйте в ~/djcode/pySite/pySite/urls.py строки:

# djcode/pySite/pySite/urls.py

from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()


urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),

)

После того, как вы зайдёте по адресу 127.0.0.1:8000/admin/ и создадите несколько постов, вы должны увидеть нечто подобное:

админка django

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

Так будет выглядеть шаблон базовой страницы:

{# djcode/pySite/templates/base.html #}

{% load static from staticfiles %} <!-- подгружаем статику -->
{% load i18n  %}

<!DOCTYPE HTML>
<html>
<head>
{% block meta %}
    <!-- подключаем наш css-файл, если он есть -->
    <link rel="stylesheet" type="text/css" href="{% static "css/base.css" %}" />
{% endblock %}
<title>{% block title %}My Django Site{% endblock %}</title>

</head>

<body>
    {% block header %}
        {% include "header.html" %} <!-- подключаем «шапку» сайта -->
    {% endblock %}

    <div id="content">
        {% block content %}{% endblock %} <!-- место для контента -->
    </div>

    {% block footer %} <!-- подключаем «подвал» сайта -->
        {% include "footer.html" %}
    {% endblock %}
</body>
</html>

Теперь примемся за шаблон, который выведет все наши посты.

{# djcode/pySite/templates/news/news.html #}

{% extends 'base.html' %} <!-- подключаем базовый шаблон -->

{% block title %}News{% endblock %}

{% block content %}
    <div id="post">
        {% for post in posts %} <!-- итерация по всем постам -->
            <dl>
              <dd>
                <h3>
                    <a href="{% url 'news:one_new' post.id %}">{{ post.title }}</a>
                </h3>
              </dd>
            </dl>

            <!-- дата публикации и автор -->
            {{ post.timestamp }}
            {{ post.author }}

            <!-- обрезаем посты, вывод по 80 слов -->
            {{ post.body|truncatewords:80|safe }}

            <!-- ссылаемся на полный текст поста -->
            <a href="{% url 'news:one_new' post.id %}">Читать полностью&raquo;</a>
        {% endfor %}
    </div>
{% endblock %}

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

# djcode/pySite/news/views.py

# после функции news дописать
def one_new(request, post_id):
    ''' Show single news'''
    post = get_object_or_404(Post, pk=post_id)

    vars = dict(
        title=post.title,
        body=post.body,
        author=post.author,
        timestamp=post.timestamp,
    )

    return render_to_response('news/one_new.html', vars, context_instance=RequestContext(request))

Переменной post передаётся объект Post с его ID либо вызывается исключение 404: страница не найдена. Определяется то, что должно быть выведено: название, автор, время создания. Возвращается страница one_new.html, которой мы сейчас и займёмся.

{# djcode/pySite/templates/news/one_new.html #}

{% extends 'base.html' %}

<!-- отображать название поста как title страницы-->
{% block title %}{{ title }}{% endblock %}

{% block content %}

Дата: {{ timestamp }}
Автор: {{ author }}

<h1>{{ title }}</h1>
<div>{{ body|safe|escape}}</div>
{% endblock %}

Примечание

escape экранирует HTML-теги, если вы использовали их при написании статьи в админке

Теперь напишем функцию и шаблон для главной страницы, с которой будем переходить на последние десять записей. Представление и шаблон главной страницы:

# djcode/pySite/pySite/views.py

from django.shortcuts import render_to_response
from django.template import RequestContext
from news.views import Post

# можно переписать как в news/views.py
def home(request):
    vars = dict (
        posts=Post.objects.all().order_by('-timestamp')[:10],
    )

    return render_to_response('index.html', vars, context_instance=RequestContext(request))

Шаблон для вывода последних десяти публикаций на главной:

{# djcode/pySite/templates/index.html #}

{% extends "base.html" %}
{% block title %}Index Page{% endblock %}

{% block content %}
    <div id="posts">
        <ul>
            {% for post in posts %}
                <li>
                    <a href="{% url 'news:one_new' post.id %}">
                        {{ post.title }}
                    </a>
                </li>
            {% endfor %}
        </ul>
    </div>
{%endblock%}

Наконец, создадим два (в примере мы ведь отделяем все приложения друг от друга, чтобы их можно было подключать к другому проекту внесением одной лишь строки в INSTALLED_APPS и синхронизацией с БД) файла urls.py: один главный, другой — персонально для созданного приложения news. При наличии пяти-десяти приложений это может быть удобным. Если приложение одно и со временем расширять функционал не требуется, создайте лишь один urls.py.

Импортируем необходимые модули, при входе на главную выводим представление, указанное в pySite.views.home функции, при входе на http://127.0.0.1:8000/news перенаправляем поиск в news/urls.py.

# djcode/pySite/pySite/urls.py

from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^$', 'pySite.views.home', name='home'),
    url(r'^news/', include('news.urls', namespace='news')),
    url(r'^admin/', include(admin.site.urls)),

)

В следующем файле обрабатываем наши представления:

если пользователь заходит на страницу вывода всех новостей, использовать функцию news.views.news; если на конкретную новость — вывод выбранной новости из news.views.one_new:

# djcode/pySite/news/urls.py

from django.conf.urls import patterns, include, url

urlpatterns = patterns('news.views',
    url(r'^$', 'news', name='news'),
    url(r'^(?P<post_id>\d+)/$', 'one_new', name='one_new'),

)

Дело сделано. Если всё прошло гладко, мы должны прийти примерно к подобному результату:

django

Успехов в изучении этого отличного фреймворка!