пятница, 1 февраля 2013 г.

Django Admin

Django Controller Model View.



Установка Python в Ubuntu.

sudo apt-get update
sudo apt-get install python

Установка Django в Ubuntu.

Скачать с сайта архив с Django 1.3 с сайта http://www.djangoproject.com/download/.
Перейти в папку с архивом Django-1.3.5.tar.gz.

cd ~/Downloads/django
tar xfz Django-1.3.5.tar.gz
cd  Django-1.3.5
sudo python setup.py install

Проверка правильности установки Django.

django-admin.py --version

Создание проекта.
Перейти в папку, в которой будет храниться папка с созданным проектом.
cd ~/
mkdir django_project
cd ~/django_project
django-admin.py startproject django_bookmarks

В папке django_bookmarks будут созданы файлы вашего проекта Django.

django_bookmarks/
    __init__.py
    manage.py
    settings.py
    urls.py

Файл __init__.py - сообщает Python, что в папке находится набор модулей. Данный файл используется для группировки подобных файлов вместе во избежания конфликтов имен.

Файл manage.py - используется, как и django-admin.py, внутри проекта для запуска сервера (runserver), синхронизации базы данных (syncdb), показа кода базы данных (sql), создание папок приложение (startapp).

Файл settings.py - главный конфигурационный файл всего данного проекта Django. В нем указывается выбранная база данных, язык, серверное время, прописываются адреса расположения папок с шаблонами (templates) и адреса расположения папок с приложениями (apps).

Файл urls.py - используется для описания адресов ссылок всего сайта с вызовом соответсвующих методов акшенов из контроллеров приложений (apps).

Настройка файла settings.py для подключения базы данных:

DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = 'bookmarksdb'
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = ''

После изменения настроек необходимо синхронизировать базу данных.

python manage.py syncdb

Запускаем сервер и проверяем работу Django в браузере.

python manage.py runserver

Также сервер можно запустить на другом порту:

python manage.py runserver 8001

Переходим в браузере по адресу

http://localhost:8000 или http://127.0.0.1:8000

Выключение сервера производится нажатием сочетания клавиш Ctrl + C или закрытием окна терминала.

Создание приложения Django внутри папки с проектом.

python manage.py startapp bookmarks

В папке bookmarks будут созданы файлы вашего приложения для проекта Django.

django_bookmarks/
    bookmarks/
        __init__.py
        views.py
        models.py

Заполним файл контроллера views.py содержимым с методом акшоном.

from django.http import HttpResponse

def main_page(request):
    output = '''
        <html>
            <head>
                <title>%s</title>
            </head>
            <body>
                <h1>%s</h1>
                <p>%s</p>
            </body>
        </html>
    ''' % (
             u'Django Bookmarks',
             u'Welcome to Django Bookmarks',
             u'Where you can store and share bookmarks!'
    )
    return HttpResponse(output)

Параметр request содержит данные из адресной строки, полученные серверром из браузера.
Внутри request содержатся массивы с данными get, post, cookie:
request.GET
request.POST
request.COOKIES

Соединение метода акшона main_page с адресом url в файле urls.py

from django.conf.urls.defaults  import *

from bookmarks.views import main_page

urlpatterns = patterns('',
    url(r'^$', main_page)
)

Использующиеся регулярные выражения в URL Django.



Далее при перезагрузке сервера и открытия адреса http://127.0.0.1:8000

И мы увидим созданную нами страницу.

При переходе по адресу http://127.0.0.1:8000/does_not_exist/ страница будет не найдена, так как адресу does_not_exist/ ничего в файле urls.py не соответствует.

Создание модели базы данных в файле bookmarks/models.py

from django.db import models

class Link(models.Model):
    url = models.URLField(unique=True)

Типы полей таблицы базы данных.



Более подробно о типах полей таблиц базы данных можно узнать по адресу
http://docs.djangoproject.com/en/dev/ref/models/fields/

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

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django_bookmarks.bookmarks',
)

Синхронизируем базу данных для создания таблицы из модели.

python manage.py syncdb

Посмотри код SQL, который в результате был создан

python manage.py sql

BEGIN;
CREATE TABLE "bookmarks_link" (
    "id" integer NOT NULL PRIMARY KEY,
    "url" varchar(200) NOT NULL UNIQUE
);
COMMIT;

Откроем консоль Django

python manage.py shell

Заполним таблицу базы данных данными из консоли.

from bookmarks.models import *

link1 = Link(uirl=u'http://www.packtppub.com')
link1.save()

link2 = Link(url=u'http://www.example.com')
link2.save()

Выведем данные.

link1.url
link2.url

Изменим данные.

link2.url = u'http://www.google.com'
link2.save()

Без save() данные в таблицу физически добавлены не будут.

Выведем все данные из таблицы.

links = Link.objects.all()

for link in links:
    print(link.url)

Выведем дынные, соответствующие конкретному id записи в таблице базы данных.

Link.objects.get(id=1)

Удалим конкретную запись из таблицы базы данных.

link2.delete()

Сосчитаем общее число записей в таблице базы данных.

Link.objects.count()

Модель данных зарегистрированных на сайте пользователей.

Запустите консоль Django.

python manage.py shell

В консоли введите:

from django.contrib.auth.models import User

User.objects.all()

Посмотреть список названий колонок в таблице базы данных.

user = User.objects.get(id=1)
dir(user)

Создадим в файле bookmarks/models.py модель базы данных для таблицы Bookmarks.

from django.contrib.auth.models import User

class Bookmarks(models.Model):
    title = models.CharField(max_length=200)
    user = models.ForeignKey(User)
    link = models.ForeignKet(Link)

Синхронизируем базу данных.

python manage.py syncdb

Посмотри какой SQL код был выполнен.

BEGIN;
CREATE TABLE "bookmarks_link" (
    "id" integer NOT NULL PRIMARY KEY,
    "url" varchar(200) NOT NULL UNIQUE
);
CREATE TABLE "bookmarks_bookmark" (
    "id" integer NOT NULL PRIMARY KEY,
    "title" varchar(200) NOT NULL,
    "user_id" integer NOT NULL
    REFERENCES "auth_user" ("id"),
    "link_id" integer NOT NULL
    REFERENCES "bookmarks_link" ("id")
);
COMMIT;

Создадим папку templates.

cd ~/django_project/django_bookmarks
mkdir templates

Добавим в файл settings.py адрес расположения шаблонов HTML-страниц (templates).

import os.path

TEMPLATE_DIRS = (
    os.path.join(os.path.dirname(__file__), templates)
)

Создадим в папке templates шаблон HTML-страницы main_page.html

<html>
    <head>
        <title>{{ head_title }}</title>
    </head>
    <body>
        <h1>{{ page_title }}</h1>
        <p>{{ page_body }}</p>
    </body>
</html>

Изменим акшон метод в файле bookmarks/views.py

from django.http import HttpResponse

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

def main_page(request):
    template = get_template('main_page.html')
    variables = Context({
        'head_title': u'Django Bookmarks',
        'page_title': u'Welcome to Django Bookmarks',
        'body_title': u'Where you can store and share bookmarks!'
    })
    output = template.render(variables)
    return HttpResponse(output)

Перейдя в браузере по адресу http://127.0.0.1:8000 мы увидим все ту же страницу.

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

Добавим адрес url для страницы в файл urls.py

from django.conf.urls.defaults  import *

from bookmarks.views import main_page

from bookmarks.views import user_page

urlpatterns = patterns('',
    (r'^$', main_page),
    (r'^user/(\w+)$'),
)

Добавим в файл bookmarks/views.py метод акшон для страниц пользователя.

from django.http import HttpResponse
from django.http import Http404

from django.contrib.auth.models import User

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

def main_page(request):
    template = get_template('main_page.html')
    variables = Context({
        'head_title': u'Django Bookmarks',
        'page_title': u'Welcome to Django Bookmarks',
        'body_title': u'Where you can store and share bookmarks!'
    })
    output = template.render(variables)
    return HttpResponse(output)

def user_page(request, username):
    try:
        username = User.objects.get(username=username)
    except:
        reise Http404(u'Requested user not found.')
    bookmarks = user.bookmark_set.all()
    template = get_template('user_page.html')
    variables = Context({
        'username': username,
        'bookmarks': bookmarks
    })
    output = template.render(variables)
    return HttpResponse(output)

Метод user_page(request, username) помимо параметра request принимает дополнительный параметр username, который берется из данных в скобочках (\w+), прописанных в url для этой страницы.
То, что пишется внутри скобочек в url всегда передается как параметр внутрь метода views.py (controller).

Создадим шаблон страницы user_page.html в папке templates.

<html>
    <head>
        <title>Django Bookmarks - User: {{ username }} </title>
    </head>
    <body>
        <h1>Bookmarks for {{ username }}</h1>
        {% if bookmarks %}
        <ul>
           {% for bookmark in bookmarks %}
            <li><a href="{{ bookmark.url }}">{{ bookmark.title }}</a></li>
           {% endfor %}
        </ul>
       {% else %}
        <p>No bookmarks found.</p>
       {% endfor %}
    </body>
</html>

Переменная bookmark - это хэш-массив,  поэтому внутри шаблона мы можем вывести из нее элементы по ключам url и title.

Теперь перейдем в браузере по адресу http://127.0.0.1:8000/user/dmitriy
и увидим нашу страницу.

Перейдем к консоль Django и заполним таблицу базы данных данными.

python manage.py shell

Заполняем таблицу базу данных пользователя.

from django.cotrib.auth.models import User
from bookmarks.models import *
user = User.objects.get(id=1)
link = Link.objects.get(id=1)

Обратите внимание, что user.bookmark_set пока еще пуст.

user.bookmark_set.all()

Теперь создадим таблицу базы данных.

bookmark = Bookmark(
    title = u'Packt Publishing',
    user = user,
    link = link
)
bookmark.save()

Проверим user.bookmark_set.

user.bookmark_set.all()

Обновим страницу http://localhost:8000/user/dmitriy и увидим нашу запись из базы данных.

Создадим страницу для логина пользователя.

urlpatterns = patterns('',
    (r'^$', main_page),
    (r'^user/(\w+)/$', user_page),
    (r'^login/$', django.contrib.auth.views.login),
)

Создадим папку registration внутри папки templates.

mkdir registration

Создадим в папке templates/registration файл login.html

<html>
    <head>
        <title>Django Bookmarks - User Login</title>
    </head>
    <body>
        <h1>User Login</h1>
        {% if form.errors %}
        <p>Your username and password didn't match. Please try again.</p>
        {% endif %}
        <form method="post" action=".">
            <p><label for="id_username">Username:</label> {{ form.username }}</p>
            <p><label for="id_password">Password:</label> {{ form.password }}</p>
            <input type="hidden" name="next" value="/" />
            <input type="submit" value="login" />
        </form>

    </body>
</html>

Перейлем в браузере на страницу http://127.0.0.1:8000/login/ и увидим страницу с нашей формой.

Теперь отредактируем файл templates/main_page.html

<html>
    <head>
        <title>Django Bookmarks</title>
    </head>
    <body>
        <h1>Welcome to Django Bookmarks</h1>
        {% if user.username %}
        <p>Welcome {{ user.username }}! Here you can store and share bookmarks!</p>
        {% else %}
        <p>Welcome anonymous user! You need to <a href="/login/">login</a> before you can store and share bookmarks.</p>
        {% endif %}
    </body>
</html>

Далее отредактируем соответственно файл bookmarks/views.py

def main_page(request):
    template = get_template('main_page.html')
    variables = Context({'user': request.user})
    output = template.render(variables)
    return HttpResponse(output)

Так же можно сделать короче, используя django.shortcuts

from django.shortcuts import render_to_response

def main_page(request):
    return render_to_response('main_page.html', {'user', request.user})

Встроенные методы класса user.

is_authenticated() - возвращает Boolean, обозначающий залогинился ли пользователь или нет.

get_full_name() - возвращает имя и фамилию пользователя, разделенные пробелом.

email_user(subject, message, from_email=None) - посылает e-mail пользователю.

set_password(raw_password) -  устанавливает пользовательский пароль на переаданное значение.

check_password(raw_password) -  возвращает Boolean, обозначающийсовпал пароль или нет.

Откроем консоль Django и проверим пароль пользователя.

from django.contrib.auth.models import User
user = User.objects.get(id=1)
user.password

Добавим функцию Logout.

Добавим код в файл bookmarks/views.py

from django.http import HttpResponseRedirect
from django.contrib.auth import logout

def logout_page(request):
    logout(request)
    return HttpResponseRedirect('/')

Теперь добавим url для метода logout_page в файл urls.py

urlpatterns = patterns('',
    (r'^$', main_page),
    (r'^user/(\w+)/$', user_page),
    (r'^login/$', django.contrib.auth.views.login),
    (r['^logout/$', logout_page), 
)

Теперь перейдя по адресу http://127.0.0.1:8000/logout/ вы вернетесь на главную страницу уже в качестве анонимного пользователя.

Усовершенствуем структуру шаблонов.

Создадим в папке templates файл base.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>Django Bookmarks | {% block tirle %}{% endblock %}</title>
    </head>
    <body>
        <h1>{% block head %}{% endblock %}</h1>
        {% blockcontent %}{% endblock %}
    </body>
</html>

В соотвествии с этим отредактируем шаблон templates/main_page.html

{% extends "base.html" %}
{% block title %}
    Welcome to Django Bookmarks
{% endblock %}
{% block head %}
    Welcome to Django Bookmarks
{% endblock %}
{% block content %}
    {% if user.username %}
        <p>Welcome {{ user.username }}! Here you can store and share bookmarks!</p>
    {% else %}
        <p>Welcome anonymous user! You need to <a href="/login/">login</a> before you can store and share bookmarks.</p>
    {% endif %}
{% endblock %}

Изменим шаблон в файле templates/user_page.html

{% extends "base.html" %}
{% block title %}
    {{ username }}
{% endblock %}
{% block head %}
    Bookmarks for {{ username }}
{% endblock %}
{% block content %}
    {% if bookmarks %}
        <ul>
            {% for bookmark in bookmark.link.url %}
            <li><a href="{{ bookmark.link.url }}">{{ bookmark.title }}</a></li>
        </ul>
    {% else %}]
        <p>No bookmarks found.</p>
    {% end if %}
{% endblock %}

Теперь отредактируем файл templates/registration/login.html

{% extendes "base.html" %}
{% block title %}
    User Login
{% endblock %}
{% block head %}
    User Login
{% endblock %}
{% block content %}
    {% if form.has_errors %}
       <p>Your username and password didn't match. Please try again.</p>
    {% endif %}
    <form method="get" action=".">
        <p><label for="id_username">Username:</label>{{ form.username }}</p>
        <p><label for="id_password">Password:</label>{{ form.password }}</p>
        <input type="submit" value="login" />
        <input type="hidden" name="text" valuse="/" />
    </form>
{% endblock %}

Далее добавим код в файл urls.py

from django.conf.urls.defaults import *
from bookmarks.views import *

import os

site_media = os.path.join(os.path.dirname(__file__), 'site_media')

urlpatterns = patterns('',
    (r'^$', main_page),
    (r'^/user/(\w+)/$', user_page),
    (r'^login/$', 'django.contrib.auth.views.login'),
    (r'^logout/$', logout_page),
    (r'^site_media/(?<P<path>.*)$', 'django.views.static.serve', ('document_root': site_media)
)

Дале создадим папку site_media в общей папке нашего проекта.

mkdir site_media

Внутри нее создадим файл style.css

#nav {float: right;}

Отредактируем шаблон templates/base.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>Django Bookmarks | {% block title %}{% endblock %}</title>
        <link rel="stylesheet" href="/site_media/style.css" type="text/css" />
    </head>
    <body>
        <div id="nav">
            <a href="/">home</a> |
            {% if user.is_authenticated %}
            welcome {{ username }}
            (<a href="/logout">logout</a>)
            {% else %}
            <a href="/login/">login</a>
            {% endif %}
        </div>
        <h1>{% block head %}{% endblock %}</h1>
        {% block content %}{% endblock %}
    </body>
</html>

Отредаутирум файл bookmarks/views.py

from django.template import RequestContext

def main_page(request):
    return render_to_response('main_page.html', RequestContext(request))

Теперь нам не нужно передавать request.user,  RequestContext(request) обо всем позаботится автоматически.

Продолжим делать аналогичные изменения:

def user_page(request, username):
    try:
        user = User.objects.get(username=username)
    except:
    raise Http404(u'Requested user not found.')
    bookmarks = user.bookmarks_set.all()
    variables = RequestContext(request, {
        'username': username,
        'bookmarks': bookmarks 
    })
    return render_to_response('user_page.html', variables)

Форма регистрации новых пользователей.

Создадим файл bookmarks/forms.py в котором опишем форму для регистрации пользователей.

from django import forms

class RegistrationForm(forms.Form):
    username = forms.CharField(label=u'Username', max_length=30)
    email = forms.EmailField(label=u'Email')
    password1 = forms.CharField(label=u'Password', widget=forms.PasswordInput())
    password2 = forms.CharField(label=u'Password (Again)', widget=forms.PasswordInput())

Параметры форм.

label - создает в HTML коде метку <label for=""></label>

required - параметр по умолчанию установленный в True и требующий обязательного заполнения поль формы input. Чтобы разрешить пустой input требуется добавть параметр required=False.

widget - данный параметр определяет внешний вид поля в форме.

help_text -  параметр добавляющий пояснительный текст к полю формы.



Поработаем с формой из консоли Django.

python manage.py shell

Введем следующие команды:

from bookmarks.forms import *
form = RegistrationForm()
print(form.as_table())
print(form.as_ul())
print(form.as_p)

Мы также можем вывести одно поле формы.

print(form['username'])

Теперь введем данные в форму и проверим  их.

form = RegistrationForm({
    'username': 'test',
    'email': 'test@example.com',
    'password1': 'test',
    'password2': 'test'
})

form.is_valid()

Добавим в файл bookmarks/forms.py свой метод валидации пароля.

def clean_password2(self):
    if 'password1' in self.cleaned_data:
       password1 = self.cleaned_data['password1']
       password2 = self.cleaned_data['password2']
       if password1 == password2:
           return password2
    reise forms.ValidationError('Password do not match.')

В начало файла bookmarks/forms.py добавим:

imort re
from django.cotrib.auth.models import User

Далее добавим метод валидации имени пользователя.

def clean_username(self):
    username = self.cleaned_data['username']
    if not re.search(r'^\w+$', username):
        raise forms.ValidationError('Username can only contain alphanumeric characters and the underscore.')
    try:
        User.objects.get(username=username)
    except User.DoesNotExist:
        return username
    raise forms.ValidationError('Username is already taken.')

Отредактируем файл bookmarks/views.py для работы с формой.

from bookmarks.forms import *

def register_page(request):
    if request.method == 'POST':
        form = RegistrationForm(request.POST)
        if form.is_valid:
            user = User.objects.create_user(
                username = form.cleaned_data['username'],
                password = form.cleaned_data['password1'],
                email = form.cleaned_data['email']
            )
           return HttpResponseRedirect('/')
    else:
        form = RegistrationForm()
    variables = RequestContext(request, {
        'form': form
    })
    return render_to_response('registration/register.html', variables)

Создадим шаблон templates/registration/register.html для формы регистрации

{% extends "base.html" %}
{% block title %}
    User Registration
{% endblock %}
{% block head %}
    User Registrarion
{% endblock %}
{% block content %}
    <form metho="post" action=".">
    {{ form.as_p }}
    <input type="submit" value="register" />
    </form>
{% endblock %}

Добавим новый адрес в urls.py

(r'^register/$, register_page)

Добавим новый стиль в файл style.css

input {display: block;}

Перейдем в браузере по ссылке http://127.0.0.1:8000/register/ и увидим наше форму регистрации.

Теперь изменим шаблон templates/base.html

<div id="nav">
    <a href="/">home</a> |
    {% if user.is_authenticated %}
    welcome {{ user.username }}
    (<a href="/logout">logout</a>)
    {% else %}
    <a href="/login/">login</a> |
    <a href="/register/">register</a>
    {% endif %}
</div>

Создадим шаблон templates/registration/register_success.html

{% extends "base.html" %}

{% block title %}
    Registration Successful
{% endblock %}
{% block head %}
    Registration Completed Successfully
{% endblock %}
{% block content %}
    Thank you for registering. Your information has been saved in the database. Now you can either <a href="/login/">login</a> or go back to the <a href="/">main page</a>.
{% endblock %}

Для прямой ссылки на шаблон добавим ссылку в url в файле urls.py

from django.views.generic.simple import direct_to_template

(r'^register/success/$', direct_to_template, {'template': 'registration/register_success.html'})

В файле bookmarks/views.py заменим в register_page
return HttpResponseRedirect('/')
на
return HttpResponseRedirect('/register/success/')

Про авторизацию пользователей в Djnago подробнее можно прочитать по адресу http://docs.djangoproject.com/en/dev/topics/auth/

Создадим облако тэгов.

Добавим в файл bookmarks/models.py модель базы данных для облака тэгов.

class Tag(models.Model):
    name = models.CharField(max_length=64, unique=True)
    bookmarks = models.ManyToManyField(Bookmarks)

После этого синхронизируем базу данных:

python manage.py syncdb

И посмотрим SQL-код

python manage.py sql

BEGIN;
CREATE TABLE "bookmarks_link" (
    "id" integer NOT NULL PRIMARY KEY,
    "url" varchar(200) NOT NULL UNIQUE
);
CREATE TABLE "bookmarks_bookmark" (
    "id" integer NOT NULL PRIMARY KEY,
    "title" varchar(200) NOT NULL,
    "user_id" integer NOT NULL REFERENCES
    "auth_user" ("id"),
    "link_id" integer NOT NULL REFERENCES
    "bookmarks_link" ("id"),
);
CREATE TABLE "bookmarks_tag" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(64) NOT NULL UNIQUE
);
CREATE TABLE "bookmarks_tag_bookmarks" (
    "id" integer NOT NULL PRIMARY KEY,
    "tag_id" integer NOT NULL
    REFERENCES "bookmarks_tag" ("id"),
    "bookmark_id" integer NOT NULL
    REFERENCES "bookmarks_bookmark" ("id"),
    UNIQUE ("tag_id", "bookmark_id")
);
COMMIT;

Запустим консоль Django и посмотрим как работает модель многие ко многим.

python manage.py shell

from bookmarks.models import *
bookmark = Bookmark.objects.get(id=1)
bookmark.link.url
tag1 = Tag(name="book")
tag1.save()
bookmark.tag_set.add(tag1)
tag2 = Tag(name="publisher")
tag2.save()
bookmark.tag_set.add(tag2)
tag1.bookmarks.all()

Теперь в файле bookmarks/models.py внесем следующие изменения:

class Tag(models.Model):
    name = models.CharField(max_length=64, unique=True)
    bookmarks = models.ManyToManyField(Bookmark)
    def __unicode__(self):
        return self.name

Далее протестируем все это в консоли.

python manage.py shell

from bookmarks.models import *
Tag.objects.all()

Внесем схожие изменения в классы Link и Bookmark в файлу model.py

class Link(models.Model):
    url = models.UrlField(unique=true)
    def __unicode__(self):
        return self.url

class Bookmark(models.Model):
    title = models.CharField(max_length=200)
    user = models.ForeignKey(User)
    link = models.ForeignKey(Link)
    def __unicode__(self):
        return u'%s, %s' % (self.user.username, self.link.url)

Создадим форму для добавления вкладок в файле bookmarks/forms.py

class BookmarksSaveForm(forms.Form):
    url = forms.UrlField(
        label = u'Url',
        widget = forms.TextInput(attrs = {'size': 64})
    )
    title = forms.CharField(
        label = u'Title',
        widget = forms.TextInput(attrs = {'size': 64})
    )
    tags = forms.CharField(
        label = u'Tags',
        widget = forms.TextInput(attrs = {'size': 64})
    )

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

from bookmarks.models import *

class bookmark.save_page(request):
    if request.method == 'POST':
        form = BookmarksSaveForm(request.POST)
        if form.is_valid():
            # Создать или получить link.
            link, dummy = Link.objects.get_or_create(url=form.cleaned_data['url'])
            # Создать или получить bookmark.
            bookmark, created = Bookmark.objects.get_or_create(
                user = request.user,
                link = link
            )
            # Обновить bookmark title.
            bookmark.title = form.cleaned_data['title']
            # Если bookmark обновлена, то очистить старый tag list.
            if not created:
                bookmark.tag_set.clear()
            # Создать новый tag list.
            tag_names =  form.cleaned_data['tags'].split()
            for tag_name in tag_names:
                tag, dummy = Tag.objects.get_or_create(name=tag_name)
                bookmark.tag_set.add(tag)
            # Сохранить bookmark в базу данных.
            bookmark.save()
            return HttpResponseRedirect('user/%s/' % request.user.username)
        else:
            form = BookmarkSaveForm()
        variables = RequestContext({'form': form})
        return render_to_response('bookmark_save.html', variables)


В папке templates создадим файл bookmark_save.html

{% extends "base.html" %}

{% block title %}
    Save Bookmark
{% endblock %}

{% block head %}
    Save Bookmark
{% endblock %}

{% block content %}
    <form method="POST" action=".">
        {{ form.as_p }}
        <input type="submit" value="save" />
    </form>
{% endblock %}

Добавим изменения в файл urls.py

urlpatterns = patterns('',

    # Browsing
    (r'^$', main_page),
    (r'^user/(\w+)/$', user_page),

    # Session management
    (r'^logon$', django.contrib.auth.views.login),
    (r'^logout$', logout_page),
    (r'^register$', register_page),
    (r'^register/success$', direct_to_template, {'template': 'tegistration/register_success.html'}),

    # Account management
    (r'^save/$', bookmark_save_page),

    # Site media
    (r'^site_media/?P<path>.*$',  'django.views.static.serve', {'document_root': site_media}),

)

Запустим сервер

python manage.py startserver

Перейдем в браузере по адресу http://127.0.0.1:8000/save/ и увидим страницу с формой добавления bookmark.

Изменим меню в файле templates/base.html

<div id="nav">
    <a href="/">home</a>
    {% if user.is_authenticated %}
        <a href="/save/">submit</a> |
        <a href="/user/{{ user.username }}/">{{ user.username }}</a> |
        <a herf="/logout/">logout</a>
    {% else %}
        <a href="/login/">login</a>
        <a href="/register/">register</a>
    {% endif %}
</div>

В файле views.py в секцию bookmark_save_page добавим

from djnago.contrib.auth.decorators import login_required

@login_required
def bookmark_save_pager():

Данный код реализует примерно следующую логику:

if request.user.ia_authenticated():
    # Обработать данные из формы.
else:
    # Перейти на страницу ввода логина и пароля пользователя.

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

LOGIN_URL = '/login/'

Теперь перейдите в браузере по адресу http://127.0.0.1:8000/save/ и вас перенесет на страницу ввода логина и пароля.

В папке templates создадим новый шаблон bookmark_list.html

{% if bookmarks %}
    <ul class="bookmarks">
        {% for bookmark in bookmarks %}
            <li><a href="{{ bookmark.link.url }}" class="title">{{ bookmark.title }}</a><br />
            {% if show_tags %}
                 Tags:
                     {% if bookmark.tag_set.all() %}
                         <ul class="tags">
                             {% for tag in bookmark.tag_set.all() %}
                                 <li>{{ tag.name }}</li>
                             {% endfor %}
                         </ul>
                     {% else %}
                         None.
                     {% endif %}
                     <br />
                 {% endif %}
                 {% if show_user %}
                     Posted by: <a href="/user/{{ bookmark.user.username }}/" class="username">{{ bookmark.user.username }}</a>
                 {% endif %}
            </li>
        {% endfor %}
    </ul>
{% else %}
    <p>No bookmarks found.</p>
{% endif %}

Теперь модифицируем файл templates/user_page.html

{% extends "base.html" %}

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

{% block head %}
    Bookmarks fo {{ username }}
{% endblock %}

{% block content %}
    {% include "bookmark_list.html" %}
{% endblock %}

Внесем изменения в файл bookmarks/views.py

from django.shortcuts import get_object_or_404

def user_page(request, username):
    user = get_object_or_404(User, username=username)
    bookmarks = user.bookmark_set.order_by('-id')
    variables = RequestContext(reques, {
        'bookmarks': bookmarks,
        'username': username,
        'show_tags': True
    })
    return render_to_response('user_page.html', variables)

Добавим стили в файл site_media/style.css

ul.tags,
ul.tags li {
    display: inline;
    margin: 0;
    padding: 0;
}

Перейдем в браузере по адресу http://127.0.0.1:8000/user/your_username/ и увидим тэги рядом с гиперссылками.

В файл urls.py добавим url для страницы тэгов.

(r'^tag/([^\s]+)/$', tag_page),

В файле bookmarks/views.py создадим акшон-метод контроллера для страницы с тэгами

def tag_page(request, tag_name):
    tag = get_object_or_404(Tag, name=tag_name)
    bookmarks = tag.bookmarks.order_by('-id')
    variable = RequestContext(request, {
        'bookmarks': bookmarks,
        'tag_name': tag_name,
        'show_tags': True,
        'show_user': True
    })
    return render_to_repsonse('tag_page.html', variables)

Создадим шаблон для страницы с тэгами templates/tag_page.html

{% extends "base.html" %}

{% block title %}
    Tag: {{ tag_name }}
{% endblock %}

{% block head %}
    Bookmarks for tag: {{ tag_name }}
{% endblock %}

{% block content %}
    {% include "bookmark_list.html" %}
{% endblock %}

Модифицируем шаблон bookmark_list.html

<ul class="tags">
    {% for tag in bookmark.tag_set.all %}
        <li><a href="/tag/{{ tag.name }}">{{ tag.name }}</a></li>
    {% endfor %}
</ul>

В браузере перейдем на страницу пользователя и кликнем на гиперссылку с тэгом. В результате мы перейдем на страницу с тэгами.

В файле bookmarks/views.py создадим новый акшон-метод для облака тэгов

def tag_cloud_page(request):
    MAX_WEIGHT = 5
    tags = Tag.objects.order_by('name')
    # Рассчитать вес тэга, минимальное и максимальное значение.
    min_count = max_count = tags[0].bookmarks.count()
    for tag in tags:
        tag.count = tag.bookmarks.count()
        if tag.count < min_count:
            min_count = tag_count
        if max_count < tag.count:
            max_count = tag.count
    # Рассчитать count range, избегая деления на ноль.
    if range = 0.0:
        range = 1.0
    # Рассчитать вес тэга.
    for tag in tags:
        tag.weight = int(MAX_WEIGHT * (tag.count - min_count) / range)
    variables = RequestContext(request, {
        'tags': tags
    })
    return render_to_response('tag_cloud_page.html', variables)

В папке tamplates создадим шаблон страницы tag_cloud_page.html

{% extends "base.html" %}

{% block title %}
    Tag Cloud
{% endblock %}

{% block head %}
    Tag Cloud
{% endblock %}

{% block content %}
    <div id="tag-cloud">
        <a href="/tag/{{ tag.name }}/" class="tag-cloud-{{ tag.weight }}">{{ tag.name }}</a>
    </div>
{% endblock %}

Добавим стили для облака тэгов в файл site_media/style.css

#tag-cloud   {text-align: center;}
#tag-cloud a {margin: 0 0.2em;}
.tag-cloud-0 {font-size: 100%;}
.tag-cloud-1 {font-size: 120%;}
.tag-cloud-2 {font-size: 140%;}
.tag-cloud-3 {font-size: 160%;}
.tag-cloud-4 {font-size: 180%;}
.tag-cloud-5 {font-size: 200%;}

Добавим url для страницы с облаком тэгов в файл urls.py

(r'^tag/$', tag_cloud_page)

Перейдем в браузере по адресу http://127.0.0.1:8000/tag/ и увидим страницу с облаком тэгов.

Доработаем шаблон templates/bookmark_list.html, добавив фильт urlencode

[...]
{% if show_tags %}
    Tags:
    {% if bookmark.tag_set.all %}
        <ul class="tags">
            {% for tag in bookmark.tag_set.all %}
                <li><a href="/tag/"{{ tag.name|urlecode }}/>{{ tag.name }}</a></li>
            {% endfor %}
        </ul>
    {% else %}
        None.
    {% endif %}
    <br />
{% endif %}
[...]

Внесем подобные изменения, добавив фильтр urlencode в шаблон templates/tag_cloud_page.html

{% extends "base.html" %}

{% block title %}
    Tag Cloud
{% endblock %}

{% block head %}
    Tag Cloud
{% endblock %}

{% block content %}
    <div id="tag-cloud">
        {% for tag in tags %}
            <a href="/tag/"{{ tag.name|urlencode }}/" class="tag-cloud-{{ tag.weight }}">{{ tag.name }}</a>
        {% endfor %}
    </div>
{% endblock %}

Добавим jQuery в основной шаблон templates/base.html

<head>
    <title>Django Bookmarks | {% block title %}{% endblock %}</title>
    <link rel="stylesheet" href="/site_media/style.css" type="text/css" />
    <script type="text/javascript" src="/site_media/jquery.js"></script>
    {% block external %} {% endblock %}
</head>

Создадим форму поиска в файле bookmarks/forms.py

class SearchForm(forms.Form):
    query = forms.CharField(
        label=u'Enter a keyword to search for',
        widget=form.TextInput(attrs={'size': 32})
    )

Создадим акшон-метод для обработки данных из формы в файле bookmarks/views.py

def search_page(request):
    form = SearchForm()
    bookmarks = []
    shoe_results = False
    if 'query' in request.GET:
        show_results = True
        query = request.GET['query'].strip()
        if query:
            form = SearchForm({'query': query})
            bookmarks = Bookmark.objects.filter(title__icontains=query)[:10]
        variables = RequestContext(request, {
            'form': form,
            'bookmarks': bookmarks,
            'show_results': show_results,
            'show_tags': True,
            'show_user': True
        })
        return render_to_response('search.html', variables)

Фильтры данных в модели базы данных:

field__operator
__exact - значение соотвествует точно.
__contains - значение является частью поля.
__startswith - поле начинается с этого значения.
__lt - поле меньше, чем значение.
__gt - поле больше, чем значение.

Теперь создадим в папке templates шаблон search.html

{% extends "base.html" %}

{% block title %}
    Search Bookmarks
{% endblock %}

{% block head %}
   Search Bookmarks
{% endblock %}

{% block content %}
    <form id="search-form" method="get" action=".">
        {{ form.as_p }}
        <input type="submit" value="search" />
    </form>
    <div id="search-results">
        {% if show_results %}
            {% include "bookmark_list.html" %}
        {% endif %}
    </div>
{% endblock %}

Добавим ссылку на страницу поиска в файл urls.py

urlpatterns = patterns('',
    (r'^$', main_page),
    (r'^user/(\w+)/$', user_page),
    (r'^tag/([^\s]+)/$', tag_page),
    (r'^tag/$', tag_cloud_page),
    (r'^search/$', search_page),
    [...]
)

Добавим гиперссылку на страницу поиска в меню шаблона templates/base.html

<div id="nav">
    <a href="/">home</a> |
    {% if user.is_authenticated %}
        <a href="/save/">submit</a> |
        <a href="/search/">search</a> |
        <a href="/user/{{ user.username }}/">{{ user.username }}</a> |
        <a href="/logout/">logout</a>
    {% else %}
        <a href="/login/">login</a> |
        <a href="/register/">register</a>
    {% endif %}
</div>

Модифицируем акшон-метод search_page в файле bookmarks/views.py

def search_page(request):
    [...]
    variables = RequesContext(request, {
        'form': form,
        'bookmarks': bookmarks,
        'show_results': show_results,
        'show_tags': True,
        'show_user': True
    })
    if request.GET.has_key('ajax'):
        return render_to_response('bookmark_list.html', variables)
    else:
        return render_to_response('search.html', variables)

В папке site_media создадим файл search.js

function search_submit() {
    var query = $("#id_query").val();
    $("#search-results").load("/search/?ajax&query=" + encodeURIComponent(query));
    return false;
}

$(document).ready(funtion(){
    $("#search-form").submit(search_submit);
});

Добавим ссылку на этот файл в шаблон templates/search.html

{% extends "base.html" %}

{% block external %}
    <script type="text/javascript" src="/site_media/search.js"></script>
{% endblock %}

{% block title %}
    Search Bookmarks
{% endblock %}

{% block content %}
    Search Bookmarks
{% endblock %}

[...]

По адресу http://127.0.0.1:8000/search/ перейдем на страницу поиска.

Добавим акшон-метод bookmark_save_page в файл bookmarks/views.py

def bookmark_save(request, form):
    # Создать или получить link.
    link, dummy = Link.objects.get_or_create(url=form.cleaned_dat['url'])
    # Создать или получить bookmark.
    bookmark, created = Bookmark.objects.get_or_create(
        user=reques.user,
        link=link
    )
    # Обновить bookmark title.
    bookmark.title = form.cleaned_data['title']
    # Если bookmark обновлен, то очистить старый tag list.
    if not created:
        bookmark.tag_set_clear()
    # Создать новый tag list.
    tag_names = form.cleaned_data['tags'].split()
    for tag_name in tag_names:
        tag, dummy = Tag.objects.get_or_create(name=tag_name)
        bookmark.tag_set.add(tag)
    # Сохранить bookmark в базу данных и вернуть его.
    bookmark.save()
    return bookmark

Ниже изменим код следующим образом

@login_required
def bookmark_save_page(request):
    if request.method == 'POST':
        form = BookmarkSaveForm(request.POST)
        if form.is_valid():
            bookmark = _bookmark_save(request, form)
            return HttpResponseRedirect('/user/%s' % request.user.username)
    elif 'url' in request.GET:
        url = request.GET['url']
        title =''
        tags = ''
        try:
            link = Link.objects.get(url=url)
            bookmark = Bookmark.objects.get(
                link=link,
                user=request.user
            )
            title = bookmark.title
            tags = ' '.join(tag.name for tag in bookmark.tag_set.all())
        except (Link.DoesNotExist, Bookmark.DoesNotExist):
            pass
        form = BookmarkSaveForm({
            'url': url,
            'title': title,
            'tags': tags
        })
    else:
        form = BookmarkSaveForm()
    variables = RequestContext(request, {
        'form': form
    })
    return render_to_response('bookmark_save.html', variables)

Внесем изменения в шаблон файла templates/bookmark_list.html

{% if bookmarks %}
    <ul class="bookmarks">
        {% for bookmark in bookmarks %}
            <li><a href="{{ bookmark.link.url }}" class="title">{{ bookmark.title }}</a>
            {% if show_edit %}
                <a href="/save/?url={{ bookmark.link.url|urlencode }}" class="edit">[edit]</a>
            {% endif %}
            <br />
            {% if show_tags %}
                Tags:
                {% if bookmarks.tag_set.all %}
                   <ul class="tags">
                       {% for tag in bookmark.tag_set.all %}
                           <li><a href="/tag/{{ tag.name|urlencode }}">{{ tag.name|escape }}</a></li>
                       {% endfor %}
                   </ul>
                {% else %}
                    None.
                {% endif %}
                <br />
[...]

Далее отредактируем акшон-метод user_page в файле bookmarks/views.py

def user_page(request, username):
    user = get_object_or_404(User, username=username)
    bookmarks = user.bookmark_set.order_by('-id')
    variables = RequestContext(request, {
        'bookmarks': bookmarks,
        'username': username,
        'show_tags': True,
        'show_edit': username == request.user.username,
    })
    return render_to_response('user_page.html', variables)

Добавим стиль в файл site_media/style.css

ul.bookmarks .edit {font-size: 70%;}

Теперь создадим шаблон bookmark_save_form.html и переметим в него из шаблона bookmark_save.html следующий код

<form id="save-form" method="post" action=".">
    {{ form.as_p }}
    <input type="submit" value="save" />
</form>

Теперь подключим новый файл шаблона в файл bookmark_save.html

{% extends "base.html" %}

{% block title %}
    Save Bookmark
{% endblock %}

{% block head %}
   Save Bookmark
{% endblock %}

{% block content %}
    {% include "bookmark_save_form.html" %}
{% endblock %}

Обновим метод bookmark_save_page в файле bookmarks/views.py

@login_required
def bookmark_save_page(request):
    ajax = 'ajax' in request.GET
    if request.method == 'POST':
        form = BookmarkSaveForm(request.POST)
        if form.is_valid():
            bookmark = _bookmark_save(form)
            if ajax:
                variables = RequestContext(request, {
                    'bookmarks': [bookmark],
                    'show_edit': True,
                    'show_tags': True
                })
                return render_to_response('bookmark_list.html', variables)
            else:
                 return HttpResponseRedirect('/user/%s' % request.user.username)
        else:
            if ajax:
                return HttpResponse(u'failure')
    elif 'url' in request.GET:
        url = request.GETp['url']
        title = ''
        tags = ''
        try:
            link = Link.objects.get(url=url)
            bookmark = Bookmark.objrcts.get(link=link, user=request.user)
            title = bookmark.title
            tags = ' '.join(tag.name for tag in bookmark.tag_set.all())
        execpt (Link.DoesNotExist, Bookmark.DoesNotExist):
            pass
        form = BookmarkSaveForm({
            'url': url,
            'title': title,
            'tags': tags
        })
    else:
        form = BookmarkSaveForm()
    variables = RequestContext(reques, {
        'form': form
    })
    if ajax:
        return render_to_response('bookmark_save_form.html', variables)
    else:
        return render_to_response('bookmark_save.html', variables)

Далее модифицируем шаблон user_page.html

{% extends "base.html" %}

{% block external %}
   <script type="text/javascript" scr="/site_media/bookmark_edit.js"></script>
{% endblock %}

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

{% block head %}
    Bookmarks for {{ username }}
{% endblock %}

{% block content %}
    {% include "bookmark_list.html" %}
{% endblock %}

Запишем пару функций в файл site_media/bookmark_edit.js

function bookmark_edit() {
    var item = $(this).parent();
    var url = item.find(".title").attr("href");
    item.load("/save/?ajax&url=" + encodeURIComponent(url), null, function(){
        $("#save-form").submit(bookmark_save);
    });
    return false;
}

function bookmark_save() {
    var item = $(this).parent();
    var data = {
        url: item.find("#id_url").val(),
        title: item.find("#id_title").val(),
        tags: item.find("#id_tags").val()
    };
    $.post("/save/?ajax", data, function(){
        if (result != "failure") {
            item.before($("li", result).get(0));
            item.remove();
            $("ul.bookmarks .edit").click(bookmark_edit);
        } else {
            alert("Failed to validate bookmark before saving.");
        }
    });
    return false;
}

$(document).ready(function(){
    $("ul.bookmarks .edit").click(bookmark_edit);
});

Далее скачаем jQuery плагины:
jquery.autocomplete.css
dimensions.js
jquery.bgiframe.min.js
jquery.autocomplete.js
и подключим их к шаблону templates/bookmark_save.html

{% extends "base.html" %}

{% block external %}
    <link rel="stylesheet" href="/site_media/jquery.autocomplete.css" type="text/css" />
    <script type="text/javascript" src="/site_media/dimensions.js"></script>
    <script type="text/javascript" src="/site_media/jquery.bgiframe.min.js"></script>
    <script type="text/javascript" src="/site_media/jquery.autocomplete.js"></script>
    <script type="text/javascript" src="/site_media/tag_autocomplete.js"></script>
{% endblock %}

{% block title %}
    Save Bookmark
{% endblock %}

{% block head %}
    Save Bookmark
{% endblock %}

В конец файла bookmarks/views.py добавим следующий код

def ajax_tag_autocomplete(request):
    if 'q' in request.GET:
        tags = Tag.objects.filter(name__istartswith=request.GET['q'])[:10]
        return HttpResponse(u'\n'.join(tag.name for tag in tags))
    return HttpResponse()

Добавим url в файл urls.py

urlpatterns = patterns('',
[...]
# Ajax
(r'^ajax/tag/autocomplete/$', ajax_tag_autocomplete )
[...]
)

Далее добавим код в файл site_media/tag_autocomplete.js

$(document).ready(function(){
    $("#id_tags").autocomplete("/ajax/tag/autocomplete/", {multiple: true, multipleSeparator:" "});
});

Перейдем в браузере по адресу http://127.0.0.1:8000/save/ и посмотри как работает sugget в форме поиска.

Создадим новую модель базы SharedBookmark данных в файле bookmarks/models.py

class SharedBookmarks(models.Model):
    bookmark = models.ForeignKey(Bookmark, unique=True)
    date = models.DateTimeField(auto_now_add=True)
    votes = models.IntegerField(default=1)
    users_voted = models.ManyToManyField(User)

    def __unicode__(self):
        return u'%s, %s' % (self.bookmark, self.votes)

Синхронизируем базу данных

python manage.py syncdb

И посмотрим SQL

python manage.py sql

Модифицируем форму BookmarkSaveForm в файле bookmarks/forms.py

class BookmarkSaveForm(forms.Form):
    url = forms.URLField(
        label=u'URL',
        widget=form.TextInput(attrs={'size': 64})
    )
    title = form.CharField(
        label=u'Title',
        widget=forms.TextInput(attrs={'size': 64})
    )
    tags = forms.CharField(
        label=u'Tags',
        widget=forms.TextInput(attrs={'size': 64})
    )
    share = forms.BooleanField(
        label=u'Shared on the main page',
        required=False
    )

Модифицируем метод _save_bookmark в файле bookmarks/views.py

def _bookmark_save(request, form):
    # Создать или получить link.
    link, dummy = Link.objects.get_or_create(url=form.cleaned_data['url'])
    # Создать или получить bookmark.
    bookmark, created = Bookmark.objects.get_or_create(
        user=reques.user,
        link=link
    )
    # Обновить bookmark title.
    bookmark.title = form.cleaned_data['title']
    # Если bookmark обновлено, то очистить старый tag list.
    if not created:
        bookmark.tag_set.clear()
    # Создать новый tag list.
    tag_names = form.cleaned_data['tags'].split()
    for tag_name in tag_names:
        tag, dummy = Tag.objects.get_or_create(name=tag_name)
        bookmark.tag_set.add(tag)
    # Расшарить на главную страницу, если будет запрос.
    if form.cleaned_data['share']:
        shared, created = SharesBookmark.objects.get_or_crete(
            'bookmark': bookmark
        )
        if created:
            shared.users_voted.add(request.user)
            shared.save()
    # Сохранить bookmark в базу данных и вернуть его.
    bookmark.save()
    return bookmark

Модифицируем акшон-метод main_page в файле bookmarks/views.py

def main_page(request):
    shared_bookmarks = SharedBookmark.objects.order_by('-date')[:10]
    variables = RequestContext(request, {
        'shared_bookmarks': shared_bookmarks
    })
    return render_to_response('main_page.html', variables)

В папке templates создадим новый шаблон shared_bookmark_list.html

{% if shared_bookmarks %}
    <ul class="bookmarks">
        {% for shared_bookmark in shared_bookmarks %}
            <li><a href="{{ shared_bookmark.bookmark.link.url }}" class="title">{{ shared_bookmark.bookmark.title }}</a>
            <br />
            Posted By:
            <a href="/user/{{ shared_bookmark.bookmark.user.username }}/" class="username">{{ shared_bookmark.bookmark.user.username</a> |
            <span class="vote-count">Votes:{{ shared_bookmark.votes }}</span>
            </li>
        {% endfor %}
    </ul>
{% else %}
    <p>No bookmarks found.</p>
{% endif %}

Включи шаблон shared_bookmark_list.html в файл main_page.html

{% extends "base.html" %}

{% block title %}
    Welcome to Django Bookmarks
{% endblock %}

{% block head %}
   Welcome to Django Bookmarks
{% endblock %}

{% block content %}
    {% if user.username %}
        <p>Welcome {{ user.username }}! Here you can store and share bookmarks!</p>
    {% else %}
        <p>Welcome user! You need to <a href="/login/">login</a> before you can store and share bookmarks.</p>
    {% endif %}
    <h2>Bookmarks Shared by Users</h2>
    {% include "shared_bookmark_list.html" %}
{% endblock %}

Перейдя в браузере по адресу http://127.0.0.1:8000/ вы увидите обновленную главную страницу.

Обновим код в файле bookmarks/views.py

@login_required
def bookmark_vote_page(request):
    if 'id' in request.GET:
        try:
            id = request.GET['id']
            shared_bookmark = SharedBookmark.objects.get(id=id)
            user_voted = shared_bookmark.users_voted.filter(username=request.user.username)
            if no user_voted:
                shared_bookmark.votes += 1
                shared_bookmark.users_voted.add(request.user)
        except SharedBookmark.DoesNotExist:
            raise Http404('Bookmark not found.')
    if 'HTTP_REFERER' in request.META:
        return HttpResponseRedirect(request.META['HTTP_REFERER'])
    return HttpResponseRedirect('/')

Добавим url для этого акшон-метода в файл urls.py

urlpatterns = patterns('',
[...]
# Account management
(r'^save/$',bookmark_save_page),
(r'^vote/$', bookmark_vote_page)
)

В шаблон файла shared_bookmark_list.html добавим следующий код

{% if shared_bookmarks %}
    <ul class="bookmarks">
        {% for shared_bookmark in shared_bookmarks %}
            <li>
                <a href="/vote/?id={{ shared_bookmark.id }}" class="vote">[+]</a>
                <a href="{{ shared_bookmark.bookmark.link.url }}" class="title">{{ shared_bookmark.bookmark.title }}</a>
                <br />
                Posted By:
                <a href="/user/{{ shared_bookmark.bookmark.user.username }}/" class="username">{{ shared_bookmark.bookmark.user.username }}</a> |
                <span class="vote-count">Votes: {{ shared_bookmark.votes }}</span>
            </li>
         {% endfor %}
    </ul>
{% else %}
    <p>No bookmarks found.</p>
{% endif %}

Добавим новый акшон-метод в файл bookmarks/views.py

from datetime import detetime, timedelta

def popular_page(request):
    today = datetime.today()
    yesterday = today - timedelta(1)
    shared_bookmarks = SharedBookmarks.objects.filter(date__gt=yesterday)
    shared_bookmarks = shared_bookmarks.order_by('-votes')[:10]
    variables = RequestContext(request, {
        'shared_bookmarks': shared_bookmarks
    })
    return render_to_response('popular_page.html', variables)

Создадим файл шаблона popular_page.html в папке templates

{% extends "base.html" %}

{% block title %}
    Popular Bookmarks
{% endblock %}

{% block head %}
    Popular Bookmarks
{% endblock %}

{% block content %}
    {% include "shared_bookmark_list.html" %}
{% endblock %}

Добавим url на эту страницу в файл urls.py


urlpatterns = patterns('',
    # Browsing
    (r'^$', main_page),
    (r'^popular/$', popular_page),
    (r'^user/(\w+)/$', user_page),
    (r'^tag/([^\s]+)/$', tag_page),
    (r'^tag/$', tag_cloud_page),
    (r'^search/$', search_page),
    [...]
)

В браузере перейдем по адресу http://127.0.0.1:8000/popular/ и посмотрим на страницу.

В главный шаблон в файле templates/base.html добавим ссылку на эту страницу.

[...]
    <div id="nav">
        <a href="/">home</a> |
        <a href="/popular/">popular</a> |
        {% if user.is_authenticated %}
            <a href="/save/">submit</a> |
            <a href="/search/">search</a> |
            <a href="/user/{{ user.username }}/">
            {{ user.username }}</a> |
            <a href="/logout/">logout</a>
        {% else %}
            <a href="/login/">login</a> |
            <a href="/register/">register</a>
        {% endif %}
    </div>
[...]

Добавим встроенное в Django приложение для размещения комментариев на сайте.

В файле settings.py добавим строчку 'django.contrib.comments'

INSATALLED_APPS = {
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.comments',
    'django_bookmarks.bookmarks'
}

Синхронизируем базу данных

python manage.py syncdb

Посмотри SQL

python manage.py sql

Включим url для комментариев в файл urls.py

urlpatterns = patterns('',
[...]
    # Comments
    (r'^comments/$', include('django.contrib.comments.urls')),
)

Добавим еще url в файл urls.py

urlpatterns = patterns('',
    # Browsing
    (r'^$', main_page),
    (r'^popular/$', popular_page),
    (r'^user/(\w+)/$', user_page),
    (r'^tag/([^s]+)$', tag_page),
    (r'^tag/$', tag_cloud_page),
    (r'^search/$', search_page),
    (r'^bookmark/(\d+)/$', bookmark_page),
    [...]
)

Добавим акшон-метод bookmark_page в файл bookmarks/views.py

def bookmark_page(request, bookmark_id):
    shared_bookmark = get_object_or_404(SharedBookmark, id=bookmark_id)
    variables = RequestContext(request, {
        'shared_bookmark': shared_bookmark
    })
    return render_to_response('bookmark_page.html', variables)

Создадим новый шаблон bookmark_page.html в папке templates

{% extends "base.html" %}

{% block title %}
    Bookmark: {{ shared_bookmark.bookmark.title }}
{% endblock %}

{% block head %}
    <a href="/vote/?id={{ shared_bookmark.id }}" class="vote">[+]</a>
    <a href="{{ shared_bookmark.bookmark.link.url }}" class="title">{{ shared_bookmark.bookmark.title }}</a>
{% endblock %}

{% block content %}
    Posted By:
    <a href="/user/{{ shared_blockmark.user.usernaem }}/" class="username">{{ shared_bookmark.bookmark.user.username }}</a> |
    <span class="vote-count">Voted: {{ shared_bookmark.votes }}</span>
{% endblock %}

Специальные тэги для шаблона комментариев:

get_comment_count - возвращает число комментариев для конкретной страницы.
get_comment_list -  возвращает список комментариев для конкретной страницы.
render_comment_form - отображает форму для добавления комментариев.

Данные тэги децствуют в шаблоне после добавления команды

{% load comments %}

Каждый комментарий имеет следующий атрибут:

user - объект User пользователя, оставившего комментарий.
submit_date -дата и время добавления комментария.
comment - текст комментария.
ip_address - IP-адрес пользователя, оставившего комментарий.

Обновим код в шаблоне templates/bookmark_page.html

{% render_comment_form for bookmarks.sharedbookmark shared_bookmark.id %}

Добавим код в шаблон templates/bookmark_page.html

{% extends "base.html" %]

{% load comments %}

{% block title %}
    Bookmarl: {{ shared_bookmark.bookmark.title }}
{% endblock %}

{% block head %}
    <a href="/vote/?id={{ shared_bookmark.id }}" class="vote">[+]</a>
{% endblocl %}

{% block content %}
    Posted By:
    <a href="/user/{{ shared_bookmark.bookmark.user.username }}/" class="username">{{ shared_bookmark.bookmark.user.username }}</a> |
    <span class="vote-count">Votes: {{ shared_bookmark.votes }}</span>
    <h2>Comments</h2>
    {% get_comment_count for bookmarks.sharedbookmark shared_bookmark.id as comment_count %}
    {% get_comment_list for bookmarks.sharedbookmark shared_bookmark.id as comment_list %}
    {% for comment in comment_list %}
        <div class="comment">
            <p><b>{{ comment.user.username }}</b> said:</p>
            {{ comment.comment|escape|urlizetrunc:40|linebreaks }}
        </div>
    {% endfor %}
    <p>Number of comments: {{ comment_count }}</p>
    {% render_comment_form for bookmarks.sharedbookmark shared_bookmark.id %}
{% endblock %}

Создадим шаблон формы form.html в папке templates/comments/

{% load comments %}

{% if user.is_authenticated %}
    <form action="{% comment_form_target %} method="POST">
        {% for field in form %}
            {% if field.is_hidden %}
                {{ field }}
            {% endif %}
        {% endfor %}
        <input type="hidden" name="name" value="{{ user.username }}" />
        <input type="text" name="honeypot" size="64" style="display: none;" />
        <label for="id_comment">Comment</label>
        <textarea id="id_comment" rows="10" cols="40" name="comment"></textarea>
        <input type="submit" name="submit" class="submit-post" value="Post" />
    </form>
{% else %}
    <p>Please <a href="/login/">log in</a> to post comments.</p>
{% endif %}

Далее создадим шаблон posted.html в папке templates/comments/

{% extends "base.html" %}

{% block title %}
    Comment Posted Successfully
{% endblock %}

{% block head %}
    Comment Posted Successfully
{% endblock %}

{% block content %}
    <p>Thank you for contributing.</p>
{% endblock %}

Теперь изменим шаблон templates/shared_bookmark_list.html

{% if shared_bookmarks %}
    <ul class="bookmarks">
        {% for shared_bookmark in shared_bookmarks %}
            <li>
                <a href="/vote/?id={{ shared_bookmark.id }}" class="vote">[+]</a>
                <a href="{{ shared_bookmark.bookmark.link.url }}" class="title">{{ shared_bookmark.bookmark.title }}</a>
                <br />
                Posted By:
                <a href="/user/{{ shared_bookmark.bookmark.user.username }}/" class="username">{{ shared_bookmark.bookmark.user.username }}</a> |
                <span class="vote-count">Votes: {{ shared_bookmark.votes }}</span> |
                <a href="/bookmark/{{ shared_bookmark.id }}/">Comments</a>
             </li>
        {% endfor %}
    </ul>
{% else %}
    <p>No bookmarks found.</p>
{% endif %}

Добавим новые стили в файл site_media/style.css

textarea {
    display: block;
}

.comment {
    margin: 1em;
    padding: 5px;
    border: 1px solid #000;
}

В браузере перейдем на главную страницу по адресу http://127.0.0.1:8000/ и кликнем на гиперссылку Comments.

Попробуем добавить свой комментарий.

Вернемся на страницу bookmarks и увидим там свой комментарий.

Активируем админку Django.

В файл settings.py добавим админку в INSTALLED_APPS.

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.admin',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.comments',
    'django_bookmarks.bookmarks',
)

Выполним синхронизацию базы данных для админки.

python manage.py syncdb

Псомотрим получившийся SQL код.

python manage.py sql

В файле urls.py добавим ссылку url на админку Django.

from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
    [...]
    # Admin interface
    (r'^admin/(.*)', admin.site.root),
)

 Создадим файл с акшон методом bookmarks/admin.py

from django.contrib import admin
from bookmarks.models import *

class LinkAdmin(admin.ModelAdmin):
    pass

admin.site.register(Link, LinkAdmin)

В браузе перейдем по адресу http://127.0.0.1:8000/admin/

Введем логин и пароль и попадем на страницу администрирования.

В админке используются только классы базы данных, введенных в файле bookmarks/admin.py

Изменим итерфейс админки.

Добавим в файл bookmarks/admin.py класс BookmarkAdmin

class BookmarkAdmin(admin.ModelAdmin):
    list_display = ('title', 'link', 'user')

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

Для админки можно определить следующие типы атрибутов в классе.

list_filter - если прописан, то создает сайдбар со ссылками, который может фильтровать объекты по одному или нескольким полям базы данных.

ordering - используется для упорядочивания обектов в списке. Если имя поля представлено со знаком минут, то упорядочивание идет по убыванию, а не по возрастанию.

search_fields - если прописан, то создает поле поиска, которое может быть использована для поиска объектов базы данных.

Дополним класс BookmarkAdmin в файле bookmarks/admin.py

class BookmarkAdmin(admin.ModelAdmin):
    list_display = ('title', 'link', 'user')
    list_filter = ('user',)
    ordering = ('title',)
    search_fields = ('title')

list_filter - добавляет фильтрацию bookmarks по имени user
ordering - упорядочивает bookmarks по title
search_fields - позволяет искать bookmarks по title

Обновим страницу админки и увидим внесенные изменения.

Приведем список путей установки Django по умолчанию

Windows: C:\PythonXX\Lib\site-packages\django
UNIX and Linux: /usr/lib/pythonX.X/site-packages/django
Mac OS X: /Library/Python/X.X/site-packages/django

Найдите файл django-admin.py внутри папки bin.

Откройте папку django/contrib/admin/templates/

Найдите там следующие файлы:

admin/base_site.html - это главный шаблон админки. Все страницы админки наследуют из этого шаблона.

admin/change_list.html - этот шаблон генерирует список доступных объектов модели.

admin/change_form.html - этот шаблон генерирует форму для добавления или удаления объектов.

admin/delete_confirmation.html - этот шаблон генерирует страницу подтверждения для удаления объекта.

Изменим один из этих шаблонов.

Создайте в папке templates вашего проекта папку admin, в которую скопируйте файл admin/base_site.html

Измените в скопированном файле все слова Django на Django Bookmarks

{% extends "base.html" %}

{% load il8n %}

{% block title %}
    {{ title }} | {% trans 'Django Bookmarks site admin' %}
{% endblock %}

{% block branding %}
    <h1 id="site-name">{% trans 'Django Bookmarks administration' %}</h1>
{% endblock %}

{% block nav-global %}
{% endblock %}

Добавим RSS.

Создадим файл feeds.py в папке bookmarks

from django.contrib.syndication.feeds import Feed

from bookmarks.models import Bookmark

class RecentBookmarks(Feed):
    title = u'Django Bookmarks | Recent Bookmarks'
    link = '/feeds/recent/'
    description = u'Recent bookmarks posted to Django Bookmarks'
    def items(self):
        return Bookmark.objects.order_by('-id')[:10]

Добавим изменения в файл bookmarks/models.py

class Bookmark(models.Model):
    title = models.CharField(max_length=200)
    user = models.ForeignKey(User)
    link = models.ForeignKey(Link)
    def __unicode__(self):
        return u'%s, %s' % (self.user.username, self.link.url)
    def get_absolute_url(self):
        return self.link.url

Добавим url для RSS в файл urls.py

import os.path
from django.conf.urls.default import *
from django.views.generic.simple import direct_to_template
from django.contrib import admin
from bookmarks.views import *
from bookmarks.feeds import *

admin.autodiscover()

site_media = os.path.join(os.path.dirname(__file__), 'site_media')

feeds = {'recent': RecentBookmarks}

urlpatterns = patterns('',
   [...]
   # Feeds
   (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}),
)

Перейдем в браузере по адресу http://127.0.0.1:8000/feeds/recent/ и увидим наш RSS.

Создадим в папке tamplates папку feeds и в ней создадим файл recent_title.html

{{ obj.title }}

Создадим пустой файл recent_description.html в папке templates/feeds/

Перезагрузим страницу http://127.0.0.1:8000/feeds/recent/ и увидим изменения.

Отредактируем файл bookmarks/feeds.py

from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User

class UserBookmarks(Feed):
    def get_object(self, bits):
        if len(bits) != 1:
            raise ObjectDoesNotExist
        return User.objects.get(username=bits[0])
    def title(self, user):
        return (u'Django Bookmarks | Bookmarks for %s' % user.username)
    def link(self, user):
        return '/feeds/user/%s/' % user.username
    def description(self, user):
        return u'Recent bookmarks posted by %s' % user.username
    def items(self, user):
        return user.bookmark_set.order_by('-id')[:10]

Добавим изменение для feeds в файл urls.py

import os.path
from django.conf.urls.defaults import *
from django.views.generic.simple import direct_to_template
from django.contrib import admin
from bookmarks.views import *
from bookmarks.feeds import *

admin.autodiscover()

site_media = os.path.join(os.path.dirname(__file__),'site_media')

feeds = {
    'recent': RecentBookmarks,
    'user': UserBookmarks
}

Добавим файл user_title.html в папку templates/feeds/

{{ obj.title }}

Перейдем в браузере по адресу http://127.0.0.1:8000/feeds/user/
your_username/

Изменим шаблон в файле templates/main_page.html

{% extend "base.html" %}

{% block title %}
    Welcome to Django Bookmarks
{% endblock %}

{% block head %}
    Welcome to Django Bookmarks
{% endblock %}

{% block external %}
    <link rel="alternate" type="application/rss+xml" title="Django Bookmarks | Recent Bookmarks" href="/feeds/recent/" />
{% endblock %}

[...]

Обновим шаблон в файле templates/user_page.html

{% extends "base.html" %}

{% block external %}
    <script type="text/javascript" src="/site_media/bookmark_edit.js"><script>
    <link rel="alternate" type="application/rss+xml" title="Django Bookmarks | Bookmarks for {{ username }}" href="/feeds/user/{{ username }}" />
{% endblock %}

Методы выборки данных из базы данных.

.get() - возвращает объект по имени поля.
.all() - возвращает вообще все объекты из таблицы базы данных.
.count() - возвращает общее число объектов в таблице базы данных.
.filter() -  используется для отбора объектов по критериям (аналог select из SQL).
.order_by() -  позволяет сортировать найденные объекты по порядку возрастания или убывания.

Пример.

Link.objects.filter(url__istartswith='https')

Link.objects.filter(url__istartswith='https', url__iendwith='html')

Bookmark.objects.filter(link_istartswith='http')

Bookmark.objects.filter(link_url_istartswith='https')

Bookmark.objects.filter(tag__name_iexact='JavaScript')

query_set = Bookmark.objects.filter(tag__name__iexact='JavaScript')

bookmarks = query_set.filter(user__username__iexact='dmitriy')

bookmarks = Bookmark.objects.filter(tag__name__iexact='JavaScript').filter(user__username__iexact='dmitriy')

Bookmark.objects.filter(title__icontains='JavaScript').exclude(tag__name__iexact='Web')

Bookmark.objects.filter(user__username__iexacts='dmitriy').order_by('title')

Bookmark.objects.all()[:10]

from django.db.models import Q
q1 = Q(title__icontains='Python')
q2 = Q(title__icontains='JavaScript')
q3 = q1 | q2
Bookmark.objects.filter(q3)
Bookmark.objects.filter(Q(title__icontains='Python') | Q(title__icontains='JavaScript'))

Отредактируем search_page в файле bookmarks/views.py

from django.db.models import Q

def search_page(request):
    form = SearchForm()
    bookmark = []
    show_results = False
    if 'query' in reques.GET:
        show_results = True
        query = reques.GET['query'].strip()
        if query:
            keywords = query.split()
            q = Q()
            for keyword in keywords:
                q = q & Q(title__icontains=keyword)
            form = SearchForm({'query': query})
            bookmarks = Bookmark.objects.filter(q)[:10]
        variables = RequestContext(request, {
            'form': form,
            'bookmarks': bookmarks,
            'show_results': show_results,
            'show_tags': True,
            'show_user': True
        })
        if 'ajax' in request.GET:
            return render_to_response('bookmark_list.html', variables)
        else:
            return render_to_response('search.html', variables)

Поработаем с паджинацией.

from bookmarks.models import *
from django.core.paginator import Paginator
query_set = Bookmark.objects.all()
paginator = Paginator(query_set, 10)

paginator.num_pages
paginator.count
page = paginator.page(1)
page.object_list
page.has_previous()
page.has_next()

Модифицируем user_page в файле bookmarks/views.py

from django.core.paginator import Paginator, InvalidPage

ITEMS_PER_PAGE = 10

def user_page(request, username):
    user = get_object_or_404(User, username=username)
    query_set = user.bookmark_set.order_by('-id')
    paginator = Paginator(query_set, ITEMS_PER_PAGE)
    try:
        page_number = int(request.GET['page'])
    except (KeyError, ValueError):
        page_number = 1
    try:
        page = paginator.page(page_number)
    except InvalidPage:
        raise Http404
    bookmarks = page.object_list
    variables = RequestContext(request, {
        'bookmarks': bookmarks,
        'username': username,
        'show_tags': True,
        'show_edit': username == request.user.username,
        'show_paginator': paginator.num_pages > 1,
        'has_prev': page.has_previous(),
        'has_next': page.has_next(),
        'page': page_number,
        'pages': paginator.num_pages,
        'next_page': page_number + 1,
    })
    return render_to_response('user_page.html', variables)

Обновим шаблон templates/bookmark_list.html для использования паджинатора.

{% if bookmarks %}
   <ul class="bookmarks">
        {% for bookmark in bookmarks %}
            [...]
        {% endfor %}
   </ul>
   {% if show_paginator %}
       <div class="paginator">
           {% if has_prev %}
               <a href="?page={{ page_prev }}">&laquo; Previous</a>
           {% endif %}
           {% if has_next %}
                <a href="?page={{ next_page }}">Next &raquo;</a>
           {% endif %}
           (Page {{ page }} of {{ pages }})
        </div>
    {% endif %}
{% else %}
    <p>No bookmarks found.</p>
{% endif %}

Перейдем в браузере на страницу со вкладками и увидим паджинацию.

Создадим новую модель базы данных в файле bookmarks/models.py

class Friendship(models.Model):
    from_friend = models.ForeignKey(User, related_name='friend_set')
    to_friend = models.ForeignKey(User, related_name='to_friend-set')
    def __unicode__(self):
        return u'%s, %s' % (self.from_friend.username, self.to_friend.username)
    class Meta:
        unique_together = (('to_friend', 'from_friend'),)

Выполним синхронизацию базы данных.

python manage.py syncdb

Посмотрим код SQL.

python manage.py sql

Потренируемся в командной строке Django.

from bookmarks.models import *
from django.contrib.auth.models import User
user1 = User.objects.get('id=1')
user2 = User.objects.get('id=2')
user3 = User.objects.get('id=3')

friendship1 = Friendship(from_friend=user1, to_friend=user2)
friendship1.save()
friendship2 = Friendship(from_friend=user1, to_friend=user3)
friendship2.save()

user1.friend_set.all()

[friendship.to_friend for friendshipt in user1.friend_set.all()]

user2.friend_set.all()

friendship3 = Friendship(from_friend=user2, to_friend=user1)
friendship3.save()
user2.friend_set.all()

Теперь отредактируем файл bookmarks/views.py

def friends_page(request, username):
    user = get_object_or_404(User, username=username)
    friends = [friendship.to_friend for friendship in user.friend_set.all()]
    friend_bookmarks = Bookmark.objects.filter(user__in=friends).order_by('-id')
    variables = RequestContext(request, {
        'username': username,
        'friends': friends,
        'bookmarks': friend_bookmarks[:10],
        'show_tags': True,
        'show_user': True
    })
    return render_to_response('friends_page.html', variables)

Создадим шаблон friends_page.html в папке templates

{% extends "base.html" %}

{% block title %}
    Friends for {{ username }}
{% endblock %}

{% block head %}
    Friends for {{ username }}
{% endblock %}

{% block content %}
    <h2>Friend List</h2>
        {% if frineds %}
            <ul class="friends">
            {% for friend in friends %}
                <li><a href="/user/{{ friend.username }}/">{{ friend.username }}</a></li>
            {% endfor %}
            </ul>
        {% else %}
            <p>No friends found.</p>
        {% endif %}
        <h2>Latest Friend Bookmarks</h2>
        {% include "bookmark_list.html" %}
{% endblock %}

Добавим новый url в файл urls.py

urlpatterns = patterns('',
    [...]
    # Friends
    (r'^friends/(\w+)/$', friends_page),
)

Перейдем в браузере по адресу http://127.0.0.1:8000/friends/dmitriy/ и увидим страницу друзей.

Добавим новый акшон-метод в файл bookmarks/views.py

@login_required
def frind_add(request):
    if 'username' in request.GET:
        friend = get_object_or_404(User, username=request.GET['username'])
        friendship = Friendship(from_friend=request.user, to_friend=friend)
        friendship.save()
        return HttpResponseRedirect('/friends/%s/', request.user.username)
    else:
        raise Http404

Добавим новый адрес в файл urls.py

urlpatterns = patterns('',
    [...]
    # Friends
    (r'^friend/(\w+)/$', friends_page),
    (r'^friend/add/$', friend_add),
)

Изменим метод user_page  в файле bookmarks/views.py

def user_page(request, username):
    user = get_object_or_404(User, username=username)
    query_set = user.bookmark_set.order_by('-id')
    paginator = Paginator(query_set, ITEMS_PER_PAGE)
    if reques.user.is_authenticated():
        is_friend = Friendship.objects.filter(from_friend=request.user, to_friend=user)
    else:
        is_friend = False
    try:
        page_number = int(requesr.GET['page'])
    except (KeyError, ValueError):
        page_number = 1
    try:
        page = paginator.page(page_number)
    except InvalidPage:
        raise Http404
    bookmarks = page.object_list
    variables = RequestContext(request, {
        'username': username,
        'bookmarks': bookmarks,
        'show_tags': True,
        'show_edit': username == request.user.username,
        'show_paginator': paginator.num_pages > 1,
        'has_prev': page.has_previous(),
        'has_next': page.has_next(),
        'page': page_number,
        'pages': paginator.num_pages,
        'next_page': page_number + 1,
        'prev_page': page_number,
        'is_friend': is_friend,
    })

Теперь отредактируем шаблон templates/user_page.html

[...]
{% block content %]
    {% ifequal user.username usernaem %}
        <a href="/friends/{{ username }}/">views your friends</a>
    {% else %}
        {% if is_friend %}
            <a href="/friends/{{ user.username }}/">{{ username }}</a>
        {% else %}
            <a href="/friend/add/?username={{ username }}">add {{ username }} to your friends</a>
        {% endif %}
        - <a href="/friends/{{ username }}/">view {{ username }}'s friends</a>
    {% endifequal %}
    {% include "bookmark_list.html" %}
{% endblock %}

Отредактируем файл settings.py для рассылки e-mail

SITE_HOST = '127.0.0.1:8000'
DEFAULT_FROM_EMAIL = 'Django Bookmarks <django.bookmarks@example.com>'
EMAIL_HOST = 'mail.yourisp.com'
EMAIL_PORT = ''
EMAIL_HOST_USER = 'username'
EMAIL_HOST_PASSWORD = 'password'

Проверил настройки в консоли Django.

from django.core.mail import send_mail
send_mail(mail('Subject', 'Body of the message.', 'from@example.com', ['your_email@example.com'])

Здесь вы можете заменить E-mail свой.

Создадим новый класс базы данных Invitation в файле bookmarks/models.py

class Invitation(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()
    code = models.CharField(max_length=20)
    sender = models.ForeignKey(User)
    def __unicode__(self):
        return u'%s, %s' % (self.sender.username, self.email)

Соответсвенно доработаем файл bookmarks/models.py

from django.core.mail import send_mail
from django.template.loader import get_template
from django.template import Context
from django.conf import settings

class Inaviatation(models.Model):
    [...]
    def send(self):
        subject = u'Invitation to join Django Bookmarks'
        link = 'http://%s/friend/accept/%s/' % (settings.SITE_HOST, self.code)
        template = get_template('invitation_email.txt')
        context = Context({
            'name': self.name,
            'link': link,
            'sender': self.sender.username,
        })
        message = template.render(context)
        send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [self.email])

В папке templates создадим файл invitation_email.txt

Hi {{ name }},
{{ sender }} invited you to join Django Bookmarks,
a website where you can post and share your bookmarks with friends!
To accept the invitation, please click the link below:
{{ link }}
-- Django Bookmarks Team

Создадим новую форму в файле bookmarks/forms.py

class FriendInviteForm(form.Form):
    name = forms.CharField(label=u'Friend\'s Name')
    email = forms.EmailField(label=u'Friend\'s Email')

Отредактируем файл bookmarks/views.py

@login_required
def friend_invite(request):
    if request.method == 'POST':
        form = FriendInviteForm(request.POST)
        if form.is_valid():
            invitation = Invitation(
                name=form.cleaned_data['name'],
                email=form.cleaned_data['email'],
                code=User.objects.make_random_password(20),
                sender=request.user
            )
            invitation.save()
            invitation.send()
            return HttpResponseRedirect('/friend/invite/')
        else:
            form = FriendInviteForm()
        variables = RequestContext(request, {'form': form})
        return render_to_response('friend_invite.html', variables)

Добавим новый шаблон friend_invite.html в папку templates

{% extends "base.html" %}

{% block title %}
    Invite A Friend
{% endblock %}

{% block head %}
    Invite A Friend
{% endblock %}

{% block content %}
Enter your friend name and email below, and click "send invite" to invite your friend to join the site:
<form method="post" action=".">
    {{ form.as_p %}
    <input type="submit" value="send invite" />
</form>
{% endblock %}

Добавим url в файл urls.py

urlpatterns = patterns('',
    [...]
    # Friends
    (r'^friends/(\w+)/$', friends_page),
    (r'^friends/add/$', friend_add),
    (r'^friend/invite/$', friend_invite),
)

Перейдем в браузере по адресу http://127.0.0.1:8000/friend/invite/ и увидим форму приглашения друга.

Создадим новую ссылку в файле urls.py

urlpatterns = patterns('',
    [...]
    # Friends
    (r'^friends/(\w+)/$', friends_page),
    (r'^friend/add/$', friend_add),
    (r'^friend/invite/$', friend_invite),
    (r'^friend/accept/(\w+)/$', friend_accept),
)

Изменим файл bookmarks/views.py

def friend_accept(request, code):
    invitation = get_object_or_404(Invitation, code__exact=code)
    request.session['invitation'] = invitation.id
    return HttpResponseRedirect('/register/')

def register_page(request):
    if request.method == 'POST':
        form = RegistrationForm(request.POST)
        if form.is_valid():
            user = User.objects.create_user(
                username = form.cleaned_date['username'],
                password = form.cleaned_data['password1'],
                email = form.cleaned_data['email']
            )
            if 'invitation' in request.session:
                # Получить invitation object
                invitation = Invitation.objects.get(id=request.session['invitation'])
                # Создать friendship от user к sender
                friendship = Friendship(from_friend=user, to_friend=invitation.sender)
                friendship.save()
                # Создать friendship от sender к user
                friendship = Frindship(from_friend=invitation.sender, to_friend=user)
                friendship.save()
                # Удалить invitation из базы данных и session
                invitation.delete()
                del request.session['invitation']
            return HttpResponseRedirect('/register/success/')
        else:
            form = RegistrationForm()
        variables = RequestContext(request, {'form': form})
        return render_to_response('registration/register.html', variables)

Изменим шаблон в файле templates/friends_page.html

{% extends "base.html" %}

{% block title %}
    Friends for {{ user }}
{% endblock %}

{% block head %}
    Friends for {{ user }}
{% endblock %}

{% block content %}
    <h2>Friend List</h2>
    {% if friends %}
        <ul class="friends">
            {% for friend in friends %}
                <li><a href="/user/{{ friend.username }}/">{{ friend.username }}</a></li>
            {% endfor %}
        </ul>
    {% else %}
        <p>No friends found.</p>
    {% endif %}
    <a href="/friend/invite/">Invite a friend!</a>
    <h2>Latest Friend Bookmarks</h2>
    {% include 'bookmark_list.html' %}
{% endblock %}

Изменим базовый шаблон в файле templates/base.html

<body>
    <div id="nav">
        [...]
    </div>
    <h1>{% block head %}{% endblock %}</h1>
    {% if messges %}
        {% for message in message %}
            <li>{{ message}}</li>
        {% endfor %}
    {% endif %}
    {% block content %}{% endblock %}
</body>
</html>

Добавим новые стили в файл site_media/style.css

ul.messages {
    border: 1px dashed #000;
    margin: 1em 4em;
    padding: 1em;
}

Изменим метод friend_invite в файле bookmarks/views.py

import smtplib

@login_required
def frind_invite(request):
    if request.method == 'POST':
        from = FriendInviteForm(request.POST)
        if form.is_valid():
            invitation = Invitation(
                name=form.cleaned_data['name'],
                email=form.cleaned_data['email'],
                code=User.objects.make_random_password(20),
                sender=request.user
            )
            invitation.save()
            try:
                invitation.send()
                request.user.message_set.create(message=u'An invitation was sent to %s' % invitation.email)
            except smtplib.SMTPException:
                request.user.message_set.create(message=u'An error happen when sending the invitation')
            return HttpResponseRedirect('/friend/invite/')
        else:
            form = FriendInviteForm()
        variables = RequestContext(request, {
            'form': form
        })
        return render_to_response('friend_invite.html', variables)

Перейдем в браузере по адресу http://127.0.0.1:8000/friend/invite/

Внесем изменения в файл bookmarks/views.py

@login_required
def friend_add(request):
  if 'username' in request.GET:
     friend = get_object_or_404(User, username=request.GET['username'])
     friendship = Friendship(from_friend=request.user, to_friend=friend)
     try:
         friendship.save()
         request.user.message_set.create(message=u'%s was added to your friend list.' % friend.username)
    except:
         request.user.meassge_set.create(message=u'%s is already a friend of yours.' % friend.username)
    return HttpResponseRedirect('/friends/%s' % request.user.username)
  else:
    raise Http404

Теперь сделаем перевод сообщений на другой язык.

Изменим файл bookmarks/views.py

from django.utils.translation import ugetext as _

@login_required
def friend_invite(request):
    if request.method == 'POST':
        form = FriendInvitationForm(request.POST
        if form.is_valid():
            invitation = Invitation(
                name=form.cleaned_data['name'],
                email=form.cleaned_data['email'],
                code=User.objects.make_random_password(20),
                sender=request.user
            )
            invitation.save()
            try:
                invitation.send()
                request.user.message_set.create(message=_(u'An invitation was sent to %s' % invitation.email))
            except smtplib.SMTPException:
                request.user.meassage_set.create(message=_(u'An error happend when sending the invitation.'))
                return HttpresponseRedirect('/friend/invite/')
            else:
                form = FriendInviteForm()
            variables = RequestContext(request, {
                'form': form
            })
            return render_to_response('friend_invite.html', variables)

Изменим шаблон friend_invite.html в папке templates

{% extends "base.html" %}

{% load i18n %}

{% block title %}
    {% trans 'Invite A Friend' %}
{% endblock %}

{% block head %}
    {% trans 'Invite A Friend' %}
{% endblock %}

{% block content %}
    {% trans 'Enter your friend name and email below, and click "send invite" to invite your friend to join the site:' %}
    <form method="post" action=".">
        {{ form.as_p %}
        <input type="submit" value="{% trans 'send invite' %}" />
    </form>
{% endblock %}

Изменим форму в файле bookmarks/forms.py

from django.ustils.translation import ugettext_lazy as _

class FriendInviteForm(forms.Form):
    name=forms.CharField(label=_("Friend's Name"))
    email=forms.EmailField(label=_("Friend's Email"))

Управление подключением языков перевода в файле settings.py

USE_I18N = True

LANGUAGES = (
    ('en': 'English'),
    ('de': 'German'),
)

LANGUAGE_CODE = 'de'

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.locale.LocaleMiddleware',
)

Добавим url на перевод в файл urls.py

urlpatterns = patterns('',
    [...]
    # i18n
    (r'i18n/', include('django.conf.urls.i18n')),
)

Изменим основной шаблон templates/base.html

{% load i18n %}

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
[...]
</head>
<body>
[...]
    <div id="footer">
       <form action="/i18n/setlang/" method="post">
           <select name="language">
               {% for lang in LANGUAGES %}
                   <option value="{{ lang.0 }}">{{ lang.1 }}</option>
               {% endfor %}
           </select>
           <input type="submit" value="Go" />
       </form>
    </div>
</body>
</html>
Добавим новые стили в файл site_media/style.css

#footer {
    margin-top: 2em;
    text-align: center;
}

#footer input {
    display: inline;
}

Включим кэширование в файле settings.py

CACHE_BACKEND = 'locmem:///'

Кэшированные данные будут хранится в базе данных.
Создадим таблицу в базе данных для кэширования.

python manage.py createcachetable cache_table

Добавим ссылку на созданную таблицу в файл settings.py

CACHE_BACKEND = 'db://cache_table'

Если мы захотим кэшировать данные в файл, то создадим такую строчку в файле settings.py

CACHE_BACKEND = 'file:///tmp/django_cache'

В виндовсе адрес файла для кэширования будет выглядеть так

С:\tmp\django_cache

Если вы используете фреймворк Memcached, то можете прописать IP-адрес до его сервера.

CACHE_BACKEND = 'memcached://ip:port'

Далее в файле settings.py надо указать время кэширования страниц в секундах.

CACHE_MIDDLEWARE_SECONDS = 60 * 5

Для кэширования всего сайта внесите в файл settings.py следующие изменения

MIDDLEWARE_CLASSES = (
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
)

Для кэширования специфических контроллеров внесем изменения в файл bookmarks/views.py

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def tag_cloud_page(request):
[...]

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

python manage.py shell

from django.test.client import Client
client = Client()
response = client.get('/')
print response
print client.post('/login/', {'username': 'user', 'password': 'password'})
client.login(username='your_username', password='your_password')

Протестируем страницу регистрации.

В папке приложения bookmarks создадим файл tests.py

from django.test import TestCase
from django.test.client import Client

class ViewTest(TestCase):
    def setUp(self):
        self.client = Client()
    def test_register_page(self):
        data = {
            'username': 'test_user',
            'email': 'test_user@example.com',
            'password1': 'pass123',
            'password2': 'pass123',
        }
        response = self.client.post('/register/', data)
        self.assertRedirects(response, '/register/success/')

Типы утверждений в Django.

assertEqual - ожидает, что второе значение будет эквивалентно первому.
assertNotEquals - ожидает, что второе значение не будет эквивалентно первому.
assertTrue - ожидает, что значение будет равно True.
assertFalse - ожидает, что значени будет равно False.
assertRedirects - ожидает ответ на редирект по заданному URL.
assertContains - ожидает, что ответ содержит часть текста.

Теперь протестируем весь класс bookmarks

python manage.py test bookmarks

Модифицируем метод test_register_page в файле tests.py

def test_register_page(self):
    data = {
        'username': 'test_user',
        'email': 'test_user@example.com',
        'password1': '1',
        #'password2': '1'
    }
    response = self.client.post('/register/', data)
    self.assertRedirects(response, '/register/success/')

Внесем еще изменения в файл bookmarks/tests.py

class ViewTest(TestCase):
    [...]
    def test_bookmark_save(self):
        response = self.client.login(username='your_username', password='your_password')
        self.assertTrue(response, msg='Failed to login.')
        data = {
            'url': 'http://www.example.com/',
            'title': 'Test URL',
            'tags': 'test-tag'
        }
        response = self.client.post('/save/', data)
        self.assertRedirects(response, '/user/your_username/')
        response = self.client.get('/user/your_username/')
        self.assertContains(response, 'http://www.example.com/')
        self.assertContains(response, 'Test URL')
        self.assertContains(response, 'test-tag')

Скопируем значения из базы данных в текстовый файл

python manage.py dumpdata bookmarks auth > test_data.json

Изменим класс ViewTest в файле bookmarks/tests.py

class ViewTest(TestCase):
    fixtures = ['test_data.json']
    [...]

Перед размещение Django на реальном сервере выключите режим отладки в файле settings.py

DEBUG = False

Измените переменную администратора

ADMINS = (
    ('Your Name', 'your_email@domain.com'),
)

Обновите переменные :

EMAIL_HOST
EMAIL_PORT
EMAIL_HOST_USER
EMAIL_HOST_PASSWORD

Создайте шаблоны страниц 404.html и 500.html

Добавим папку templatetags в папку bookmarks и поместите туда пустой файл __init__.py.
Создадим в папке templatetags модуль bookmarks_filters.py

В итоге мы получим такую структуру.

django_bookmarks/
    bookmarks/
        templatetags/
            __init__.py
            bookmarks_filters.py

Добавим код в файл bookmarks_filters.py

from django import template

register = template.Library()

@register.filter
def capitalize(value):
    return value.capitalize()

Теперь мы можем загрузить наш фильтр в шаблон командой

{% load bookmarks_filters %}

и использовать его так

Hi {{ name|capitalize }}!

Пример создания SQL-запросов

from django.db import connection

Query = '-- SQL code goes here. --'
cursor = connection.cursor()
cursor.execute(query)
rows = cursor.fetchall()
cursor.execute('SELECT * FROM auth_user WHERE username = %s AND password = %s', (username, email))

Внутренние фреймворки Django:

admin - админка Django
auth - система авторизации пользователей сайта
sessions - фреймвор создания сессий и сессионных куки
syndication - генератор RSS

Комментариев нет:

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