Spellchecked, cleaned, and Edit API section expanded

Added to edit api, fixed queryset example code, stubbed out Page ref

Some cleanup of the edit api doc

Some cleanup of the frontend doc

Added to edit api docs

Added to edit api docs
pull/262/head
Jeffrey Hearn 2014-05-23 23:03:07 -04:00
rodzic 992ef2eec7
commit 94af650c83
6 zmienionych plików z 333 dodań i 90 usunięć

Wyświetl plik

@ -4,7 +4,7 @@ For Django developers
.. note::
This documentation is currently being written.
Wagtail requires a little careful setup to define the types of content that you want to present through your website. The basic unit of content in Wagtail is the ``Page``, and all of your page-level content will inherit basic webpage-related properties from it. But for the most part, you will be defining content yourself, through the contruction of Django models using Wagtail's ``Page`` as a base.
Wagtail requires a little careful setup to define the types of content that you want to present through your website. The basic unit of content in Wagtail is the ``Page``, and all of your page-level content will inherit basic webpage-related properties from it. But for the most part, you will be defining content yourself, through the construction of Django models using Wagtail's ``Page`` as a base.
Wagtail organizes content created from your models in a tree, which can have any structure and combination of model objects in it. Wagtail doesn't prescribe ways to organize and interrelate your content, but here we've sketched out some strategies for organizing your models.
@ -82,7 +82,7 @@ So what does a Wagtail model definition look like? Here's a model representing a
ImageChooserPanel('feed_image'),
]
To keep track of your ``Page``-derived models, it might be helpful to include "Page" as the last part of your classname. ``BlogPage`` defines three properties: ``body``, ``date``, and ``feed_image``. These are a mix of basic Django models (``DateField``), Wagtail fields (``RichTextField``), and a pointer to a Wagtail model (``Image``).
To keep track of your ``Page``-derived models, it might be helpful to include "Page" as the last part of your class name. ``BlogPage`` defines three properties: ``body``, ``date``, and ``feed_image``. These are a mix of basic Django models (``DateField``), Wagtail fields (``RichTextField``), and a pointer to a Wagtail model (``Image``).
Next, the ``content_panels`` and ``promote_panels`` lists define the capabilities and layout of the Wagtail admin page edit interface. The lists are filled with "panels" and "choosers", which will provide a fine-grain interface for inputting the model's content. The ``ImageChooserPanel``, for instance, lets one browse the image library, upload new images, and input image metadata. The ``RichTextField`` is the basic field for creating web-ready website rich text, including text formatting and embedded media like images and video. The Wagtail admin offers other choices for fields, Panels, and Choosers, with the option of creating your own to precisely fit your content without workarounds or other compromises.
@ -117,23 +117,28 @@ It might be handy to think of the ``Page``-derived models you want to create as
Nodes
`````
Parent nodes on the Wagtail tree probably want to organize and display a browsable index of their descendents. A blog, for instance, needs a way to show a list of individual posts.
Parent nodes on the Wagtail tree probably want to organize and display a browse-able index of their descendants. A blog, for instance, needs a way to show a list of individual posts.
A Parent node could provide its own function returning its descendant objects.
.. code-block:: python
class EventPageIndex(Page):
...
# ...
def events(self):
# Get list of event pages that are descendants of this page
events = EventPage.objects.filter(
live=True,
path__startswith=self.path
)
# Get list of live event pages that are descendants of this page
events = EventPage.objects.live().descendant_of(self)
# Filter events list to get ones that are either
# running now or start in the future
events = events.filter(date_from__gte=date.today())
# Order by date
events = events.order_by('date_from')
return events
This example makes sure to limit the returned objects to pieces of content which make sense, specifically ones which have been published through Wagtail's admin interface (``live=True``) and are descendants of this node. Wagtail will allow the "illogical" placement of child nodes under a parent, so it's necessary for a parent model to index only those children which make sense.
This example makes sure to limit the returned objects to pieces of content which make sense, specifically ones which have been published through Wagtail's admin interface (``live()``) and are children of this node (``descendant_of(self)``). By setting a ``subpage_types`` class property in your model, you can specify which models are allowed to be set as children, but Wagtail will allow any ``Page``-derived model by default. Regardless, it's smart for a parent model to provide an index filtered to make sense.
Leaves
@ -146,23 +151,18 @@ The model for the leaf could provide a function that traverses the tree in the o
.. code-block:: python
class BlogPage(Page):
...
def blog_index(self):
# Find blog index in ancestors
for ancestor in reversed(self.get_ancestors()):
if isinstance(ancestor.specific, BlogIndexPage):
return ancestor
class EventPage(Page):
# ...
def event_index(self):
# Find closest ancestor which is an event index
return self.get_ancestors().type(EventIndexPage).last()
# No ancestors are blog indexes, just return first blog index in database
return BlogIndexPage.objects.first()
Since Wagtail doesn't limit what Page-derived classes can be assigned as parents and children, the reverse tree traversal needs to accommodate cases which might not be expected, such as the lack of a "logical" parent to a leaf.
If defined, ``subpage_types`` will also limit the parent models allowed to contain a leaf. If not, Wagtail will allow any combination of parents and leafs to be associated in the Wagtail tree. Like with index pages, it's a good idea to make sure that the index is actually of the expected model to contain the leaf.
Other Relationships
```````````````````
Your ``Page``-derived models might have other interrelationships which extend the basic Wagtail tree or depart from it entirely. You could provide functions to navigate between siblings, such as a "Next Post" link on a blog page (``post->post->post``). It might make sense for subtrees to interrelate, such as in a discussion forum (``forum->post->replies``) Skipping across the hierarchy might make sense, too, as all objects of a certain model class might interrelate regardless of their ancestors (``events = EventPage.objects.all``). Since there's no restriction on the combination of model classes that can be used at any point in the tree, and it's largely up to the models to define their interrelations, the possibilities are really endless.
Your ``Page``-derived models might have other interrelationships which extend the basic Wagtail tree or depart from it entirely. You could provide functions to navigate between siblings, such as a "Next Post" link on a blog page (``post->post->post``). It might make sense for subtrees to interrelate, such as in a discussion forum (``forum->post->replies``) Skipping across the hierarchy might make sense, too, as all objects of a certain model class might interrelate regardless of their ancestors (``events = EventPage.objects.all``). It's largely up to the models to define their interrelations, the possibilities are really endless.
Anatomy of a Wagtail Request
@ -173,10 +173,105 @@ For going beyond the basics of model definition and interrelation, it might help
#. Django gets a request and routes through Wagtail's URL dispatcher definitions
#. Starting from the root content piece, Wagtail traverses the page tree, letting the model for each piece of content along the path decide how to ``route()`` the next step in the path.
#. A model class decides that routing is done and it's now time to ``serve()`` content.
#. The model constructs a context, finds a template to pass it to, and renders the content.
#. The templates are rendered and the response object is sent back to the requester.
#. ``serve()`` constructs a context using ``get_context()``
#. ``serve()`` finds a template to pass it to using ``get_template()``
#. A response object is returned by ``serve()`` and Django responds to the requester.
You can apply custom behavior to this process by overriding ``Page`` class methods such as ``route()`` and ``serve()`` in your own models. For examples, see :ref:`model_recipes`.
Page Properties and Methods Reference
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In addition to the model fields provided, ``Page`` has many properties and methods that you may wish to reference, use, or override in creating your own models. Those listed here are relatively straightforward to use, but consult the Wagtail source code for a full view of what's possible.
Properties:
specific
url
full_url
relative_url
has_unpublished_changes
status_string
subpage_types
indexed_fields
Methods:
route
serve
get_context
get_template
is_navigable
get_other_siblings
get_ancestors
get_descendants
get_siblings
search
get_page_modes
show_as_mode
Page Queryset Methods
~~~~~~~~~~~~~~~~~~~~~
The ``Page`` class uses a custom Django model manager which provides these methods for structuring queries on ``Page`` objects.
get_query_set()
return PageQuerySet(self.model).order_by('path')
live(self):
return self.get_query_set().live()
not_live(self):
return self.get_query_set().not_live()
page(self, other):
return self.get_query_set().page(other)
not_page(self, other):
return self.get_query_set().not_page(other)
descendant_of(self, other, inclusive=False):
return self.get_query_set().descendant_of(other, inclusive)
not_descendant_of(self, other, inclusive=False):
return self.get_query_set().not_descendant_of(other, inclusive)
child_of(self, other):
return self.get_query_set().child_of(other)
not_child_of(self, other):
return self.get_query_set().not_child_of(other)
ancestor_of(self, other, inclusive=False):
return self.get_query_set().ancestor_of(other, inclusive)
not_ancestor_of(self, other, inclusive=False):
return self.get_query_set().not_ancestor_of(other, inclusive)
parent_of(self, other):
return self.get_query_set().parent_of(other)
not_parent_of(self, other):
return self.get_query_set().not_parent_of(other)
sibling_of(self, other, inclusive=False):
return self.get_query_set().sibling_of(other, inclusive)
not_sibling_of(self, other, inclusive=False):
return self.get_query_set().not_sibling_of(other, inclusive)
type(self, model):
return self.get_query_set().type(model)
not_type(self, model):
return self.get_query_set().not_type(model)
You can apply custom behavior to this process by overriding the ``route()`` and ``serve()`` methods of the ``Page`` class in your own models.
Site

Wyświetl plik

@ -19,7 +19,7 @@ Displaying Pages
==========================
Template Location
-----------------
~~~~~~~~~~~~~~~~~
For each of your ``Page``-derived models, Wagtail will look for a template in the following location, relative to your project root::
@ -36,7 +36,7 @@ Class names are converted from camel case to underscores. For example, the templ
Self
----
~~~~
By default, the context passed to a model's template consists of two properties: ``self`` and ``request``. ``self`` is the model object being displayed. ``request`` is the normal Django request object. So, to include the title of a ``Page``, use ``{{ self.title }}``.
@ -45,9 +45,8 @@ Static files (css, js, images)
========================
Images
~~~~~~~~~~
~~~~~~
Images uploaded to Wagtail go into the image library and from there are added to pages via the :doc:`page editor interface </editor_manual/new_pages/inserting_images>`.
@ -55,7 +54,7 @@ Unlike other CMS, adding images to a page does not involve choosing a "version"
Images from the library **must** be requested using this syntax, but images in your codebase can be added via conventional means e.g ``img`` tags. Only images from the library can be manipulated on the fly.
Read more about the image manipulation syntax here :ref:`Images tag <image-tag>`.
Read more about the image manipulation syntax here :ref:`image_tag`.
========================
@ -64,7 +63,9 @@ Template tags & filters
In addition to Django's standard tags and filters, Wagtail provides some of it's own, which can be ``load``-ed `as you would any other <https://docs.djangoproject.com/en/dev/topics/templates/#custom-tag-and-filter-libraries>`_
.. _image-tag:
.. _image_tag:
Images (tag)
~~~~~~~~~~~~
@ -123,8 +124,9 @@ The available ``method`` s are:
Wagtail *does not allow deforming or stretching images*. Image dimension ratios will always be kept. Wagtail also *does not support upscaling*. Small images forced to appear at larger sizes will "max out" at their their native dimensions.
To request the "original" version of an image, it is suggested you rely on the lack of upscalling support by requesting an image much larger than it's maximum dimensions. e.g to insert an image who's dimensions are uncertain/unknown, at it's maximum size, try: ``{% image self.image width-10000 %}``. This assumes the image is unlikely to be larger than 10000px wide.
To request the "original" version of an image, it is suggested you rely on the lack of upscaling support by requesting an image much larger than it's maximum dimensions. e.g to insert an image who's dimensions are uncertain/unknown, at it's maximum size, try: ``{% image self.image width-10000 %}``. This assumes the image is unlikely to be larger than 10000px wide.
.. _rich-text-filter:
Rich text (filter)
~~~~~~~~~~~~~~~~~~

Wyświetl plik

@ -27,11 +27,11 @@ There are three types of panels:
``FieldPanel( field_name, classname=None )``
This is the panel used for basic Django field types. ``field_name`` is the name of the class property used in your model definition. ``classname`` is a string of optional CSS classes given to the panel which are used in formatting and scripted interactivity. By default, panels are formatted as inset fields. The CSS class ``full`` can be used to format the panel so it covers the full width of the Wagtail page editor. The CSS class ``title`` can be used to mark a field as the source for auto-generated slug strings.
``MultiFieldPanel( panel_list, heading )``
``MultiFieldPanel( children, heading="", classname=None )``
This panel condenses several ``FieldPanel`` s or choosers, from a list or tuple, under a single ``heading`` string.
``InlinePanel( base_model, relation_name, panels=None, label='', help_text='' )``
This panel allows for the creation of a "cluster" of related objects over a join to a separate model, such as a list of related links or slides to an image carousel. This is a very powerful, but tricky feature which will take some space to cover, so we'll skip over it for now. For a full explaination on the usage of ``InlinePanel``, see :ref:`inline_panels`.
This panel allows for the creation of a "cluster" of related objects over a join to a separate model, such as a list of related links or slides to an image carousel. This is a very powerful, but tricky feature which will take some space to cover, so we'll skip over it for now. For a full explanation on the usage of ``InlinePanel``, see :ref:`inline_panels`.
Wagtail provides a tabbed interface to help organize panels. ``content_panels`` is the main tab, used for the meat of your model content. The other, ``promote_panels``, is suggested for organizing metadata about the content, such as SEO information and other machine-readable information. Since you're writing the panel definitions, you can organize them however you want.
@ -65,8 +65,7 @@ Let's look at an example of a panel definition:
MultiFieldPanel(COMMON_PANELS, "Common page configuration"),
]
After the ``Page``-derived class definition, just add lists of panel definitions to order and organize the Wagtail page editing interface for your model.
Built-in Fields and Choosers
@ -85,100 +84,219 @@ Wagtail provides a general-purpose WYSIWYG editor for creating rich text content
.. code-block:: python
from wagtail.wagtailcore.fields import RichTextField
...
from wagtail.wagtailadmin.edit_handlers import FieldPanel
# ...
class BookPage(Page):
book_text = RichTextField()
BookPage.content_panels = [
FieldPanel('body', classname="full"),
# ...
]
``RichTextField`` inherits from Django's basic ``TextField`` field, so you can pass any field parameters into ``RichTextField`` as if using a normal Django field. This field does not need a special panel and can be defined with ``FieldPanel``.
If you're interested in extending the capabilities of the Wagtail editor, See :ref:`extending_wysiwyg`.
However, template output from ``RichTextField`` is special and need to be filtered to preserve embedded content. See :ref:`rich-text-filter`.
If you're interested in extending the capabilities of the Wagtail WYSIWYG editor (hallo.js), See :ref:`extending_wysiwyg`.
Images
------
One of the features of Wagtail is a unified image library, which you can access in your models through the ``Image`` model and the ``ImageChooserPanel`` chooser. Here's how:
.. code-block:: python
from wagtail.wagtailimages.models import Image
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
# ...
class BookPage(Page):
cover = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
BookPage.content_panels = [
ImageChooserPanel('cover'),
# ...
]
feed_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
Django's default behavior is to "cascade" deletions through a ForeignKey relationship, which is probably not what you want happening. This is why the ``null``, ``blank``, and ``on_delete`` parameters should be set to allow for an empty field. (See `Django model field reference (on_delete)`_ ). ``ImageChooserPanel`` takes only one argument: the name of the field.
.. _Django model field reference (on_delete): https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.on_delete
Displaying ``Image`` objects in a template requires the use of a template tag. See :ref:`image_tag`.
Documents
---------
For files in other formats, Wagtail provides a generic file store through the ``Document`` model:
.. code-block:: python
from wagtail.wagtaildocs.models import Document
from wagtail.wagtaildocs.edit_handlers import DocumentChooserPanel
# ...
class BookPage(Page):
book_file = models.ForeignKey(
'wagtaildocs.Document',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
link_document = models.ForeignKey(
'wagtaildocs.Document',
null=True,
blank=True,
related_name='+'
)
BookPage.content_panels = [
DocumentChooserPanel('book_file'),
# ...
]
As with images, Wagtail documents should also have the appropriate extra parameters to prevent cascade deletions across a ForeignKey relationship. ``DocumentChooserPanel`` takes only one argument: the name of the field.
Documents can be used directly in templates without tags or filters. Its properties are:
.. glossary::
``title``
The title of the document.
``url``
URL to the file.
``created_at``
The date and time the document was created (DateTime).
``filename``
The filename of the file.
``file_extension``
The extension of the file.
``tags``
A ``TaggableManager`` which keeps track of tags associated with the document (uses the ``django-taggit`` module).
Pages and Page-derived Models
-----------------------------
You can explicitly link ``Page``-derived models together using the ``Page`` model and ``PageChooserPanel``.
.. code-block:: python
from wagtail.wagtailcore.models import Page
from wagtail.wagtailadmin.edit_handlers import PageChooserPanel
# ...
class BookPage(Page):
publisher = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
)
page = models.ForeignKey(
'wagtailcore.Page',
related_name='+',
null=True,
blank=True
)
BookPage.content_panels = [
PageChooserPanel('related_page', 'demo.PublisherPage'),
# ...
]
Can also use more specific models.
``PageChooserPanel`` takes two arguments: a field name and an optional page type. Specifying a page type (in the form of an ``"appname.modelname"`` string) will filter the chooser to display only pages of that type.
Snippets (and Basic Django Models?)
Snippets
--------
Snippets are not not subclasses, so you must include the model class directly. A chooser is provided which takes the snippet class.
Snippets are not subclasses, so you must include the model class directly. A chooser is provided which takes the field name snippet class.
.. code-block:: python
advert = models.ForeignKey(
'demo.Advert',
related_name='+'
)
from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
# ...
class BookPage(Page):
advert = models.ForeignKey(
'demo.Advert',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
BookPage.content_panels = [
SnippetChooserPanel('advert', Advert),
# ...
]
See :ref:`snippets` for more information.
PageChooserPanel
~~~~~~~~~~~~~~~~
ImageChooserPanel
~~~~~~~~~~~~~~~~~
DocumentChooserPanel
~~~~~~~~~~~~~~~~~~~~
SnippetChooserPanel
Field Customization
~~~~~~~~~~~~~~~~~~~
By adding CSS classnames to your panel definitions or adding extra parameters to your field definitions, you can control much of how your fields will display in the Wagtail page editing interface. Wagtail's page editing interface takes much of its behavior from Django's admin, so you may find many options for customization covered there. (See `Django model field reference`_ ).
.. _Django model field reference:https://docs.djangoproject.com/en/dev/ref/models/fields/
Full-Width Input
----------------
Use ``classname="full"`` to make a field (input element) stretch the full width of the Wagtail page editor. This will not work if the field is encapsulated in a ``MultiFieldPanel``, which places its child fields into a formset.
Required Fields
---------------
To make input or chooser selection manditory for a field, add ``blank=False`` to its model definition. (See `Django model field reference (blank)`_ ).
.. _Django model field reference (blank): https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.Field.blank
Hiding Fields
-------------
Without a panel definition, a default form field (without label) will be used to represent your fields. If you intend to hide a field on the Wagtail page editor, define the field with ``editable=False`` (See `Django model field reference (editable)`_ ).
.. _Django model field reference (editable): https://docs.djangoproject.com/en/dev/ref/models/fields/#editable
MultiFieldPanel
~~~~~~~~~~~~~~~
.. code-block:: python
BOOK_FIELD_COLLECTION = [
ImageChooserPanel('cover'),
DocumentChooserPanel('book_file'),
PageChooserPanel('publisher'),
]
BookPage.content_panels = [
MultiFieldPanel(
BOOK_FIELD_COLLECTION,
heading="Collection of Book Fields",
classname="collapsible collapsed"
),
# ...
]
.. _inline_panels:
@ -193,6 +311,8 @@ The ``django-modelcluster`` module allows for streamlined relation of extra mode
Extending the WYSIWYG Editor (hallo.js)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Adding hallo.js plugins:
https://github.com/torchbox/wagtail/commit/1ecc215759142e6cafdacb185bbfd3f8e9cd3185
Edit Handler API

Wyświetl plik

@ -1,4 +1,6 @@
.. _model_recipes:
Model Recipes
=============
@ -174,3 +176,24 @@ Here, ``blogs.filter(tags__name=tag)`` invokes a reverse Django queryset filter
Iterating through ``self.tags.all`` will display each tag associated with ``self``, while the link(s) back to the index make use of the filter option added to the ``BlogIndexPage`` model. A Django query could also use the ``tagged_items`` related name field to get ``BlogPage`` objects associated with a tag.
This is just one possible way of creating a taxonomy for Wagtail objects. With all of the components for a taxonomy available through Wagtail, you should be able to fulfill even the most exotic taxonomic schemes.
Custom Page Contexts by Overriding get_context()
------------------------------------------------
Load Alternate Templates by Overriding get_template()
-----------------------------------------------------
Page Modes
----------
get_page_modes
show_as_mode

Wyświetl plik

@ -1,9 +1,12 @@
.. _snippets:
Snippets
========
Snippets are pieces of content which do not necessitate a full webpage to render. They could be used for making secondary content, such as headers, footers, and sidebars, editable in the Wagtail admin. Snippets are models which do not inherit the ``Page`` class and are thus not organized into the Wagtail tree, but can still be made editable by assigning panels and identifying the model as a snippet with ``register_snippet()``.
Snippets are not searchable or orderable in the Wagtail admin, so decide carefully if the content type you would want to build into a snippet might be more suited to a page.
Snippets are not search-able or order-able in the Wagtail admin, so decide carefully if the content type you would want to build into a snippet might be more suited to a page.
Snippet Models
--------------

Wyświetl plik

@ -127,7 +127,7 @@ Lets also add a simple interface for the search with a ``<input>`` element to ga
<div id="json-results"></div>
</div>
Finally, we'll use JQuery to make the aynchronous requests and handle the interactivity:
Finally, we'll use JQuery to make the asynchronous requests and handle the interactivity:
.. code-block:: guess
@ -186,7 +186,7 @@ Results are returned as a JSON object with this structure:
]
}
What if you wanted access to the rest of the results context or didn't feel like using JSON? Wagtail also provides a generalized AJAX interface where you can use your own template to serve results asyncronously.
What if you wanted access to the rest of the results context or didn't feel like using JSON? Wagtail also provides a generalized AJAX interface where you can use your own template to serve results asynchronously.
The AJAX interface uses the same view as the normal HTML search, ``wagtailsearch_search``, but will serve different results if Django classifies the request as AJAX (``request.is_ajax()``). Another entry in your project settings will let you override the template used to serve this response:
@ -194,7 +194,7 @@ The AJAX interface uses the same view as the normal HTML search, ``wagtailsearch
WAGTAILSEARCH_RESULTS_TEMPLATE_AJAX = 'myapp/includes/search_listing.html'
In this template, you'll have access to the same context variablies provided to the HTML template. You could provide a template in JSON format with extra properties, such as ``query.hits`` and editor's picks, or render an HTML snippet that can go directly into your results ``<div>``. If you need more flexibility, such as multiple formats/templates based on differing requests, you can set up a custom search view.
In this template, you'll have access to the same context variables provided to the HTML template. You could provide a template in JSON format with extra properties, such as ``query.hits`` and editor's picks, or render an HTML snippet that can go directly into your results ``<div>``. If you need more flexibility, such as multiple formats/templates based on differing requests, you can set up a custom search view.
.. _editors-picks: