Django cms на русском


Страницы CMS и контроль версий | Recipes

Часто спрашивают, как отслеживать изменение структуры сайта на Django-CMS в системах контроля версий.

В чём, собственно, проблема?

Бывает, заказчик ставит задачу разработчикам так:

  1. Добавить страницу «О компании» по адресу /about/;
  2. Разместить на странице текст «мы самые крутые».
Как такое рекомендуется делать в Django-CMS:

Все эти действия приводят к изменениям в БД, но ни один файл при этом не затронут. Соответственно, страница добавлена, а в коммит добавлять нечего. Каждый разработчик должен у себя самостоятельно создать новую страницу, а на тестовом сервере и в продакшне ещё отдельно сделать то же самое? Но это же не наш метод!

И что же делать?

На помощь нам приходят миграции данных и cms.api. Миграции данных позволяют управлять контентом в БД. Cms.api позволяет выполнять действия по управлению контентом.

Итак, создаём пустую миграцию:

python manage.py makemigrations app_name --empty

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

from cms.api import create_page, add_plugin, publish_page from django.contrib.auth.models import User def add_pages(apps, schema_editor): page = create_page( 'about', 'default.html', 'ru' ) placeholder = page.placeholders.get(slot='content') add_plugin(placeholder, 'TextPlugin', 'ru', body='Мы самые крутые') try: user = User.objects.filter(is_superuser=True).first() publish_page(page, user, 'ru') except User.DoesNotExists: print("стоит создать суперюзера, иначе опубликовать страницу можно будет только из админки, для чего всё равно нужен суперюзер")

Теперь в классе миграции можно определять действия: class Migration(migrations.Migration):

operations = [ migrations.RunPython(add_pages) # это имя вышеопределённой функции ]

Применяем эту миграцию, и видим новую уже опубликованную страницу, на которой размещён текстовый плагин, и в нём набран нужный текст.

А вот миграцию уже можно определить в коммит. Что и требовалось.

djbook.ru

Глава 14: DjangoBook по-русски | Django на русском

Стандартная Django находится в пакете django.contrib. В каждом субпакете находится отдельная часть дополнительного функционала. Эти части не всегда зависят друг от друга, но некоторые могут.

Нет жёсткого требования для типов функциональности в django.contrib. Некоторые из пакетов включают в себя модели (и следовательно требуют, чтобы вы установили их таблицы в базу данных), но другие могут состоять полностью из кода или шаблонов.

Единственная общая особенность пакетов django.contrib — если вы полностью удалите пакет django.contrib, вы сможете использовать основные компоненты Django без проблем. Когда разработчики добавляют новую функциональность в Django они используют это правило при рассмотрении вопроса о том, должна ли эта функциональность быть размещена в django.contrib или где-то в другом месте.

Библиотека django.contrib содержит следующие пакеты:

Остальная часть этой главы посвящена деталям каждого модуля пакета django.contrib, которые мы ещё не рассматривали в нашей книге.

Среда управления комментариями

Перевод статьи http://code.djangoproject.com/wiki/UsingFreeComment.

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

Система комментариев является приложением Django. Следовательно, требуется прописать его в параметре INSTALLED_APPS файла конфигурации проекта:

INSTALLED_APPS = ( [...] 'django.contrib.comments', )

Если вы используете свои представления и точно не используете базовые представления, вам потребуется добавить нижеприведённую строчку в начала соответствующего файла urls.py. Базовые представления сами подключают этот класс, при их использовании об этом можно не беспокоиться.

from django.contrib.comments.models import FreeComment

Добавьте следующий шаблон URL в глобальный файл привязки:

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

Потребуется обновить вашу базу данных, чтобы там появилась необходимая таблица. Для этого следует остановить веб сервер и выполнить python manage.py syncdb.

В каждом шаблонном файле, который должен будет предоставлять доступ к комментариям, следует подгрузить соответствующий пакет (вставьте эту строку в самое начало шаблона):

{% load comments %}
Информация о количестве комментариев

Для получения доступа к комментариям объекта вам нужен только идентификатор этого объекта. При его наличии вы можете получить сами комментарии, информацию об их количестве и другую информацию.

django.views.generic.list_detail.object_list

В шаблоне object_list вы можете делать следующее для того, чтобы добавить информацию о количестве комментариев для каждой записи блога в списке. Нижеприведённый пример предполагает, что вы работаете над приложением blog с классом entry, у которого есть поля title и summary, а также метод get_absolute_url(), который возвращает абсолютный URL для страницы с записью блога.

Замечание

Следует отметить, что имя класса должно всегда быть указано в нижнем регистре. Например, если класс имеет имя Entry, все обращения к нему будут идти как к blog.entry.

<ul> {% for object in object_list %} {% get_free_comment_count for blog.entry object.id as comment_count %} <li> <h3><a href="{{ object.url }}">{{ object.title }}</a></h3> <p>{{ object.summary}}</p> <p> <a href="{{ object.get_absolute_url }}"> {{ comment_count }} Comments </a> </p> </li> {% endfor %} </ul>
django.views.generic.date_based.archive_index

Базовое представление archive_index работает почти так как описанное в предыдущей секции, только итерация происходит по объектам из коллекции latest, а не по object_list:

<ul> {% for object in latest %} {% get_free_comment_count for blog.entry object.id as comment_count %} [...] {% endfor %} </ul>

В архивах, отсортированных по дате (таких как archive_year или archive_month), коллекция, по которой производится итерация называется «object_list». archive_index является только базовым представлением с latest.

Добавление комментариев на страницу

Обычно добавление комментариев разрешено пользователям на страницах с детальной информацией об объекте. Можно разрешить добавление комментариев откуда угодно, но для простоты данный пример не будет так делать. При использовании любых базовых представлений для отображения детальной информации, таких как django.views.generic.list_detail.object_detail или django.views.generic.date_based.object_detail, у вас будет идентификатор объекта в object.id, таким образом получение информации о количестве комментариев для этого объект будет несложным:

{% get_free_comment_count for blog.entry object.id as comment_count %}

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

{% get_free_comment_list for blog.entry object.id as comment_list %}

Каждый объект object в comment_list имеет следующие поля:

А также следующие встроенные методы:

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

Пример:

{% get_free_comment_count for blog.entry object.id as comment_count %} <h3><a href="{{ object.url }}">{{ object.title }}</a></h3> <em>{{ object.description }}</em> <div> <b>Added on {{ object.add_date|date:"F j, Y" }}</b> <a href="{{ object.get_absolute_url }}#comments">{{ comment_count }} Comment{{ comment_count|pluralize }}</a> </div> {% get_free_comment_list for blog.entry object.id as comment_list %} <h3>Comments</h3> {% for comment in comment_list %} <div> <span> <a href="#c{{ comment.id }}"> #{{ forloop.counter }} </a> </span> <p> <b>{{ comment.person_name|escape }}</b> commented, on {{ comment.submit_date|date:"F j, Y" }} at {{ comment.submit_date|date:"P" }}: </p> {{ comment.comment|escape|urlizetrunc:40|linebreaks }} </div> {% endfor %} <h3>Post a comment</h3> {% free_comment_form for blog.entry object.id %}

Django поставляется со встроенными стандартными шаблонами для системы комментирования.

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

Шаблон с формой отправки комментария

Этот шаблон (freeform.html) содержит код формы, с помощью которой пользователь отправляет комментарий. В вышеприведённом примере, он подключался так:

<h3>Post a comment</h3> {% free_comment_form for blog.entry object.id %}

Пример шаблона:

{% if display_form %} <form action="/comments/postfree/" method="post"> <p>Your name: <input type="text" name="person_name" /></p> <p>Comment:<br /><textarea name="comment" rows="10" cols="60"></textarea></p> <input type="hidden" name="options" value="{{ options }}" /> <input type="hidden" name="target" value="{{ target }}" /> <input type="hidden" name="gonzo" value="{{ hash }}" /> <p><input type="submit" name="preview" value="Preview comment" /></p> </form> {% endif %}

Если вы не желаете использовать страницу для предварительного просмотра комментария, просто замените name="preview" на name="post".

Шаблон для предварительного просмотра

Этот шаблон (free_preview.html)используется при просмотре пользователем своего комментария перед его отправкой.

Пример:

<h2>Preview your comment</h2> <form action="/comments/postfree/" method="post"> {% if comment_form.has_errors %} <p><strong>Please correct the following errors.</strong></p> {% else %} <div> {{ comment.comment|escape|urlizetrunc:"40"|linebreaks }} <p>Posted by <strong>{{ comment.person_name|escape }}</strong></p> </div> <p><input type="submit" name="post" value="Post public comment" /></p> <h2>Or edit it again</h2> {% endif %} {% if comment_form.person_name.errors %} {{ comment_form.person_name.html_error_list }} {% endif %} <p><label for="id_person_name">Your name:</label> {{ comment_form.person_name }}</p> {% if comment_form.comment.errors %} {{ comment_form.comment.html_error_list }} {% endif %} <p> <label for="id_comment">Comment:</label> <br /> {{ comment_form.comment }} </p> <input type="hidden" name="options" value="{{ options }}" /> <input type="hidden" name="target" value="{{ target }}" /> <input type="hidden" name="gonzo" value="{{ hash }}" /> <p> <input type="submit" name="preview" value="Preview revised comment" /> </p> </form>
Шаблон для отправленного комментария

Этот шаблон (posted.html) отображается после того как пользователь отправит комментарий. Вы можете получить доступ к прокомментированному объекту через контекстную переменную object.

Пример:

<h2>Comment posted successfully</h2> <p>Thanks for contributing.</p> {% if object %} <ul> <li><a href="{{ object.get_absolute_url }}">View your comment</a></li> </ul> {% endif %}
Wrapping post_free_comment to change the redirect url

В настоящее время django.contrib.comments.views.comments в методе post_free_comment() имеет жёстко определённый адрес перенаправления на posted.html. Если вы желаете, чтобы отправка комментария возвращала пользователя на комментируемую страницу, вы можете переопределить метод post_free_comment() и добавить перенаправление в другое место после отправки комментария. URL может передаваться в виде скрытого параметра в шаблоне freeform.html. Для этого потребуется создать обработчик в views.py:

from django.contrib.comments.views.comments import post_free_comment from django.http import HttpResponseRedirect def my_post_free_comment(request): if request.has_key('url') and not request.has_key('preview'): response = post_free_comment(request) # Check there's a url to redirect to, and that post_free_comment worked if len(request['url'].strip()) > 0 and isinstance(response, HttpResponseRedirect): return HttpResponseRedirect(request['url']) # Fall back on the default post_free_comment response return response return post_free_comment(request)

После этого потребуется перенаправить запросу на это представление вместо оригинального. Для этого, перед строкой:

(r'^comments/', include('django.contrib.comments.urls.comments')),

надо добавить строку:

(r'^comments/postfree/', 'views.my_post_free_comment'),

Теперь надо добавить в форму скрытое поле с URL. Но как получить текущий URL в шаблонной переменной? Для этого потребуется активировать шаблонный контекстный процессор request добавив следующий код в файл settings.py:

from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS TEMPLATE_CONTEXT_PROCESSORS += ( 'django.core.context_processors.request', )

После активации контекстного процессора шаблонам будет доступна переменная request. Используя эту переменную вы можете выполнить request.get_full_path для получения текущего URL. Внесём эти изменения в freeform.html:

{% load i18n %} {% if display_form %} <form action="/comments/postfree/" method="post"> <p> <label for="id_person_name">{% trans "Your name:" %}</label> <input type="text" name="person_name" /> </p> <p> <label for="id_comment">{% trans "Comment:" %}</label><br /> <textarea name="comment" rows="10" cols="60"></textarea> </p> <p> <input type="hidden" name="options" value="{{ options }}" /> <input type="hidden" name="target" value="{{ target }}" /> <input type="hidden" name="gonzo" value="{{ hash }}" /> <input type="hidden" name="url" value="{{ request.get_full_path }}" /> <input type="submit" name="preview" value="{% trans "Preview comment" %}" /> </p> </form> {% endif %}

В free_preview.html добавим:

<input type="hidden" name="url" value="{{ request.url }}" />

перед

<p><input type="submit" name="preview" value="Preview revised comment" /></p>

Этого должно быть достаточно!

Другие примеры

Список свежих комментариев. Нижеприведённый код отображает в виде списка последние комментарии для всего вашего сайта, не обращая внимания на приложения:

<h2>Recent comments</h2> <p> {% if has_previous %} <a href="?page={{ previous }}">Previous</a> | {% endif %} Page {{ page }} of {{ pages }} {% if has_next %} | <a href="?page={{ next }}">Next</a> {% endif %} </p> {% for comment in object_list %} <div> <h4> <a href="{{ comment.get_absolute_url }}"> {{ comment.person_name|escape }} <span> {{ comment.submit_date|date:"F j, Y" }} at {{ comment.submit_date|date:"P" }} </span> </a> </h4> {{ comment.comment|escape|urlizetrunc:"40"|linebreaks }} </div> {% endfor %} <p> {% if has_previous %} <a href="?page={{ previous }}">Previous</a> | {% endif %} Page {{ page }} of {{ pages }} {% if has_next %} | <a href="?page={{ next }}">Next</a> {% endif %} </p>

djbook.ru

Глава 4: DjangoBook по-русски | Django на русском

Глава 4. Шаблоны

Перевод © Попов Руслан <ruslan.popov • gmail>

В предыдущей главе вы могли заметить что-то особенное в том, как мы возвращали текст в наших представлениях. А именно, HTML был напрямую вбит в код на Python, примерно так:

def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)

Несмотря на то, что данный подход был удобен для объяснения принципов работы представления, он не может быть признан годным для использования в реальных проектах. И вот почему:

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

Основы шаблонной системы

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

Давайте начнём с простого примера. Данный шаблон описывает HTML страницу, которая благодарит посетителя за заказ, отправленный компании. Рассматривайте его как вид письма:

<html> <head><title>Ordering notice</title></head> <body> <h2>Ordering notice</h2> <p>Dear {{ person_name }},</p> <p>Thanks for placing an order from {{ company }}. It's scheduled to ship on {{ ship_date|date:"F j, Y" }}.</p> <p>Here are the items you've ordered:</p> <ul> {% for item in item_list %} <li>{{ item }}</li> {% endfor %} </ul> {% if ordered_warranty %} <p>Your warranty information will be included in the packaging.</p> {% else %} <p>You didn't order a warranty, so you're on your own when the products inevitably stop working.</p> {% endif %} <p>Sincerely,<br />{{ company }}</p> </body> </html>

Данный шаблон представляет собой обычный HTML с несколькими переменными и шаблонными тегами. Рассмотрим его подробнее:

Каждый шаблон Django имеет доступ к некоторому количеству встроенных тегов и фильтров, многие из которых будут рассмотрены в последующих разделах главы. Приложение F FIXME содержит полный список тегов и фильтров, будет неплохо, если вы ознакомитесь с этим списком. Также есть возможность создавать свои собственный фильтры и теги, мы рассмотрим это в главе 9 FIXME.

djbook.ru

Глава 3: DjangoBook по-русски | Django на русском

В качестве вашего первого задания, давайте создадим страницу, которая будет выводить знаменитое сообщение: «Здравствуй, Мир».

Если бы вы создавали обычную страницу с текстом «Здравствуй, Мир» без использования Django, то достаточно было бы создать файл с таким текстом, дать ему имя hello.html и загрузить в каталог на веб сервере. Следует отметить, что в этом случае, вы определяете два ключевых параметра страницы: её содержимое (строка «Здравствуй, Мир») и её URL (http://www.example.com/hello.html).

Используя Django, вы указываете те же самые параметры, но другим способом. Содержимое страницы создаётся с помощью функции представления, а URL определяется в файле привязок URL. Сначала давайте напишем нашу функцию представления.

Ваше первое представление

Внутри каталога mysite, который был создан с помощью команды django-admin.py startproject в предыдущей главе, создайте пустой файл с именем views.py. Этот файл будет содержать представления, описываемые в данной главе. Следует отметить, что нет ничего особенного в выбранном нами имени файла. Django нет никакой разницы как он называется и вы убедитесь в этом сами. Но, назвать так файл — это хорошая идея, другим разработчикам будет проще ориентироваться в вашем коде.

Наше представление простое, оно состоит из кода функции и оператора импорта. Вот содержимое файла views.py:

# -*- coding: utf-8 -*- from django.http import HttpResponse def hello(request): return HttpResponse("Здравствуй, Мир")

Пройдёмся по коду строка за строкой:

Основная идея в том, что представление является функцией языка Python, которая принимает HttpRequest в качестве первого параметра и возвращает экземпляр HttpResponse. Для того, чтобы функция языка Python стала функцией представления Django, он должен соответствовать этим двум правилам. Есть исключения из этих правил, но мы рассмотрим их позже.

Ваша первая привязка URL

Если в этот момент вы бы запустили снова команду python manage.py runserver, то вы бы увидели сообщение «Welcome to Django» без единого следа нашего представления. Так происходит потому, что наш проект mysite ещё не знает о представлении hello. Нам потребуется явно указать Django, что мы активируем данное представление для конкретного URL. (Продолжая нашу аналогию с публикацией статичных HTML файлов, как будто бы мы создали HTML файл, но ещё не закачали его в каталог на сервере.) Для привязки функции представления к конкретному URL в Django используются файлы привязки URL.

Файл привязки URL можно рассматривать как таблицу с содержанием вашего сайта. Проще говоря, этот файл определяет соответствие между URL и функциями представления, которые должны быть вызваны для этих URL. Именно так вы указываете Django: «Для данного URL, вызывай этот код, а для этого URL вызывай вот этот код.» Например, «Если кто-нибудь посетит URL /foo/, вызывай функцию представления foo_view(), которая расположена в модуле views.py.»

Когда вы запустили django-admin.py startproject в предыдущей главе, скрипт автоматически создал для вас файл привязки: urls.py. По-умолчанию, он выглядит примерно так:

from django.conf.urls.defaults import * # Uncomment the next two lines to enable the admin: # from django.contrib import admin # admin.autodiscover() urlpatterns = patterns('', # Example: # (r'^mysite/', include('mysite.foo.urls')), # Uncomment the admin/doc line below and add 'django.contrib.admindocs' # to INSTALLED_APPS to enable admin documentation: # (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: # (r'^admin/', include(admin.site.urls)), )

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

from django.conf.urls.defaults import * urlpatterns = patterns('', )

Давайте пройдём по каждой строчке этого кода:

Основным элементом данного кода является переменная urlpatterns, которую Django ожидает найти в вашем файле привязки. Эта переменная определяет соответствие между списком URL и кодом, который обрабатывает данные URL. По-умолчанию, как вы можете видеть, файл привязки пуст — ваше Django приложение ещё не настроено. (Именно так Django определяет, что следует показывать страницу «Welcome to Django». Если ваш файл привязки пуст, Django предполагает, что вы только что начали новый проект и, следовательно, отображает данное сообщение.)

Для того, чтобы добавить URL и представление в файл привязки, просто добавьте кортеж, объединяющий URL и функцию представления. Ниже показана привязка для нашего представления hello:

from django.conf.urls.defaults import * from mysite.views import hello urlpatterns = patterns('', ('^hello/$', hello), )

(Следует отметить, что мы убрали закомментированные строчки для ясности. Вы можете оставить эти строки, если вам захочется.)

Мы сделали следующие изменения:

В двух словах, мы просто указали Django, что любой запрос к URL /hello/ должен быть обработан с помощью функции представления hello.

Ваш PYTHONPATH

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

Например, допустим, что путь имеет следующее значение:

['', '/usr/lib/python2.4/site-packages', '/home/username/djcode/']

При выполнении кода:

from foo import bar

интерпретатор сначала проверит наличие файла foo.py в текущем каталоге. (Первым элементом списка идёт пустая строка, которая означает «текущий каталог».) Если такого файла там нет, то интерпретатор ищет файл в каталоге /usr/lib/python2.4/site-packages, затем в каталоге /home/username/djcode/. Наконец, если файл всё ещё не найден, интерпретатор возвращает ImportError.

Если вам интересно посмотреть на значения пути, запустите интерпретатор и выполните следующие команды:

>>> import sys >>> print sys.path

В общем случае вам не требуется беспокоиться о настройке пути. Это задача Python и Django, они сами об этом позаботятся. (Настройка пути, это одна из задач, которыми занимается скрипт manage.py.)

Стоит обсудить синтаксис данного шаблона URL, так как он может быть не совсем очевиден. Несмотря на то, что шаблон должен совпадать с /hello/, он выглядит немного не так. Объясним почему:

Следует отметить ещё один важный момент. В шаблоне URL мы передаём функцию представления hello в виде объекта, без его вызова. Это возможно благодаря ключевой особенности языка Python (и любого другого динамического языка): функции являются объектами первого рода, это означает, что вы можете передавать их в другие объекты аналогично переменным. Прикольно, не так ли?

Для того, чтобы протестировать наши изменения внесённые в файл привязок, запустите тестовый сервер, как это было показано в главе «Начинаем», выполнив команду python manage.py runserver. (Если вы не останавливали его работу, это тоже неплохо. Тестовый сервер автоматически обнаруживает изменения в вашем коде и выполняет его перезагрузку по необходимости, таким образом, вам не надо делать это вручную.) Сервер привязывается к адресу http://127.0.0.1:8000/, так что откройте браузер и пройдите по ссылке http://127.0.0.1:8000/hello/. Вы должны увидеть текст «Здравствуй, Мир» — результат работы вашего представления.

Ура! Вы создали вашу первую страницу с помощью Django.

Регулярные выражения

Регулярные выражения[6] — компактный метод определения шаблонов в тексте. В то время как схема URL Django позволяет использование определённых регулярных выражений для работы с URL, вероятно на практике вы будете использовать не так много регулярных выражений для шаблонов. В таблице «Выборка наиболее используемых шаблонов» приведена выборка наиболее используемых шаблонов.

Таблица 3.1. Выборка наиболее используемых шаблонов

СимволСовпадает
. (точка)Любой символ
\dЛюбая цифра
[A-Z]Любая буква (верхний регистр)
[a-z]Любая буква (нижний регистр)
[A-Za-z]Любая буква (любой регистр)
+Один или более символов предыдущего выражения, т.е. \d+ совпадает с одной или более цифрами
[^/]+Все символы подряд, кроме слэша
?Наличие или отсутствие предыдущего выражения, т.е. \d? описывает возможное наличие одной цифры
{1, 3}От одного до трёх (символов) предыдущего выражения

Кратко об ошибках 404

В текущий момент наш файл привязок определяет единственный шаблон: тот, который обрабатывает запросы по URL URL. Что произойдёт при запросе другого URL?

Чтобы узнать это запустите тестовый сервер и посетите страницу такую как http://127.0.0.1:8000/goodbye/ или http://127.0.0.1:8000/hello/subdirectory/, или даже такую http://127.0.0.1:8000/ (корень сайта). Вы должны увидеть сообщение «Страница не найдена» (см. картинку FIXME). Django отображает это сообщение, потому что вы запросили URL, который не был определён в файле привязок.

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

Обычно, эта информация предназначена только для вас, для разработчика. В случае боевого сайта, выставленного в Интернет, врядли вы пожелаете показывать такую информацию неизвестным людям. По этой причине страница с подробностями об ошибке 404 отображается только в том случае, когда ваш проект находится в режиме отладки. Мы расскажем как деактивировать режим отладки позже. Сейчас просто запомните, что новый проект Django всегда создаётся со включенным режимом отладки, и что когда проект не находится в этом режиме, Django показывает совсем другое сообщение об ошибке 404.

djbook.ru

Глава 14, раздел 2: DjangoBook по-русски

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

Сценарий 1: Использование данных на множестве сайтов

Как мы рассказывали в главе «Введение в Django», сайты LJWorld.com и Lawrence.com управляются одной новостной компанией: газетой Lawrence Journal-World в городе Lawrence, штат Канзас. Сайт LJWorld.com сфокусирован на новостях, в то время как сайта Lawrence.com сосредоточен на местных развлечениях. Но иногда корреспонденты желают разместить свою статью на обоих сайтах.

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

Есть ли решение получше? Оба сайта используют единую базу данных для статей и статья ассоциирована с одним или несколькими сайтами с помощью отношения «многие-ко-многим». Среда управления сайтами предоставляет таблицу базы данных через которую статьи ассоциируются с сайтами.

Сценарий 2: Хранение информации о сайте в одном месте

Сайты LJWorld.com и Lawrence.com имеют функциональность для уведомления пользователей по электронной почте о появившихся новостях. Это очень просто: пользователь регистрируется на форме сайта и немедленно получает по электронной почте сообщение: «Спасибо за вашу подписку.»

Будет неэффективно и избыточно реализовывать код регистрации дважды, таким образом сайты должны использовать единый код. Но сообщение должно различаться для каждого сайта. Используя объекты Site, мы можем абстрагировать ваше уведомление так, что оно будет использовать атрибуты объекта name (т.е., LJWorld.com) и domain (т.е., www.ljworld.com).

Среда управления сайтами предоставляет место для хранения атрибутов name и domain для каждого сайта вашего проекта Django и это означает, что вы можете повторно использовать эти значения в общем виде.

Как это использовать

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

То, как будут использоваться эти две концепции, полностью зависит от вас, но Django автоматически использует их в ряде случаев.

Для того, чтобы установить приложение среды управления сайтами, выполните следующие шаги:

  1. Добавьте значение django.contrib.sites в параметр INSTALLED_APPS.

  2. Выполните команду manage.py syncdb для установки таблицы django_site в вашу базу данных.

  3. Добавьте один или более объектов Site, через интерфейс администратора Django или через Python API. Создайте объект Site для каждого сайта/домена, который управляется с помощью проекта Django.

  4. Определите параметр SITE_ID в каждом из ваших файлов конфигурации. Это значение должно быть идентификатором объекта Site в базе данных для сайта, которому принадлежит редактируемый файл конфигурации.

Следующие секции описывают различные варианты использования среды управления сайтами.

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

Для повторного использования данных на множестве сайтов, как было описано в «Сценарий 1: Использование данных на множестве сайтов», следует создать поле ManyToManyField в модели Site, например:

from django.db import models from django.contrib.sites.models import Site class Article(models.Model): headline = models.CharField(max_length=200) # ... sites = models.ManyToManyField(Site)

Эта инфраструктура необходима вам для ассоциирования статей со множеством сайтов в вашей базе данных. Используя это, вы можете использовать один и тот же код функции представления для множества сайтов. Продолжая работу с примером модели Article, так может выглядеть код представления article_detail:

from django.conf import settings def article_detail(request, article_id): try: a = Article.objects.get(id=article_id, sites__id=settings.SITE_ID) except Article.DoesNotExist: raise Http404 # ...

Эта функция представления может быть использована повторно, потому что она динамически проверяет сайт для статьи, учитывая значения параметра SITE_ID.

Например, предположим что SITE_ID для LJWorld.com имеет значение 1, а для Lawrence.com — значение 2. Если данное представление будет вызвано с использованием конфигурационного файла от LJWorld.com, то выборка статей будет ограничена теми записями, у которых в списке сайтов есть LJWorld.com.

Ассоциирование данных с единственным сайтом

Аналогично, вы можете ассоциировать модель к Site как многие-к-одному с помощью ForeignKey.

Например, если отображение статьи разрешено только для одного сайта, вы можете использовать следующую модель:

from django.db import models from django.contrib.sites.models import Site class Article(models.Model): headline = models.CharField(max_length=200) # ... site = models.ForeignKey(Site)

Такой подход имеет те же преимущества, аналогичные описанным в секции «Фильтры разметки».

Изменение логики представления в зависимости от текущего сайта

На низком уровне, вы можете использовать среду управления сайтами в ваших представлениях для выполнения определённых действия, основываясь на текущем сайте, например:

from django.conf import settings def my_view(request): if settings.SITE_ID == 3: # Делаем что-то. else: # Делаем что-то другое.

Конечно, руки бы оторвать за такой код с жёстким определением идентификаторов. Более явным способом выполнения тех же действий является проверка домена текущего сайта:

from django.conf import settings from django.contrib.sites.models import Site def my_view(request): current_site = Site.objects.get(id=settings.SITE_ID) if current_site.domain == 'foo.com': # Делаем что-то. else: # Делаем что-то другое.

Идея получения объекта Site для определения значения параметра конфигурации SITE_ID довольно стандартна, поэтому менеджер модели Site (Site.objects) имеет метод get_current(). Данный пример эквивалентен предыдущему:

from django.contrib.sites.models import Site def my_view(request): current_site = Site.objects.get_current() if current_site.domain == 'foo.com': # Делаем что-то. else: # Делаем что-то другое.

Замечание

В последнем примере нет необходимости импортирования django.conf.settings.

Отображение текущего домена

При использовании методики «Не повторяйся» название вашего сайта и его доменное имя, как было показано в секции «Сценарий 2: Хранение информации о сайте в одном месте», являются всего лишь ссылками на атрибуты name и domain объекта Site. Например:

from django.contrib.sites.models import Site from django.core.mail import send_mail def register_for_newsletter(request): # Check form values, etc., and subscribe the user. # ... current_site = Site.objects.get_current() send_mail('Thanks for subscribing to %s alerts' % current_site.name, 'Thanks for your subscription. We appreciate it.\n\n-The %s team.' % current_site.name, 'editor@%s' % current_site.domain, [user_email]) # ...

Продолжая наш пример с LJWorld.com и Lawrence.com, на последнем сообщение имеет заголовок «Thanks for subscribing to lawrence.com alerts.» На LJWorld.com, соответственно, такой — «Thanks for subscribing to LJWorld.com alerts.» Аналогичное поведение применяется и для тела сообщения.

Более гибким (но и более сложным) способом реализации такого поведения будет использование шаблонной системы Django. Предположим, что сайты LJWorld.com и Lawrence.com хранят свои шаблоны в разных каталогах (TEMPLATE_DIRS), тогда переложим ответственность на шаблонную систему:

from django.core.mail import send_mail from django.template import loader, Context def register_for_newsletter(request): # Check form values, etc., and subscribe the user. # ... subject = loader.get_template('alerts/subject.txt').render(Context({})) message = loader.get_template('alerts/message.txt').render(Context({})) send_mail(subject, message, '[email protected]', [user_email]) # ...

В данном случае вам потребуется создать шаблоны subject.txt и message.txt в обоих каталогах с шаблонами. Как упоминалось ранее, это даст вам большую гибкость, но и работы прибавится.

Хорошей идеей будет максимально возможное развитие объекта Site, чтобы удалить ненужную сложность и избыточность.

Текущий домен в URL

Метод get_absolute_url() удобен для получения URL без доменного имени, но в некоторых случаях может потребоваться отобразить полный URL. Для этого можно использовать среду управления сайтами. Вот простой пример:

>>> from django.contrib.sites.models import Site >>> obj = MyModel.objects.get(id=3) >>> obj.get_absolute_url() '/mymodel/objects/3/' >>> Site.objects.get_current().domain 'example.com' >>> 'http://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url()) 'http://example.com/mymodel/objects/3/'

Если объект Site играет ключевую роль в вашем приложении, рассмотрите использование CurrentSiteManager в ваших моделях. Это менеджер модели, описанный в приложении «Справочник определений модели», который автоматически обрабатывает свои запросы так, чтобы они включали только объекты ассоциированные с текущим сайтом.

Для использование CurrentSiteManager его следует явно подключить в модель. Например:

from django.db import models from django.contrib.sites.models import Site from django.contrib.sites.managers import CurrentSiteManager class Photo(models.Model): photo = models.FileField(upload_to='/home/photos') photographer_name = models.CharField(max_length=100) pub_date = models.DateField() site = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager()

При использовании этой модели, вызов Photo.objects.all() возвратит все объекты Photo из базы данных, а вызов Photo.on_site.all() — только те объекты Photo, которые относятся к текущему сайту (это определяется с помощью параметра SITE_ID).

Другими словами, эти два оператора эквивалентны:

Photo.objects.filter(site=settings.SITE_ID) Photo.on_site.all()

Как же CurrentSiteManager определяет какое поле объекта Photo относится к Site? По умолчанию он смотрит на поле с именем site. Если ваша модель имеет поля ForeignKey или ManyToManyField с именами отличными от site, вам потребуется явно передать имя в виде параметра в CurrentSiteManager. Нижеприведённая модель, у которой такое поле называется publish_on, демонстрирует такой случай:

from django.db import models from django.contrib.sites.models import Site from django.contrib.sites.managers import CurrentSiteManager class Photo(models.Model): photo = models.FileField(upload_to='/home/photos') photographer_name = models.CharField(max_length=100) pub_date = models.DateField() publish_on = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager('publish_on')

Если вы попытаетесь использовать CurrentSiteManager, передав имя несуществующего поля, Django вызовет исключение ValueError.

Замечание

Вероятно вы пожелаете иметь обычный (не привязанный к сайтам) Manager для ваших моделей, даже если вы используете CurrentSiteManager. Как описано в приложении «Справочник определений модели», если вы определите менеджер вручную, тогда Django не будет создавать менеджер автоматически objects = models.Manager().

Также определённые части Django, а именно интерфейс администратора и базовые представления, используют тот менеджер, который был определён первым в модели. Таким образом, если потребуется, чтобы ваш сайт администратора имел доступ ко всем объектам (а не только к тем, которые относятся к сайту), следует поместить objects = models.Manager() в модели до определения CurrentSiteManager.

Как Django использует среду управления сайтами

Несмотря на то, что использование среды управления сайтами необязательно, это настоятельно рекомендуется делать, потому что Django пользуется некоторыми преимуществами среды. Даже если Django используется для управления единственным сайтом, вы должны потратить несколько секунд на создание объекта Site с правильными значениями атрибутов name и domain, а затем указать на этот объект с помощью параметра SITE_ID.

Опишем, как Django использует среду управления сайтами:

djbook.ru


Prostoy-Site | Все права защищены © 2018 | Карта сайта