= Учебник - часть 2 = <> == Введение == Это вторая часть учебника. В первой части вы узначли о структуре папок проекта, настройке Buildout, компонентах содержимого, и использовании библиотеки форм. Компоненты содержимого - объекты с видимым для пользователя видом. Вид может быть браузерным (HTML/JS/CSS), JSON, XMLRPC или любым другим. Чтобы обяснить идею контент компонентов, проект, который был начат в первой части учебника, будет расширен путем добавления новых функций. По сути объект коллектор, который был создан в последнем разделе - контент компонент. В этом разделе, вы создадите и другие контент объекты, такие как заявки и комментарии. Также следует упомянуть, что каждый контент компонент, включая контейнеры, имеет хорошо определенные интерфейсы. Этот раздел исследует контент компоненты более детально. После окончания раздела, вы будете уметь: * Определять схему контент компонентов * Создавать контейнеры * Использовать ZCML для настройки компонентов Перед продолжением, вот обзор того, что рассматривается: * '''Добавление заявок''' – В этой части вы создадите объект заявки. Мы обеспечим подробный обзор создания контент объектов и покажем их использование на простом примере. * '''Списки заявок''' – Дальше вы увидите, как отобразить список заявок на главной странице коллектора. * '''Добавление комментариев''' – В этой части вы научитесь добавлять контент объекты внутрь контейнеров. Объекти заявки будут превращены в контейнеры. * '''Списки комментариев''' – В этой части вы создадите объект комментарий и напишете код для отображения комментариев на странице заявки. {{{#!wiki note Примеры для этой документации могут быть загружены отсюда: http://download.zope.org/bluebream/examples/ticketcollector- 1.0.0.tar.bz2. Исходники доступны на разных этапах соответственно разделам: * Этап 1 : От раздела 5.2 до 5.7 * Этап 2 : Раздел 5.8 * Этап 3 : Раздел 5.9 * Этап 4 : Раздел 6.2 * Этап 5 : Раздел 6.3 * Этап 6 : Раздел 6.4 и 6.5 }}} == Добавление заявок == === Определение схемы === В этом разделе вы научитесь добавлять заявки в коллектор. Для того, чтобы использовать объекты заявки, вам нужно создать интерфейс заявки. Обновите ''src/tc/collector/interfaces.py'', добавив туда интерфейс заявки: {{{#!highlight python from zope.container.interfaces import IContainer class ITicket(IContainer): """Ticket - the ticket content component""" number = TextLine( title=u"Number", description=u"Ticket number", default=u"", required=True) summary = Text( title=u"Summary", description=u"Ticket summary", default=u"", required=True) }}} '''!TextLine''' и '''Text''' должны быть проимпортированы, если нет - проимпортируйте их: {{{#!highlight python from zope.schema import TextLine from zope.schema import Text }}} Будет очень хорошо, если вы установите предусловие для ограничения на типы объектов, добавляемых в коллектор. Если вы знаеие, что в объект коллектор можно добавлять только объекты заявки, добавьте предусловие для того, чтобы удостоверится, что в коллектор будут добавлять только заявки. Для того, чтобы сделать это, вам нужно добавить метод '''_ _setitem_ _''' интерфейса ICollector в определение интерфейса ( '''_ _setitem_ _''' - часть API интерфейсв IContainer). Ниже нужно добавить атрибут предусловия, который является экземпляром класса '''!ItemTypePrecondition'''. Вы можете передать в класс temTypePrecondition интерфейсы в качестве аргументов. В примере ниже передается тольео один интерфейс - '''ITicket'''. Таким образом, только объекты заявки можно добавлять в коллектор. Вам нужно переместить определение интерфейса '''ITicket''' выше определения '''IContainer''', поскольку '''ITicket''' используется им. Добавьте следующее определение метода в класс '''ICollector''': {{{#!highlight python from zope.container.constraints import ItemTypePrecondition def _ _setitem_ _(name, object): """Add an ICollector object.""" _ _setitem_ _.precondition = ItemTypePrecondition(ITicket) }}} Класс !ItemTypePrecondition предоставляет ограничения на типы объектов, которые могут быть добавлены внутрь контейнера. Вы также определяете объекты заявки, которые могут быть добавлены в коллектор. Для того, чтобы сделать это, вы должны создать другой интерфейс, который наследуется от '''zope.container.interfaces.IContained'''. {{{#!highlight python from zope.schema import Field from zope.container.interfaces import IContained from zope.container.constraints import ContainerTypesConstraint class ITicketContained(IContained): """Interface that specifies the type of objects that can contain tickets. So a ticket can only contain in a collector.""" _ _parent_ _ = Field( constraint = ContainerTypesConstraint(ICollector)) }}} Тут вы добавили ограничение на поле '''_ _parent_ _''', используя класс '''!ContainerTypesConstraint'''. === Реализация === Дальше вам необходимо реализовать интефейс внутри ''src/tc/collector/ticket.py'': {{{#!highlight python from zope.interface import implements from zope.container.contained import Contained from zope.container.btree import BTreeContainer from tc.collector.interfaces import ITicket from tc.collector.interfaces import ITicketContained class Ticket(BTreeContainer, Contained): implements(ITicket, ITicketContained) number = u"" summary = u"" }}} === Конфигурация === Теперь необоходимо зарегистрировать интерфейс и класс. Откройте ''src/tc/collector/configure.zcml'' и добавьте следующее: {{{#!highlight xml }}} Теперь вы можете добавить ссылку на '''@@add_ticket''' в ''src/tc/collector/collectormain.pt''. Теперь шаблон будет выглядеть вот так: {{{#!highlight html Welcome to ticket collector Welcome to ticket collector!

Add Ticket }}} Когда вы перейдете по ссылке, ожидается получение вида. Вы можете создать '''AddForm''' в ''src/tc/collector/views.py'': {{{#!highlight python from tc.collector.interfaces import ITicket from tc.collector.ticket import Ticket class AddTicket(form.AddForm): form_fields = form.Fields(ITicket) def createAndAdd(self, data): number = data['number'] summary = data['summary'] ticket = Ticket() ticket.number = number ticket.summary = summary self.context[number] = ticket self.request.response.redirect('.') }}} Вы можете зарегистрировать вид ''src/tc/collector/configure.zcml'': {{{#!highlight xml }}} Вы можете добавить заявку перейдя по ссылке: http://localhost:8080/mycollector/@@add_ticket, вы можете передать номер заявки, например, '1' и добавить 'Test Summary' в качестве '''summary'''. Вы можете проверить объект через отладочную оболочку: {{{#!highlight bash jack@computer:/projects/ticketcollector$ ./bin/paster shell debug.ini ... Welcome to the interactive debug prompt. The 'root' variable contains the ZODB root folder. The 'app' variable contains the Debugger, 'app.publish(path)' simulates a request. >>> root['mycollector'] >>> root['mycollector']['1'] }}} === Страница по умолчанию для заявки === У нас еще нет страницы по умолчанию для заявки. Если вы попытаетесь получить доступ к заявке по ссылке http://localhost:8080/mycollector/1, то получите ошибку '''!NotFound''': {{{ URL: http://localhost:8080/mycollector/1 ... NotFound: Object: , name: u'@@index' }}} Эта ошибка возникает, потому что для интерфейса ITicket нет зарегистрированного вида с именем '''index'''. Этот раздел покажет, как создать вид по усолчанию для интерфейса ITicket. Как вы уже видели в разделе "Первые шаги", вы можете создать простой вид и зарегистрировать его в ZCML. Добавьте в ''src/tc/collector/views.py'' новый вид: {{{#!highlight python class TicketMainView(form.DisplayForm): form_fields = form.Fields(ITicket) template = ViewPageTemplateFile("ticketmain.pt") }}} Вы можете создать файл с шаблоном ''src/tc/collector/ticketmain.pt'' со следующем содержанием: {{{#!highlight xml Welcome to ticket collector! You are looking at ticket number: number

Summary

Summary goes here

}}} Потом в ''src/tc/collector/configure.zcml'': {{{#!highlight xml }}} Теперь вы можете перейти к http://localhost:8080/mycollector/1/@@index. По этому адресу вы должны увидеть номер заявки и ее краткое описание. Если вы просмотрите исходник страницы в HTML через браузер, он будет выглядеть следующим образом: {{{#!highlight html Welcome to ticket collector! You are looking at ticket number: 1

Summary

Test Summary

}}} == Создание списка заявок == Этот раздел объясняет как создать список заявок на главной странице коллектора с тем, чтобы получить возможность навигации по заявкам. Для того, чтобы вивести список заявок на главной странице коллектора, вам необходимо можифицировать ''src/tc/collector/collectormain.pt'': {{{#!highlight xml Welcome to ticket collector! Welcome to ticket collector!

Add Ticket

  1. Ticket Summary
}}} Вам необходимо изменить '''!TicketCollectorMainView''', который определен в файле ''src/tc/collector/views.py'': {{{#!highlight python class TicketCollectorMainView(form.DisplayForm): form_fields = form.Fields(ICollector) template = ViewPageTemplateFile("collectormain.pt") def getTickets(self): tickets = [] for ticket in self.context.values(): tickets.append({'url': ticket.number+"/@@index", 'summary': ticket.summary}) return tickets }}} == Добавление комментариев == {{{#!wiki warning Эта глава не завершена }}} В этом разделе вы создадите объекты комментариев, которые могут быть добавлены к заявкам. В качестве первого шага вам необходимо определить интерфейс комментария. Добавьте определение интерфейса в ''src/tc/collector/interfaces.py'': {{{#!highlight python from zope.interface import Interface class IComment(Interface): """Comment for Ticket""" body = Text( title=u"Additional Comment", description=u"Body of the Comment.", default=u"", required=True) class ICommentContained(IContained): """Interface that specifies the type of objects that can contain comments. A comment can only contain in a ticket.""" __parent__ = Field( constraint = ContainerTypesConstraint(ITicket)) }}} Для реализации комментария вы можете создать файл ''src/tc/collector/comment.py'': {{{#!highlight python from zope.interface import implements from tc.collector.interfaces import IComment from tc.collector.interfaces import ICommentContained from zope.container.contained import Contained class Comment(Contained): implements(IComment, ICommentContained) body = u"" }}} После этого зарегистрируйте интерфейс и класс, для этого обновите файл ''src/tc/collector/configure.zcml'': {{{#!highlight xml }}} Вам также следует добавить '''!ItemTypePrecondition''' к '''ITicket'''. Откройте ''src/tc/collector/interfaces.py'' и обновите определение интерфейса: {{{#!highlight python class ITicket(IContainer): """Ticket - the ticket content component""" number = TextLine( title=u"Number", description=u"Ticket number", default=u"", required=True) summary = Text( title=u"Summary", description=u"Ticket summary", default=u"", required=True) def __setitem__(name, object): """Add an ICollector object.""" __setitem__.precondition = ItemTypePrecondition(IComment) }}} Вы можете обновить файл с шаблоном ''src/tc/collector/ticketmain.pt'', добавив туда новое содержимое: {{{#!highlight xml Welcome to ticket collector! You are looking at ticket number: number

Summary

Summary goes here

Add Comment }}} Вы можете создать форму добавления (!AddForm) как показано ниже. Откройте файл ''src/tc/collector/views.py'' и добавьте в него '''!AddComment''', как показано ниже: {{{#!highlight python from zope.container.interfaces import INameChooser from tc.collector.interfaces import IComment from tc.collector.comment import Comment class AddComment(form.AddForm): form_fields = form.Fields(IComment) def createAndAdd(self, data): body = data['body'] comment = Comment() comment.body = body namechooser = INameChooser(self.context) number = namechooser.chooseName('c', comment) self.context[number] = comment self.request.response.redirect('.') }}} Вы можете зарегистрировать вид в файле ''src/tc/collector/configure.zcml'': {{{#!highlight xml }}} == Создание списка комментариев == Этот раздел рассказывает о том, как создать список комментариев на странице заявки, и, таким образом, пользователь сможет увидеть комментарии, которые приаязаны к конкретной заявке. Для того, чтобы составить список комментариев на странице заявки, вам нужно модифицировать шаблон ''src/tc/collector/ticketmain.pt'': {{{#!highlight xml Welcome to ticket collector! You are looking at ticket number: number

Summary

Summary goes here

Add Comment

Comment goes here

}}} == Выводы == В этом разделе мы рассмотрели создания контент компонентов. Вы можете узнать больше о !BlueBream из руководства. '''Перевод: Ростислав Дзинько'''