Учебник - часть 2

Введение

Это вторая часть учебника. В первой части вы узначли о структуре папок проекта, настройке Buildout, компонентах содержимого, и использовании библиотеки форм. Компоненты содержимого - объекты с видимым для пользователя видом. Вид может быть браузерным (HTML/JS/CSS), JSON, XMLRPC или любым другим. Чтобы обяснить идею контент компонентов, проект, который был начат в первой части учебника, будет расширен путем добавления новых функций. По сути объект коллектор, который был создан в последнем разделе - контент компонент. В этом разделе, вы создадите и другие контент объекты, такие как заявки и комментарии. Также следует упомянуть, что каждый контент компонент, включая контейнеры, имеет хорошо определенные интерфейсы.

Этот раздел исследует контент компоненты более детально. После окончания раздела, вы будете уметь:

Перед продолжением, вот обзор того, что рассматривается:

Примеры для этой документации могут быть загружены отсюда: 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, добавив туда интерфейс заявки:

   1 from zope.container.interfaces import IContainer
   2 
   3 class ITicket(IContainer):
   4     """Ticket - the ticket content component"""
   5 
   6     number = TextLine(
   7         title=u"Number",
   8         description=u"Ticket number",
   9         default=u"",
  10         required=True)
  11 
  12     summary = Text(
  13         title=u"Summary",
  14         description=u"Ticket summary",
  15         default=u"",
  16         required=True)

TextLine и Text должны быть проимпортированы, если нет - проимпортируйте их:

   1 from zope.schema import TextLine
   2 from zope.schema import Text

Будет очень хорошо, если вы установите предусловие для ограничения на типы объектов, добавляемых в коллектор. Если вы знаеие, что в объект коллектор можно добавлять только объекты заявки, добавьте предусловие для того, чтобы удостоверится, что в коллектор будут добавлять только заявки. Для того, чтобы сделать это, вам нужно добавить метод _ _setitem_ _ интерфейса ICollector в определение интерфейса ( _ _setitem_ _ - часть API интерфейсв IContainer). Ниже нужно добавить атрибут предусловия, который является экземпляром класса ItemTypePrecondition. Вы можете передать в класс temTypePrecondition интерфейсы в качестве аргументов. В примере ниже передается тольео один интерфейс - ITicket. Таким образом, только объекты заявки можно добавлять в коллектор. Вам нужно переместить определение интерфейса ITicket выше определения IContainer, поскольку ITicket используется им. Добавьте следующее определение метода в класс ICollector:

   1 from zope.container.constraints import ItemTypePrecondition
   2 
   3 def _ _setitem_ _(name, object):
   4     """Add an ICollector object."""
   5 
   6 _ _setitem_ _.precondition = ItemTypePrecondition(ITicket)

Класс ItemTypePrecondition предоставляет ограничения на типы объектов, которые могут быть добавлены внутрь контейнера. Вы также определяете объекты заявки, которые могут быть добавлены в коллектор. Для того, чтобы сделать это, вы должны создать другой интерфейс, который наследуется от zope.container.interfaces.IContained.

   1 from zope.schema import Field
   2 from zope.container.interfaces import IContained
   3 from zope.container.constraints import ContainerTypesConstraint
   4 
   5 class ITicketContained(IContained):
   6     """Interface that specifies the type of objects that can contain
   7     tickets.  So a ticket can only contain in a collector."""
   8 
   9     _ _parent_ _ = Field(
  10         constraint = ContainerTypesConstraint(ICollector))

Тут вы добавили ограничение на поле _ _parent_ _, используя класс ContainerTypesConstraint.

Реализация

Дальше вам необходимо реализовать интефейс внутри src/tc/collector/ticket.py:

   1 from zope.interface import implements
   2 from zope.container.contained import Contained
   3 from zope.container.btree import BTreeContainer
   4 
   5 from tc.collector.interfaces import ITicket
   6 from tc.collector.interfaces import ITicketContained
   7 
   8 
   9 class Ticket(BTreeContainer, Contained):
  10 
  11     implements(ITicket, ITicketContained)
  12 
  13     number = u""
  14     summary = u""

Конфигурация

Теперь необоходимо зарегистрировать интерфейс и класс. Откройте src/tc/collector/configure.zcml и добавьте следующее:

   1 <interface
   2    interface="tc.collector.interfaces.ITicket"
   3    type="zope.app.content.interfaces.IContentType"
   4    />
   5 
   6 <class class="tc.collector.ticket.Ticket">
   7   <implements
   8      interface="zope.annotation.interfaces.IAttributeAnnotatable"
   9      />
  10   <implements
  11      interface="zope.container.interfaces.IContentContainer"
  12      />
  13   <require
  14      permission="zope.Public"
  15      interface="tc.collector.interfaces.ITicket"
  16      />
  17   <require
  18      permission="zope.Public"
  19      set_schema="tc.collector.interfaces.ITicket"
  20      />
  21 </class>

Теперь вы можете добавить ссылку на @@add_ticket в src/tc/collector/collectormain.pt. Теперь шаблон будет выглядеть вот так:

   1 <html>
   2 <head>
   3 <title>Welcome to ticket collector</title>
   4 </head>
   5 <body>
   6 
   7 Welcome to ticket collector! <br/> <br/>
   8 
   9 <a href="@@add_ticket">Add Ticket</a>
  10 
  11 </body>
  12 </html>

Когда вы перейдете по ссылке, ожидается получение вида. Вы можете создать AddForm в src/tc/collector/views.py:

   1 from tc.collector.interfaces import ITicket
   2 
   3 from tc.collector.ticket import Ticket
   4 
   5 class AddTicket(form.AddForm):
   6 
   7     form_fields = form.Fields(ITicket)
   8 
   9     def createAndAdd(self, data):
  10         number = data['number']
  11         summary = data['summary']
  12         ticket = Ticket()
  13         ticket.number = number
  14         ticket.summary = summary
  15         self.context[number] = ticket
  16         self.request.response.redirect('.')

Вы можете зарегистрировать вид src/tc/collector/configure.zcml:

   1 <browser:page
   2    for="tc.collector.interfaces.ICollector"
   3    name="add_ticket"
   4    permission="zope.Public"
   5    class="tc.collector.views.AddTicket"
   6    />

Вы можете добавить заявку перейдя по ссылке: http://localhost:8080/mycollector/@@add_ticket, вы можете передать номер заявки, например, '1' и добавить 'Test Summary' в качестве summary.

Вы можете проверить объект через отладочную оболочку:

   1 jack@computer:/projects/ticketcollector$ ./bin/paster shell debug.ini
   2 ...
   3 Welcome to the interactive debug prompt.
   4 The 'root' variable contains the ZODB root folder.
   5 The 'app' variable contains the Debugger, 'app.publish(path)' simulates a request.
   6 >>> root['mycollector']
   7 <tc.collector.ticketcollector.Collector object at 0xa5fc96c>
   8 >>> root['mycollector']['1']
   9 <tc.collector.ticket.Ticket object at 0xa5ffecc>

Страница по умолчанию для заявки

У нас еще нет страницы по умолчанию для заявки. Если вы попытаетесь получить доступ к заявке по ссылке http://localhost:8080/mycollector/1, то получите ошибку NotFound:

URL: http://localhost:8080/mycollector/1
...
NotFound: Object: <tc.collector.ticketcollector.Ticket object at 0x8fe74ac>, name: u'@@index'

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

Как вы уже видели в разделе "Первые шаги", вы можете создать простой вид и зарегистрировать его в ZCML.

Добавьте в src/tc/collector/views.py новый вид:

   1 class TicketMainView(form.DisplayForm):
   2 
   3     form_fields = form.Fields(ITicket)
   4 
   5     template = ViewPageTemplateFile("ticketmain.pt")

Вы можете создать файл с шаблоном src/tc/collector/ticketmain.pt со следующем содержанием:

   1 <html>
   2 <head>
   3 <title>Welcome to ticket collector!</title>
   4 </head>
   5 <body>
   6 
   7 You are looking at ticket number:
   8 <b tal:content="context/number">number</b>
   9 
  10 <h3>Summary</h3>
  11 
  12 <p tal:content="context/summary">Summary goes here</p>
  13 
  14 </body>
  15 </html>

Потом в src/tc/collector/configure.zcml:

   1 <browser:page
   2    for="tc.collector.interfaces.ITicket"
   3    name="index"
   4    permission="zope.Public"
   5    class="tc.collector.views.TicketMainView"
   6    />

Теперь вы можете перейти к http://localhost:8080/mycollector/1/@@index. По этому адресу вы должны увидеть номер заявки и ее краткое описание. Если вы просмотрите исходник страницы в HTML через браузер, он будет выглядеть следующим образом:

   1 <html>
   2 <head>
   3 <title>Welcome to ticket collector!</title>
   4 </head>
   5 <body>
   6 
   7 You are looking at ticket number: <b>1</b>
   8 
   9 <h3>Summary</h3>
  10 
  11 <p>Test Summary</p>
  12 
  13 </body>
  14 </html>

Создание списка заявок

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

Для того, чтобы вивести список заявок на главной странице коллектора, вам необходимо можифицировать src/tc/collector/collectormain.pt:

   1 <html>
   2 <head>
   3 <title>Welcome to ticket collector!</title>
   4 </head>
   5 <body>
   6 
   7 Welcome to ticket collector! <br/> <br/>
   8 
   9 <a href="@@add_ticket">Add Ticket</a> <br/> <br/>
  10 
  11 <ol>
  12   <li tal:repeat="ticket view/getTickets">
  13     <a href=""
  14        tal:attributes="href ticket/url"
  15        tal:content="ticket/summary">Ticket Summary</a>
  16   </li>
  17 </ol>
  18 
  19 </body>
  20 </html>

Вам необходимо изменить TicketCollectorMainView, который определен в файле src/tc/collector/views.py:

   1 class TicketCollectorMainView(form.DisplayForm):
   2 
   3     form_fields = form.Fields(ICollector)
   4 
   5     template = ViewPageTemplateFile("collectormain.pt")
   6 
   7     def getTickets(self):
   8         tickets = []
   9         for ticket in self.context.values():
  10             tickets.append({'url': ticket.number+"/@@index",
  11                             'summary': ticket.summary})
  12         return tickets

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

Эта глава не завершена

В этом разделе вы создадите объекты комментариев, которые могут быть добавлены к заявкам. В качестве первого шага вам необходимо определить интерфейс комментария. Добавьте определение интерфейса в src/tc/collector/interfaces.py:

   1 from zope.interface import Interface
   2 
   3 class IComment(Interface):
   4     """Comment for Ticket"""
   5 
   6     body = Text(
   7         title=u"Additional Comment",
   8         description=u"Body of the Comment.",
   9         default=u"",
  10         required=True)
  11 
  12 class ICommentContained(IContained):
  13     """Interface that specifies the type of objects that can contain
  14     comments.  A comment can only contain in a ticket."""
  15 
  16     __parent__ = Field(
  17         constraint = ContainerTypesConstraint(ITicket))

Для реализации комментария вы можете создать файл src/tc/collector/comment.py:

   1 from zope.interface import implements
   2 from tc.collector.interfaces import IComment
   3 from tc.collector.interfaces import ICommentContained
   4 from zope.container.contained import Contained
   5 
   6 class Comment(Contained):
   7 
   8     implements(IComment, ICommentContained)
   9 
  10     body = u""

После этого зарегистрируйте интерфейс и класс, для этого обновите файл src/tc/collector/configure.zcml:

   1 <interface
   2    interface="tc.collector.interfaces.IComment"
   3    type="zope.app.content.interfaces.IContentType"
   4    />
   5 
   6 <class class="tc.collector.comment.Comment">
   7   <implements
   8      interface="zope.annotation.interfaces.IAttributeAnnotatable"
   9      />
  10   <require
  11      permission="zope.Public"
  12      interface="tc.collector.interfaces.IComment"
  13      />
  14   <require
  15      permission="zope.Public"
  16      set_schema="tc.collector.interfaces.IComment"
  17      />
  18 </class>

Вам также следует добавить ItemTypePrecondition к ITicket. Откройте src/tc/collector/interfaces.py и обновите определение интерфейса:

   1 class ITicket(IContainer):
   2     """Ticket - the ticket content component"""
   3 
   4     number = TextLine(
   5         title=u"Number",
   6         description=u"Ticket number",
   7         default=u"",
   8         required=True)
   9 
  10     summary = Text(
  11         title=u"Summary",
  12         description=u"Ticket summary",
  13         default=u"",
  14         required=True)
  15 
  16     def __setitem__(name, object):
  17         """Add an ICollector object."""
  18 
  19     __setitem__.precondition = ItemTypePrecondition(IComment)

Вы можете обновить файл с шаблоном src/tc/collector/ticketmain.pt, добавив туда новое содержимое:

   1 <html>
   2 <head>
   3 <title>Welcome to ticket collector!</title>
   4 </head>
   5 <body>
   6 
   7 You are looking at ticket number:
   8 <b tal:content="context/number">number</b>
   9 
  10 <h3>Summary</h3>
  11 
  12 <p tal:content="context/summary">Summary goes here</p>
  13 
  14 <a href="@@add_comment">Add Comment</a>
  15 
  16 </body>
  17 </html>

Вы можете создать форму добавления (AddForm) как показано ниже. Откройте файл src/tc/collector/views.py и добавьте в него AddComment, как показано ниже:

   1 from zope.container.interfaces import INameChooser
   2 from tc.collector.interfaces import IComment
   3 from tc.collector.comment import Comment
   4 
   5 class AddComment(form.AddForm):
   6 
   7     form_fields = form.Fields(IComment)
   8 
   9     def createAndAdd(self, data):
  10         body = data['body']
  11         comment = Comment()
  12         comment.body = body
  13         namechooser = INameChooser(self.context)
  14         number = namechooser.chooseName('c', comment)
  15         self.context[number] = comment
  16         self.request.response.redirect('.')

Вы можете зарегистрировать вид в файле src/tc/collector/configure.zcml:

   1 <browser:page
   2    for="tc.collector.interfaces.ITicket"
   3    name="add_comment"
   4    permission="zope.Public"
   5    class="tc.collector.views.AddComment"
   6    />

Создание списка комментариев

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

Для того, чтобы составить список комментариев на странице заявки, вам нужно модифицировать шаблон src/tc/collector/ticketmain.pt:

   1 <html>
   2 <head>
   3 <title>Welcome to ticket collector!</title>
   4 </head>
   5 <body>
   6 
   7 You are looking at ticket number:
   8 <b tal:content="context/number">number</b>
   9 
  10 <h3>Summary</h3>
  11 
  12 <p tal:content="context/summary">Summary goes here</p>
  13 
  14 <a href="@@add_comment">Add Comment</a>
  15 
  16 <p tal:repeat="ticket context/values">
  17   <span tal:content="ticket/body">Comment goes here</span>
  18 </p>
  19 
  20 </body>
  21 </html>

Выводы

В этом разделе мы рассмотрели создания контент компонентов. Вы можете узнать больше о BlueBream из руководства.

Перевод: Ростислав Дзинько

Документации/Bluebream/BluebreamУчебник2 (последним исправлял пользователь RostislavDzinko 2010-06-13 19:31:48)