26 мая 2010 г. Django Python

Django. Первое приложение

Djangologo

Захотелось мне поведать как использовать связь ManyToMany, начал я значит писать пост, но так как я хотел его детализировать то он слишком разросся... И я решил сначала написать пост о том, как создавать первый проект и приложение на Django, чтобы потом ссылаться сюда.

-- начинал я этот пост писать еще в том году, да все руки не доходили его закончить, спасибо vasa_c за терпение

Устанавливаем Django

Заходим на страницу загрузки официального сайта, скачиваем Django 1.1.1 (или выше). Далее, как и в инструкции:

cd /tmp
wget -O Django-1.1.1.tar.gz http://www.djangoproject.com/download/1.1.1/tarball/
tar -xzvf Django-1.1.1.tar.gz
cd Django-1.1.1
sudo python setup.py install

Все, джанго установлен!

Так как мы будем работать с MySQL, то вам надо установить библиотеку py-mysqldb, представляющая собой интерфейс к MySQL.

Для Ubuntu:

apt-get install python-mysqldb 

Для FreeBSD

cd /usr/ports/databases/py-MySQLdb
make install clean

А для Windows можно скачать тут.

Создаем наш первый проект

Допустим, что имя проекта у нас будет "myproject":

cd /www
django-admin.py startproject myproject

Теперь создайте себе новую БД "myproject":

CREATE DATABASE `myproject` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

И отредактируйте "/www/myproject/settings.py", заменив только соответствующие части:

# ~*~ coding: utf-8 ~*~

...
# Настройки БД
DATABASE_ENGINE = 'mysql'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'myproject'             # Or path to database file if using sqlite3.
DATABASE_USER = 'username'             # Not used with sqlite3.
DATABASE_PASSWORD = 'password'         # Not used with sqlite3.
DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.

...
# Путь до каталога, где лежат наши медиа файлы (css, js, images)
MEDIA_ROOT = '/www/myproject/static/'

...
# URL медиа файлов (css, js, images)
MEDIA_URL = '/'

...
# URL медиа файлов админки (css, js, images, etc)
ADMIN_MEDIA_PREFIX = '/admin-media/'

...
# Путь до каталога с шаблонами
TEMPLATE_DIRS = (
    "/www/myproject/blog/templates",
)

...
# Установленные приложения в нашем проекте
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'myproject.blog',
)
# в конец добавлено две строки, первая подключает джанговскую админку,
# а второе наше приложение, которое мы ниже опишем

Для чего я ставлю в начале строки "#~~ coding: utf-8 ~~" читайте тут.

Создаем наше первое приложение

После того, как вы создали проект, надо создать приложение, если у вас проект состоит из одного приложения - можете его назвать "main".

Создаем приложение "blog":

cd /www/myproject
python manage.py startapp blog

Файл модели

Теперь перейдем к разработке модели нашего приложения, отредактируем "/www/myproject/blog/models.py".
У нас будет пока одна сущность, это Посты в Блоге:

# ~*~ coding: utf-8 ~*~

# импортируем класс модели
from django.db import models
# и админки
from django.contrib import admin

'''
Blog posts
'''
class Post(models.Model):
    # название поста
    title = models.CharField(max_length=100)
    # содержимое поста
    text = models.TextField()

    # функция необходима для того, чтобы при выводе объекта Post
    # как строки выводился вместо этого его title
    def __unicode__(self):
        return self.title;

'''
Класс для админки, тут будут дополнительные атрибуты необходимые для админки
'''
class PostAdmin(admin.ModelAdmin):
    # в таблице списка постов выводить только колонку title, если вы добавите еще одно имя поля, то и оно выведется
    list_display = ('title',)

# связываем эту модель с моделью PostAdmin
admin.site.register(Post, PostAdmin)
Вы можете все что связано с админкой вынести в admins.py и импортировать его, чтобы избавится от лишнего кода
После создания файла моделей, вам необходимо запустить синхронизацию с БД, для того чтобы создались структуры данных:
cd /www/myproject
python manage.py syncdb
Для более детального изучения команд manage.py введите:
python manage.py help syncdb

Вы так же можете создавать свои команды, но об этом поговорим не в этот раз.

syncdb создаст таблицы в вашей БД, а также при первой инициализации попросит создать рутовую учетную запись для вашего проекта в django (это будет админский аккаунт):

Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_user_permissions
Creating table auth_user_groups
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log
Creating table blog_post

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use 'root'): 
E-mail address: mail@example.com
Password: 
Password (again): 
Superuser created successfully.
Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for admin.LogEntry model

Когда вы создадите новый класс модели вам будет необходимо вновь запустить syncdb.
Если вы внесли изменения в уже синхронизированное приложение, а именно в его модель, то syncdb НЕ внесет их в вашу БД, для этого используйте sqlall и другие sql* комманды:

python manage.py sqlall название_приложения (например: blog)

Или попробуйте расширение для Django Django Evolution (для этого необходимо его установить в вашу систему и добавить в INSTALLED_APPS - "django_evolution").

Файл урлов

Для навигации по вашему проекту вам нужен роутер (URL диспетчер).
Отредактируем "/www/myproject/urls.py":

# ~*~ coding: utf-8 ~*~

# импортируем админку
from django.contrib import admin

# Makes the patterns() function available
from django.conf.urls.defaults import *

# импортируем наши вьюхи (см. ниже описание файла вьюх)
from myproject.blog.views import main_page, get_post

# Above we used admin.autodiscover() to automatically load the INSTALLED_APPS admin.py modules
admin.autodiscover()

urlpatterns = patterns('',
    # Если ничего не введено, то отдаем управление mainpage вьюхе
    (r'^$', main_page),
    # Если передан id поста, то отдаем управление get_post вьюхе
    (r'^post/([0-9]{1,5})', get_post),
    # Если введен /admin/, то идем в админку
    (r'^admin/', include(admin.site.urls)),
)

Вы конечно можете отредактировать локальный для приложения "blog" - urls.py ("/www/myproject/blog/urls.py"), но тогда вам надо будет импортировать "/www/myproject/blog/urls.py" в корневом "urls.py". Либо вы можете его определить в settings.py (ROOT_URLCONF).

Файл вьюхи

Ту роль, которую играет "Controller" в MVC, в Django играет роль View. Почему это так читайте тут.
Отредактируем "/www/myproject/blog/views.py":

# ~*~ coding: utf-8 ~*~

# функция генерирующая 404 страницу
from django.http import Http404

# функция отрисовки страницы, принимающая путь до шаблона и данные помещенные в шаблон
from django.shortcuts import render_to_response

# наша модель
from myproject.blog.models import Post

def main_page (request):
    # Получаем список постов
    posts = Post.objects.all()
    # отрисовываем
    return render_to_response('list.html', {"posts":  posts})

def get_post (request, post_id):
    try:
        # выбираем конкретный пост, pk - primary key
        post = Post.objects.get(pk=post_id)
    except Post.DoesNotExist:
        # если такого поста нет, то генерируем 404
        raise Http404

    # отрисовываем
    return render_to_response('single.html', {"title":  post.title, "text": post.text})

Вывод в шаблоне

В Django очень мощный и гибкий шаблонизатор, у него даже есть аналог на PHP - Twig, что говорит о его удобстве (был бы неудобным - не копировали бы), хотя Twig по слухам сильно хуже.
Однако, есть более крутой шаблонизатор для Django - Jinja2, с более высокой производительностью и более гибкими возможностями. Описывать Jinja я не буду, ведь не это цель сего поста, поэтому делюсь только ссылочками:

С более полными возможностями Django Template вы можете познакомится на официальной странице.

В Django Template поддерживается наследование, поэтому мы напишем общий шаблон и будем его использовать base.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>{% block title %}Default{% endblock %} / MyProject</title>
</head>
<body>
    {% block content %}
    {% endblock %}
</body>
</html>

Шаблон вывода списка постов list.html:

{% extends "base.html" %}

{% block title %}List!{% endblock %}

{% block content %}
<ul style="list-style-type:none">
{% for post in posts %}
<li style="padding-left:10px;{% if forloop.counter0|divisibleby:"4" %}background-color:#E1F3C9{% endif %}">
    <a href="/post/{{ post.id }}/">{{ post.title }}</a>
    <p>{{ post.text }}</p>
</li>
{% endfor %}
</ul>
</div>
{% endblock %}

Шаблон вывода одной записи single.html:

{% extends "base.html" %}

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

{% block content %}
<h1>{{ title }}</h1>

<div>
{{ text }}
</div>
{% endblock %}

Nginx

Теперь настроим Nginx для того, чтобы запуская "manage.py runserver" нам не прописывать порт в URL и чтобы использовать нормальное имя хоста, хотя вы может вполне без это обойтись.
Но я очень рекомендую использовать Nginx.


server {
    listen 80;
    server_name myproject.loc;

    location ^~ /admin-media {
        alias /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media;
    }

    location /static/ {
        root /www/myproject/static/;
    }

    location ~* \.(jpg|jpeg|gif|png|ico|css|zip|js|swf)$ {
        root /www/myproject/static/;
        expires 7d;
    }

    location / {
        proxy_pass http://127.0.0.1:8001/;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Добавить в "/etc/hosts" ваш хост:

127.0.0.1      myproject.loc

И перезагрузить Nginx:


# в новых версиях nginx можно послать сигнал
nginx -s reload

# или для ubuntu/debian через скрипт инициализации
service nginx restart
/etc/init.d/nginx restart

# или для freebsd
/usr/local/etc/rc.d/nginx restart

Запускаем веб-сервер проекта на 8001 порту, так как мы туда будем проксировать из Nginx:


cd /www/myproject
python manage.py runserver 8001

Теперь зайдите в админку (http://myproject.loc/admin/), введите вашу "root" учетку, которую создавали при syncdb.

Вы можете скачать архив с исходниками проекта тут

Вот мы и познакомились с первым приложение на Django, как я и обещал в начале этого поста следующая статья будет о ManyToMany, где мы подключим к нашему "Блогу" возможность создавать и выводить теги, а также расскажу более полно о возможностях моделях и ORM, View и о URL диспетере.

P.S. Пост дописывал в торопях, если найдете любые ошибки - обязательно отпишитесь в комментарии, спасибо!

Комментарии

Спасибо!
Ты охуен!

Расставь sudo где надо, не все такие хардкорные.

Разве не правильнее будет поставить django из пакетной системы,
как и все остальное?
aptitude install python-django (в squeeze сейчас 1.1.1-5)

cd /usr/ports/www/py-django ; make ; make install

P.S. textarea у коммента какая-то странная, в FF при размере окна
800x480 на моем eeepc 701 длинные строки не переносятся

vasa_c, спасибо, ты тоже)

ну я не только писал для "убунты", поэтому судо - лишнее. И вообще она не относится к делу, кто как работает. Вообще возможно кто-то под www создает проект и ему точно незачем sudo :)

bappoy,

Разве не правильнее будет поставить django из пакетной системы, как и все остальное? aptitude install python-django (в squeeze сейчас 1.1.1-5) cd /usr/ports/www/py-django ; make ; make install

Вот именно что "1.1.1-5", а сейчас есть 1.2, в этом вся проблема пакетов... С портами все конечно лучше, но всеравно не то. Лично я вообще использую версию из транка

P.S. textarea у коммента какая-то странная, в FF при размере окна 800×480 на моем eeepc 701 длинные строки не переносятся

Ага, тоже заметил, хотя у меня на нетбуке 1366x768 (заметил из-за увеличения шрифта в броузере), сенк, поправлю

эта, а если в роутере ни один шаблон не совпал и я хочу передать управление другому роутеру (Database)?

думаю несложно догадаться, кто это пишет)

Сам недавно сел за официальный туториал по Django. В документации к версии 1.2 есть небольшие отличия от того что Вы описываете, а так все классно. Отдельное спасибо за конфиг Nginx-а =)

Добавь в конце

urlpatterns = patterns(
    ...
    (r'^.*', include(myproject.myurls)),
)

А в myurls.py будет твой роутер

xoma, вам спасибо за лестный фидбек )

bappoy, поправил верстку формы комментирования в блоге, стало лучше?

да, стало намного лучше, большое спасибо! а еще кнопку "отправить" не видно, она появляется только при нажатии на tab, при этом форма съезжает влево.y

Кнопку поправил, сейчас лучше? :)

Ага, теперь все на месте, еще раз спасибо!

Тебе спасибо!

Привет! Классный туториал, спасибо за труды!)
Есть пара вопросов:
1) если меняются настройки в проекте или в других файлах (urls.py, views.py), нужно ли перезагружать апач? кажется без перезагрузки ничего не меняется
2) сделал все по твоей инструкции, админка (/admin/) почему-то не открывается пишет 404 ошибку и {'path': u'admin/'} при этом морда сайта грузится и выводит List.html

Alexander, откажитесь от использования apache, используйте для разработки runserver, а для продакшена - nginx+tornado

Хорошая статья, но есть вопросы:
1) Не получается подключить css-файлы.
2) Можете описать настройку apache для работы с django?

  1. Что именно вы делаете, по порядку опишите
  2. http://adw0rd.ru/2009/modpython/ и секцию посмотрите "Настройка Apache" на http://adw0rd.ru/2009/trac-freebsd/
  1. В settings.py задал MEDIA_ROOT, включающий путь к папке media; ADMIN_MEDIA_PREFIX='/media/'; MEDIA_URL = '/'.
    Далее, в шаблоне подключаю css-файл. Испробовал много разных вариантов, но ни один не срабатывает...
  2. Спасибо! Очень хорошо описано.
  1. Вам надо назначить alias для MEDIA_URL и ссылать на каталог MEDIA_ROOT, если у вас apache. Если у вас nginx, то посмотрите как у меня сделано

На сколько я понимаю, назначать alias надо в настройках apache? Я еще не настраивал сервер, а работаю со встроенным джанговским. Полагал, что для работы с ним настраивать ничего не надо... Или я ошибаюсь
?

Я отдаю статику через nginx и не встречался с вопросом как отдать статику через runserver. Думаю это возможно, но это совсем не его задача, так что лучше воспользуйтесь nginx или другим вебсервером предназначенный для этих целей

Попробую настроить. Спасибо.

it's work! :D

Sinkler, конгратулейшенс!

За статью спасибо! У меня такой вопрос: Хочу на всех страницах выводить список записей, в боковом меню. Не могу понять как это реализовать. Получается вывести только для главной, а на других страницах менюшка уже не отображается. Писать функцию для каждой страницы не хочется. Может кто-то порекомендует красивое решение? Заранее спасибо.

Все достаточно просто, вы можете добавить в TEMPLATE_CONTEXT_PROCESSORS свой контекс, который будет доступен в любом месте.

Два других варианта выборочны:

  • Написать общий контекст и всовывать его при необходимости
  • Использовать CBV и миксины, то есть намешивать свой контекст

Пример:

class CommonContext(object):
    def get_context_data(self):
        context = super(CommonContext, self).get_context_data()
        context.update({
            'BLA': '123'
        })
        return context

class PostList(CommonContext, ListView):
    queryset = Post.objects.all()

class PostDetail(CommonContext, DetailView):
    model = Post

Спасибо, помогло =)

А обещанную статью про manytomany так и не написал …

Оставьте свой комментарий

Markdown