Пользователь
0,0
рейтинг
31 мая 2013 в 14:50

Разработка → Простой блог с комментариями на Django: разработка и развертывание для самых маленьких tutorial

Данная статья предназначена для новичков в web-программировании и освещает вопросы разработки блога на Django с использованием Twitter Bootstrap и его развертывания на бесплатном хостинге PythonAnywhere. Я старался написать как можно более проще и понятнее. Более опытным пользователям данное чтиво не расскажет ничего нового, да и некоторые приемы возможно покажутся неэффективными.




Предполагаю, что читатель уже ознакомлен с синтаксисом Python, имеет минимальное представление о Django (для начала неплохо пройти обучение на http://codeacademy.com по соответствующей теме и прочитать туториал по Django), а также владеет приемами работы в командной строке.
Итак, начинаем с организации рабочего окружения на локальном компьютере. В принципе, для наших запросов подойдет любая операционная система в которой вы чувствуете себя уверенно, здесь я описываю процесс для GNU/Linux, а для других систем шаги могут незначительно отличаться. В системе должна быть установлена virtualenv — утилита для создания изолированного рабочего окружения (чтобы библиотеки, которые мы используем не мешались другим программам и проектам).

Cоздаем и активируем окружение:
mkdir ~/projects
cd ~/projects
virtualenv env
source env/bin/activate 


В ОС Windows последняя команда должна быть такой:
env\Scripts\activate 


Устанавливаем Django с помощью pip — менеджера пакетов Python.
pip install django

Создаем новый проект. Назовем его как-нибудь оригинально — например mysite.
django-admin.py startproject mysite && cd mysite


Скрипт отработает и создаст каталог mysite с еще одним каталогом mysite и несколькими *.py файлами внутри.

Используем скрипт manage.py для создания приложения django с именем blog.
python manage.py startapp blog


Отредактируем настройки в файле mysite/settings.py (обратите внимание: я имею ввиду ~/projects/mysite/mysite/settings.py) добавив туда следующее:

# coding: utf-8
import os

BASE_DIR = os.path.dirname(os.path.dirname(__file__))


В первой строке укажем кодировку в которой работаем, во избежание путаницы и глюков предлагаю указывать ее во всех изменяемых файлах *.py, перекодируя их соответственно в UTF-8.
В BASE_DIR будет храниться полный путь к нашему проекту, что бы использовать относительные пути при дальнейшем конфигурировании

Настроим базу данных, в нашем проекте вполне можно использовать SQLite

DATABASES = { 'default':
    {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


Настроим часовой пояс и язык:
TIME_ZONE = 'Europe/Moscow'
LANGUAGE_CODE = 'ru-ru'


Для того что бы Django узнало о созданном приложении, добавим 'blog' в кортеж INSTALLED_APPS, а также раскомментируем строку 'django.contrib.admin' для включения встроенной админки:

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


Чтобы админка заработала, редактируем mysite/urls.py

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

from django.contrib import admin
admin.autodiscover()  #функция автоматического обнаружения файлов admin.py в наших приложениях

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)), #URL админки http://имя_сайта/admin/
)


Cоздаем модель в blog/models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=255) # заголовок поста
    datetime = models.DateTimeField(u'Дата публикации') # дата публикации
    content = models.TextField(max_length=10000) # текст поста

    def __unicode__(self):
        return self.title

    def get_absolute_url(self):
        return "/blog/%i/" % self.id


На основании этой модели Django автоматически создаст таблицы в базе данных.

Регистрируем ее в админке blog/admin.py

from django.contrib import admin
from blog.models import Post # наша модель из blog/models.py

admin.site.register(Post)


Создадим таблицы командой:

python manage.py syncdb


При первом вызове этой команды Django предложит создать суперпользователя, воспользуемся данным предложением.

Запускаем отладочный сервер который предоставляет Django:

python manage.py runserver


Теперь вводим url в браузере

http://localhost:8000/admin/

Если все прошло хорошо, мы должны видеть это:


Заходим с ранее созданным логином\паролем в админку — теперь мы имеем возможность добавлять и удалять посты (кнопки справа от Posts)

Создадим несколько постов для отладки.

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

Редактируем blog/views.py

from blog.models import Post 
from django.views.generic import ListView, DetailView

class PostsListView(ListView): # представление в виде списка
    model = Post                   # модель для представления 

class PostDetailView(DetailView): # детализированное представление модели
    model = Post


Добавим в urlpatterns mysite/urls.py строку
url(r'^blog/', include('blog.urls')),


Для того что бы все URL-ы начинающиеся с /blog/ будут обрабатываться с помощью urls.py из модуля blog, и создаем сам файл urls.py в каталоге blog со следующим содержанием:

#coding: utf-8
from django.conf.urls import patterns, url

from blog.views import PostsListView, PostDetailView 

urlpatterns = patterns('',
url(r'^$', PostsListView.as_view(), name='list'), # то есть по URL http://имя_сайта/blog/ 
                                               # будет выводиться список постов
url(r'^(?P<pk>\d+)/$', PostDetailView.as_view()), # а по URL http://имя_сайта/blog/число/ 
                                              # будет выводиться пост с определенным номером

)


Теперь нужно создать шаблоны страниц. По умолчанию для класса PostListView Django будет искать шаблон в blog/templates/blog/post_list.html (такой длинный и странный путь связан с логикой работы фреймворка, в силах разработчика поменять это поведение, но в данной статье я этого не касаюсь)

создадим этот файл:

{% block content %}
    {% for post in object_list %}
        <p>{{ post.datetime }}</p>
        <h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
        <p>{{ post.content }}</p>
    {% empty %}
    <p>Нет постов</p>
    {% endfor %}

{% endblock %}


Что же, давайте попробуем как все работает, зайдя по URL на http://localhost:8000/blog/. Если ошибок нет, то мы увидим список постов, где заголовок каждого поста является ссылкой.
Пока эти ссылки ведут в никуда, надо это исправить. По умолчанию для класса PostDetailView шаблон находится в blog\templates\blog\post_detail.html.

Создаем его:

{% block content %}
    <p>{{ post.datetime }}</p>
    <h2>{{ post.title }}</h2>
    <p>{{ post.content }}</p>
{% endblock %}


И снова проверяем: http://localhost:8000/blog/1/


Добавим возможность комментирования наших записей, в этих целях мы воспользуемcя услугами DISQUS, который установим с помощью pip

pip install django-disqus 


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

Добавляем в post_detail.html перед {% endblock %}

<p>
    {% load disqus_tags %}
    {% disqus_dev %}
    {% disqus_show_comments %}
</p>


В INSTALLED_APPS файла settings.py добавляем 'disqus'

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



А также добавляем в settings.py

DISQUS_API_KEY = '***'
DISQUS_WEBSITE_SHORTNAME = '***'


Два последних значения получаем, зарегистрировавшись на http://disqus.com.

Тестируем проект в браузере. Отлично, функционал нашего приложения внушает, но надо что-то делать с оформлением. Самый простой, и в тоже время современный вариант, использовать Twitter Bootstrap.

Качаем архив http://twitter.github.io/bootstrap/assets/bootstrap.zip и разархивируем его в каталог static нашего проекта (я имею в виду ~/projects/mysite/static — создаем его)

Редактируем settings.py, что бы Django знало где искать статические страницы.

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)


Создаем файл blog/templates/blog/base.html следующего содержания

<!DOCTYPE html>
<html lang="ru">
    <head>
        <meta charset="utf-8" />
        <title>MyBlog</title>
        <link href="{{STATIC_URL}}bootstrap/css/bootstrap.css" rel="stylesheet">
        <style>
            body {
                padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
            }
        </style>
        <link href="{{STATIC_URL}}bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
        <!--[if lt IE 9]>
        <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
        <![endif]-->
        <script src="{{STATIC_URL}}bootstrap/js/bootstrap.js" type="text/javascript"></script>
        {% block extrahead %}
        {% endblock %}
        <script type="text/javascript">
        $(function(){
        {% block jquery %}
        {% endblock %}
        });
        </script>
    </head>
<body>

<div class="navbar navbar-inverse navbar-fixed-top">
    <div class="navbar-inner">
        <div class="container">
            <div class="brand">My Blog</div>
            <ul class="nav">
                <li><a href="{% url 'list' %}" class="">Список постов</a></li>
            </ul>
        </div>
    </div>

</div>

<div class="container">
     {% block content %}Empty page{% endblock %}
</div> <!-- container -->

</body>
</html>


Это основной шаблон для наших страниц, включаем его в post_list.html и post_detail.html дописав первой строкой в них
{% extends 'blog/base.html' %}


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

Регистрируем бесплатный N00b аккаунт на PythonAnywhere. Мне понравился этот сервис легкостью в установке Django, все происходит практически так же как и на локальном компьютере.

Допустим мы создали пользователя на PythonAnywhere с именем djangotest, тогда наше приложение будет располагаться по адресу djangotest.pythonanywhere.com. Внимание: заменяйте далее по текстк 'djangotest' на свой логин в PythonAnywhere.

Меняем в settings.py

DEBUG = False


и дописываем

ALLOWED_HOSTS = ['djangotest.pythonanywhere.com']


Закачиваем файлы на хостинг любым из доступных способов.

На мой взгляд, для неискушенного пользователя, проще всего заархивировать папку проекта, залить архив на сервер (в разделе Files->Upload a file) и разархивировать его на сервере командой в bash — шелле (Раздел Consoles -> bash):

например если мы залили файл mysite.tar.gz то выполняем в консоли PythonAnywhere

tar -zxvf mysite.tar.gz




Теперь настраиваем рабочее окружение на сервере, вводим в консоли PythonAnywhere:

virtualenv env

source env/bin/activate

pip install django django-disqus


Настраиваем статические страницы в разделе Web -> Static files:

В первой строке — место где лежит bootstrap, во второй статические файлы встроенной админки Django.

Настраиваем WSGI (Web -> It is configured via a WSGI file stored at: ...):

activate_this = '/home/djangotest/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))

import os
import sys

path = '/home/djangotest/mysite'
if path not in sys.path:
    sys.path.append(path)
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()


Нажимаем кнопку Web -> Reload djangotest.pythonanywhere.com

Заходим на djangotest.pythonanywere.com/blog/ — поздравляю, это было нелегко, но Вы справились. Теперь у Вас есть собственный уютненький блог, разработанный своими руками на самых современных веб-технологиях!
Станислав Фатеев @svfat
карма
24,7
рейтинг 0,0

Похожие публикации

Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (24)

  • +1
    Сделайте простой миксин
    class PostMixin(object):
        model = Post
    

    и вьюшки станут ещё проще:

    class PostsListView(PostMixin, ListView): # представление в виде списка
        pass
    
    class PostDetailView(PostMixin, DetailView): # детализированное представление модели
        pass
    
    
    • +6
      Я бы не сказал, что этот прием значительно улучшит читаемость кода.
      • 0
        В статье для начинающих, стоит упомянуть про миксины и дать ссылку на django-braces. Тогда на вопрос «зачем вообще нужны CBV» будет легче ответить, кмк.
        • 0
          Согласен, в статье многого не упомянуто и не раскрыто. Ее идея именно в том, что бы человек переписал код и сразу получил более-менее функционирующий прототип, ведь порой скучно изучать только теорию, не применяя знания на практике. Однако, как часто бывает на Хабре, комментарии к статье становятся полезнее самой статьи. Так что, думаю, пытливые умы дойдут до Вашего комментария и ознакомятся с mixins, за что Вам огромное «Спасибо»!
        • +1
          В самой статье для начинающих про «примеси» не должно быть ни слова ;)
          • 0
            Тем более правильнее это назвать — множественное наследование. В python нету миксинов аля JS или Ruby.
  • 0
    Я просто оставлю здесь своё «Спасибо!». Эта статья мне очень пригодится через пару недель!
    • 0
      И как результат? :)
      • 0
        del
  • +1
    Почему django.contrib.staticfiles вроде как заявлен, а не используете как положено? Вообще какой-то странный подход к статике у вас. Для остальных аппов в дальнейшем тоже ручками будете всё прописывать, да? Или я что-то не понял?

    Редактируем settings.py, что бы Django знало где искать статические страницы.
    STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
    )
    Как раз джанге это не надо вообще знать, это информация для staticfiles, чтобы знать откуда собирать статику и информация для runserver, который из staticfiles (а не который из джанги родной), чтобы оно в девелоперском режиме статику могло раздавать.
    • 0
      для остальных аппов, как правило, прописан django.contrib.staticfiles.finders.AppDirectoriesFinder в STATICFILES_FINDERS, а вот для каких-то общепроектные вещей (тот же бутстрап, как у автора) лежит в корне проекта и не принадлежит ни одному приложению, поэтому для него нужен django.contrib.staticfiles.finders.FileSystemFinder и STATICFILES_DIRS.
      • 0
        Это я всё знаю, но причём тут финдеры? Речь про то, как реальный сервер будет раздавать статику. Что автор и настраивает, собственно, вручную. Сервер не лазает через финдеры за статикой, очевидно. А то так-то и у аппа admin статика лежит обычным образом и AppDirectoriesFinder её, понятно дело, вполне видит, иначе бы runserver у автора не работал с этим приложением. Но настраивает автор его вручную как видите на боевом сервере, потому что ему приходится это делать, т.к. — см. информацию из моего соощения выше.
        • 0
          manage.py collectstatic.
          Или у вас nginx (или что там статику отдаёт) лазает по всему джанго-проекту?
          • 0
            Так а я про что, собственно, говорю? Вы или статью не прочитали, или меня, походу, с кем-то спутали. Я то тут причём. У меня как раз никто не лазает по всему проекту. А вот у автора непонятно что творится со статикой. Никаким collectstatic там не пахнет, т.к. статика для сервера напрямую натравливается на один из STATICFILES_DIRS (который по определению не должен быть STATIC_ROOT) и на папку глубоко внутри одного из используемых аппов. Я сказал что так никто не делает, а Вы начали рассказывать про Finder-ы, которые никаким боком тут не затрагиваются, что сами же подтвердили следующим комментарием про collectstatic.
            • 0
              Всё, увидел о чём вы говорите: три раза читал статью, и три раза фрагмент про статику на pythonanywhere проходил мимо восприятия.
              Кстати, туториал на этом хостинге предлагает статику раздавать самой джангой. Очень странно.
              • 0
                Да сам хостинг сыроват ещё просто. Научатся :)
              • 0
                Если вы про urlpatterns += staticfiles_urlpatterns(), то оно работает всё равно только при DEBUG=True, и по сути используется только для того же runserver. На боевом оно работать опять же НЕ БУДЕТ. Но вот зачем оно рассказывается там, да ещё с каментами типа «This is needed to serve static files like images and css», это я не знаю, абсурд какой-то)
  • +2
    Хороший туториал, только я бы порекомендовал не хардкодить ссылки в get_absolute_url а сделать reverse
    • +1
      В данном случае по-моему вообще лучше дать view имя (типа post-detail) и в шаблоне реверсить его с помощью {% url 'post-detail' post.pk %}
      • +1
        Я все-таки считаю что лучше получать url везде через get_absolute_url так получается более DRY и тем-более потом структура url и модели может поменяться, например добавится slug и в адресе появится не id а slug(или и то и другое), а get_absolute_url как работала так и будет работать. И править надо будет всего в одном месте. Шаблонов тоже может оказаться много.
        • 0
          Нет, не в одном. Придётся всегда синхронно делать изменения в модулях url и model. А так, как я предлагаю — поменял регэксп в модуле url, поменялся адрес, а шаблоны трогать не надо. И, кстати даже добавление slug не потребует менять шаблоны, если slug станет pk :) Это, правда, касается модели, но добавление slug — в любом случае вмешательство в модель.
  • +1
    LANGUAGE_CODE = 'ru-ru'

    Если строго, то ru-ru не бывает, судя по github.com/django/django/tree/master/django/conf/locale. Пока просто ru.
  • 0
    Спасибо за статью. Если бы не Вы, я бы так и не добрался до джанго.
  • 0
    Огромный сенкс.
    Однозначно в маст хэв

    Только у меня почему то джанго отказывался искать шаблоны в папке templates,
    пришлось прописать самому в setings.py

    TEMPLATE_DIRS = (
        os.path.join(BASE_DIR, 'templates'),
    )
    

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