时间:2023-07-11 04:54:02 | 来源:网站运营
时间:2023-07-11 04:54:02 来源:网站运营
Wagtail建站入门指南:venv
,它与 Python 3 一起打包。$ python3 -m venv mysiteenv$ mysite/env/Scripts/activate.bat
在 GNU/Linux 或 MacOS (bash) 上:$ python3 -m venv mysite/env$ source mysite/env/bin/activate
**对于其他 shell,**请参阅ref="https://docs.python.org/3/library/venv.html">venv文档。
mysite
将是您项目的目录。env
它里面的目录应该从任何版本控制中排除。start
类似于. 在您的项目中运行将生成一个新文件夹,其中包含一些特定于 Wagtail 的附加功能,包括所需的项目设置、一个带有空白模型和基本模板的“home”应用程序,以及一个示例“搜索”应用程序。django-admin startproject``wagtail start mysite``mysite``HomePage
mysite
已由 创建venv
,请使用附加参数运行以指定目标目录:wagtail start
INSTALLED_APPS
在settings
文件部分注册。查看此文件以了解start
命令如何在其中列出它们。HomePage
模型models.py
,以及创建主页并配置 Wagtail 以使用它的迁移。home/models.py
如下,向body
模型添加一个字段:from django.db import modelsfrom wagtail.core.models import Pagefrom wagtail.core.fields import RichTextFieldfrom wagtail.admin.edit_handlers import FieldPanelclass HomePage(Page): body = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel('body', classname="full"), ]
body
被定义为RichTextField
,一个特殊的 Wagtail 字段。当 时blank=True
,表示该字段不是必需的,可以为空。您可以使用任何Django 核心字段。content_panels
定义编辑界面的功能和布局。向 中添加字段时content_panels
,可以在 Wagtail 界面上对其进行编辑。有关创建页面模型的更多信息。python manage.py makemigrations``python manage.py migrate
home/home_page.html
)。这个模板文件可以存在于Django 模板规则识别的任何位置 ;通常它被放置在templates
应用程序内的文件夹下。home/templates/home/home_page.html
以包含以下内容:{% extends "base.html" %}{% load wagtailcore_tags %}{% block body_class %}template-homepage{% endblock %}{% block content %} {{ page.body|richtext }}{% endblock %}
base.html
引用父模板并且必须始终是模板中使用的第一个模板标记。从此模板扩展可以避免重写代码,并允许应用程序中的页面共享相似的框架(通过在子模板中使用块标记,您可以覆盖父模板中的特定内容)。wagtailcore_tags
还必须在模板顶部加载,并为 Django 提供的标签提供额外的标签。python manage.py startapp blog
blog
的应用程序,以INSTALLED_APPS
在mysite/settings/base.py
。blog/models.py
:from wagtail.core.models import Pagefrom wagtail.core.fields import RichTextFieldfrom wagtail.admin.edit_handlers import FieldPanelclass BlogIndexPage(Page): intro = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel('intro', classname="full") ]
运行和。python manage.py makemigrations``python manage.py migrate
BlogIndexPage
,默认模板名称(除非我们覆盖它)将是blog/templates/blog/blog_index_page.html
. 使用以下内容创建此文件:{% extends "base.html" %}{% load wagtailcore_tags %}{% block body_class %}template-blogindexpage{% endblock %}{% block content %} <h1>{{ page.title }}</h1> <div class="intro">{{ page.intro|richtext }}</div> {% for post in page.get_children %} <h2><a href="{% pageurl post %}">{{ post.title }}</a></h2> {{ post.specific.intro }} {{ post.specific.body|richtext }} {% endfor %}{% endblock %}
大部分内容应该很熟悉,但我们稍后会解释get_children
。请注意pageurl
标签,它类似于 Django 的url
标签,但采用 Wagtail Page 对象作为参数。BlogIndexPage
作为主页的子项,确保它在“推广”选项卡上有“博客”,然后发布它。您现在应该能够访问/blog
您网站上的 url (注意“提升”选项卡中的 slug 如何定义页面 URL)。blog/models.py
:from django.db import modelsfrom wagtail.core.models import Pagefrom wagtail.core.fields import RichTextFieldfrom wagtail.admin.edit_handlers import FieldPanelfrom wagtail.search import index# Keep the definition of BlogIndexPage, and add:class BlogPage(Page): date = models.DateField("Post date") intro = models.CharField(max_length=250) body = RichTextField(blank=True) search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('body'), ] content_panels = Page.content_panels + [ FieldPanel('date'), FieldPanel('intro'), FieldPanel('body', classname="full"), ]
在上面的模型中,我们导入,index
因为这使模型可搜索。然后,您可以列出您希望可供用户搜索的字段。python manage.py makemigrations``python manage.py migrate
blog/templates/blog/blog_page.html
以下位置创建模板:{% extends "base.html" %}{% load wagtailcore_tags %}{% block body_class %}template-blogpage{% endblock %}{% block content %} <h1>{{ page.title }}</h1> <p class="meta">{{ page.date }}</p> <div class="intro">{{ page.intro }}</div> {{ page.body|richtext }} <p><a href="{{ page.get_parent.url }}">Return to blog</a></p>{% endblock %}
请注意使用 Wagtail 的内置get_parent()
方法来获取此帖子所属博客的 URL。BlogIndexPage
. 创建帖子时,请务必选择“博客页面”类型。/blog
URL,您应该会看到如下内容:BlogIndexPage
是一个“节点”,单个BlogPage
实例是“叶子”。blog_index_page.html
:{% for post in page.get_children %} <h2><a href="{% pageurl post %}">{{ post.title }}</a></h2> {{ post.specific.intro }} {{ post.specific.body|richtext }}{% endfor %}
Wagtail 中的每个“页面”都可以从其在层次结构中的位置调用其父级或子级。但是为什么我们必须指定post.specific.intro
而不是post.intro
?这与我们定义模型的方式有关:class BlogPage(Page):
get_children()
方法为我们获取Page
基类的实例列表。当我们想要引用从基类继承的实例的属性时,Wagtail 提供了specific
检索实际BlogPage
记录的方法。虽然“title”字段存在于基本Page
模型中,但“intro”只存在于BlogPage
模型中,因此我们需要.specific
访问它。with
标签:{% for post in page.get_children %} {% with post=post.specific %} <h2><a href="{% pageurl post %}">{{ post.title }}</a></h2> <p>{{ post.intro }}</p> {{ post.body|richtext }} {% endwith %}{% endfor %}
当您开始编写更多定制的 Wagtail 代码时,您会发现一整套 QuerySet 修饰符来帮助您导航层次结构。# Given a page object 'somepage':MyModel.objects.descendant_of(somepage)child_of(page) / not_child_of(somepage)ancestor_of(somepage) / not_ancestor_of(somepage)parent_of(somepage) / not_parent_of(somepage)sibling_of(somepage) / not_sibling_of(somepage)# ... and ...somepage.get_children()somepage.get_ancestors()somepage.get_descendants()somepage.get_siblings()
更多信息请参见:页面查询集参考get_context()
方法使这成为可能。BlogIndexPage
像这样修改你的模型:class BlogIndexPage(Page): intro = RichTextField(blank=True) def get_context(self, request): # Update context to include only published posts, ordered by reverse-chron context = super().get_context(request) blogpages = self.get_children().live().order_by('-first_published_at') context['blogpages'] = blogpages return context
我们在这里所做的只是检索原始上下文,创建自定义 QuerySet,将其添加到检索到的上下文中,并将修改后的上下文返回给视图。您还需要blog_index_page.html
稍微修改模板。改变:{% for post in page.get_children %}
到 {% for post in blogpages %}
body
富文本字段,但将我们的画廊图像设置为数据库中新的专用对象类型有几个优点 - 这样,您可以完全控制图像上的布局和样式模板,而不必在富文本字段中以特定方式布置它们。它还使图像可以在别处使用,独立于博客文本 - 例如,在博客索引页面上显示缩略图。BlogPageGalleryImage
模型到models.py
:from django.db import models# New imports added for ParentalKey, Orderable, InlinePanel, ImageChooserPanelfrom modelcluster.fields import ParentalKeyfrom wagtail.core.models import Page, Orderablefrom wagtail.core.fields import RichTextFieldfrom wagtail.admin.edit_handlers import FieldPanel, InlinePanelfrom wagtail.images.edit_handlers import ImageChooserPanelfrom wagtail.search import index# ... (Keep the definition of BlogIndexPage, and update BlogPage:)class BlogPage(Page): date = models.DateField("Post date") intro = models.CharField(max_length=250) body = RichTextField(blank=True) search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('body'), ] content_panels = Page.content_panels + [ FieldPanel('date'), FieldPanel('intro'), FieldPanel('body', classname="full"), InlinePanel('gallery_images', label="Gallery images"), ]class BlogPageGalleryImage(Orderable): page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='gallery_images') image = models.ForeignKey( 'wagtailimages.Image', on_delete=models.CASCADE, related_name='+' ) caption = models.CharField(blank=True, max_length=250) panels = [ ImageChooserPanel('image'), FieldPanel('caption'), ]
运行和。python manage.py makemigrations``python manage.py migrate
Orderable
添加一个sort_order
字段,以跟踪图库中图像的顺序。ParentalKey
到BlogPage
什么高度图库图片到指定的页面。A 的ParentalKey
工作方式与 a 类似ForeignKey
,但也定义BlogPageGalleryImage
为BlogPage
模型的“子项” ,因此在提交审核和跟踪修订历史等操作中,它被视为页面的基本部分。image
是ForeignKey
Wagtail 的内置Image
模型,其中存储了图像本身。它带有专用面板类型 ,ImageChooserPanel
它提供了一个用于选择现有图像或上传新图像的弹出界面。这样,我们允许一个图像存在于多个画廊中——实际上,我们在页面和图像之间创建了多对多关系。on_delete=models.CASCADE
在外键上指定意味着如果图像从系统中删除,图库条目也将被删除。(在其他情况下,将条目留在原处可能是合适的 - 例如,如果“我们的员工”页面包含有头像的人的列表,并且其中一张照片被删除,我们宁愿将该人留在放置在没有照片的页面上。在这种情况下,我们将外键设置为。)blank=True, null=True, on_delete=models.SET_NULL
InlinePanel
到BlogPage.content_panels
品牌的编辑界面上可用的画廊图像BlogPage
。{% extends "base.html" %}{% load wagtailcore_tags wagtailimages_tags %}{% block body_class %}template-blogpage{% endblock %}{% block content %} <h1>{{ page.title }}</h1> <p class="meta">{{ page.date }}</p> <div class="intro">{{ page.intro }}</div> {{ page.body|richtext }} {% for item in page.gallery_images.all %} <div style="float: left; margin: 10px"> {% image item.image fill-320x240 %} <p>{{ item.caption }}</p> </div> {% endfor %} <p><a href="{{ page.get_parent.url }}">Return to blog</a></p>{% endblock %}
这里我们使用标签(存在于库中,在模板顶部导入)插入一个元素,带有一个参数来指示应该调整图像大小和裁剪以填充 320x240 矩形。您可以在docs 中阅读有关在模板中使用图像的更多信息。{% image %}``wagtailimages_tags``<img>``fill-320x240
main_image
方法,它从第一个画廊项目(或者None
如果不存在画廊项目)返回图像:class BlogPage(Page): date = models.DateField("Post date") intro = models.CharField(max_length=250) body = RichTextField(blank=True) def main_image(self): gallery_item = self.gallery_images.first() if gallery_item: return gallery_item.image else: return None search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('body'), ] content_panels = Page.content_panels + [ FieldPanel('date'), FieldPanel('intro'), FieldPanel('body', classname="full"), InlinePanel('gallery_images', label="Gallery images"), ]
现在可以从我们的模板中使用此方法。更新blog_index_page.html
以将主图像作为缩略图包含在每个帖子旁边:{% load wagtailcore_tags wagtailimages_tags %}{% for post in blogpages %} {% with post=post.specific %} <h2><a href="{% pageurl post %}">{{ post.title }}</a></h2> {% with post.main_image as main_image %} {% if main_image %}{% image main_image fill-160x100 %}{% endif %} {% endwith %} <p>{{ post.intro }}</p> {{ post.body|richtext }} {% endwith %}{% endfor %}
BlogPage
模型和内容面板,并在博客帖子模板上呈现链接标签。当然,我们还需要一个工作标签特定的 URL 视图。models.py
再次修改:from django.db import models# New imports added for ClusterTaggableManager, TaggedItemBase, MultiFieldPanelfrom modelcluster.fields import ParentalKeyfrom modelcluster.contrib.taggit import ClusterTaggableManagerfrom taggit.models import TaggedItemBasefrom wagtail.core.models import Page, Orderablefrom wagtail.core.fields import RichTextFieldfrom wagtail.admin.edit_handlers import FieldPanel, InlinePanel, MultiFieldPanelfrom wagtail.images.edit_handlers import ImageChooserPanelfrom wagtail.search import index# ... (Keep the definition of BlogIndexPage)class BlogPageTag(TaggedItemBase): content_object = ParentalKey( 'BlogPage', related_name='tagged_items', on_delete=models.CASCADE )class BlogPage(Page): date = models.DateField("Post date") intro = models.CharField(max_length=250) body = RichTextField(blank=True) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) # ... (Keep the main_image method and search_fields definition) content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel('date'), FieldPanel('tags'), ], heading="Blog information"), FieldPanel('intro'), FieldPanel('body'), InlinePanel('gallery_images', label="Gallery images"), ]
运行和。python manage.py makemigrations``python manage.py migrate
modelcluster
和taggit
进口的、新 BlogPageTag
模型的添加以及 上的tags
字段的添加BlogPage
。我们还借此机会使用MultiFieldPanel
incontent_panels
将日期和标签字段组合在一起以提高可读性。BlogPage
实例之一,您现在应该能够标记帖子:BlogPage
,请将其添加到blog_page.html
:{% if page.tags.all.count %} <div class="tags"> <h3>Tags</h3> {% for tag in page.tags.all %} <a href="{% slugurl 'tags' %}?tag={{ tag }}"><button type="button">{{ tag }}</button></a> {% endfor %} </div>{% endif %}
请注意,我们使用内置slugurl
标记而不是pageurl
我们之前使用的链接到此处的页面。不同之处在于slugurl
它以 Page slug(来自“提升”选项卡)作为参数。pageurl
更常用,因为它是明确的并且避免了额外的数据库查找。但是在这个循环的情况下,Page 对象不是随时可用的,所以我们回到不太喜欢的slugurl
标签。models.py
:class BlogTagIndexPage(Page): def get_context(self, request): # Filter by tag tag = request.GET.get('tag') blogpages = BlogPage.objects.filter(tags__name=tag) # Update template context context = super().get_context(request) context['blogpages'] = blogpages return context
请注意,这个基于页面的模型没有定义自己的字段。即使没有字段,子类化Page
也使它成为 Wagtail 生态系统的一部分,这样你就可以在管理中给它一个标题和 URL,这样你就可以通过从它的get_context()
方法返回一个 QuerySet 来操纵它的内容。BlogTagIndexPage
在管理员中创建一个新的。您可能希望将新页面/视图创建为 Homepage 的子项,与您的博客索引平行。在“提升”选项卡上为其添加 slug“标签”。/tags
和 Django 会告诉你你可能已经知道的事情:你需要创建一个模板blog/blog_tag_index_page.html
:{% extends "base.html" %}{% load wagtailcore_tags %}{% block content %} {% if request.GET.tag %} <h4>Showing pages tagged "{{ request.GET.tag }}"</h4> {% endif %} {% for blogpage in blogpages %} <p> <strong><a href="{% pageurl blogpage %}">{{ blogpage.title }}</a></strong><br /> <small>Revised: {{ blogpage.latest_revision_created_at }}</small><br /> {% if blogpage.author %} <p>By {{ blogpage.author.profile }}</p> {% endif %} </p> {% empty %} No pages found with that tag. {% endfor %}{% endblock %}
我们正在调用 模型latest_revision_created_at
上的内置字段Page
- 知道这总是可用的很方便。BlogPage
模型中添加一个“作者”字段,我们也没有作者的 Profile 模型——我们将这些留给读者作为练习。BlogCategory
模型。类别本身并不是一个页面,因此我们将其定义为标准的 Django,models.Model
而不是从Page
. Wagtail 为需要通过管理界面管理但不作为页面树本身的一部分存在的可重用内容片段引入了“片段”的概念;可以通过添加@register_snippet
装饰器将模型注册为片段。到目前为止,我们在页面上使用的所有字段类型也可以用于片段 - 在这里,我们将为每个类别提供一个图标图像和一个名称。添加到blog/models.py
:from wagtail.snippets.models import register_snippet@register_snippetclass BlogCategory(models.Model): name = models.CharField(max_length=255) icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) panels = [ FieldPanel('name'), ImageChooserPanel('icon'), ] def __str__(self): return self.name class Meta: verbose_name_plural = 'blog categories'
请注意,我们正在使用panels
而不是content_panels
在这里 - 由于片段通常不需要诸如 slug 或发布日期之类的字段,因此它们的编辑界面不会作为标准拆分为单独的“内容”/“推广”/“设置”选项卡,并且因此无需区分“内容面板”和“推广面板”。BlogPage
模型添加类别,作为多对多字段。我们为此使用的字段类型是ParentalManyToManyField
- 这是标准 Django 的变体,ManyToManyField
它确保所选对象正确存储在修订历史记录中的页面记录中,与ParentalKey
替换ForeignKey
一对多关系的方式大致相同。# New imports added for forms and ParentalManyToManyFieldfrom django import formsfrom django.db import modelsfrom modelcluster.fields import ParentalKey, ParentalManyToManyFieldfrom modelcluster.contrib.taggit import ClusterTaggableManagerfrom taggit.models import TaggedItemBase# ...class BlogPage(Page): date = models.DateField("Post date") intro = models.CharField(max_length=250) body = RichTextField(blank=True) tags = ClusterTaggableManager(through=BlogPageTag, blank=True) categories = ParentalManyToManyField('blog.BlogCategory', blank=True) # ... (Keep the main_image method and search_fields definition) content_panels = Page.content_panels + [ MultiFieldPanel([ FieldPanel('date'), FieldPanel('tags'), FieldPanel('categories', widget=forms.CheckboxSelectMultiple), ], heading="Blog information"), FieldPanel('intro'), FieldPanel('body'), InlinePanel('gallery_images', label="Gallery images"), ]
在这里,我们使用定义中的widget
关键字参数FieldPanel
来指定基于复选框的小部件而不是默认的多选框,因为这通常被认为对用户更友好。blog_page.html
模板以显示类别:<h1>{{ page.title }}</h1><p class="meta">{{ page.date }}</p>{% with categories=page.categories.all %} {% if categories %} <h3>Posted in:</h3> <ul> {% for category in categories %} <li style="display: inline"> {% image category.icon fill-32x32 style="vertical-align: middle" %} {{ category.name }} </li> {% endfor %} </ul> {% endif %}{% endwith %}
关键词:指南,入门