wagtail的使用

数据模型models中的基本字段

templates

template = 'home/home_page.html'

指定模板文件的位置,如果不指定,则模板文件位于本app的models同级templates文件夹下的app文件名下的与class同名的html中
在这里插入图片描述

字符型

页面中展示的内容都是models中类的一个字段,在homepage添加一个subtile字段
在这里插入图片描述
并把它展示在页面中,如下,使用content_panels 和 FieldPanel关键字
在这里插入图片描述
在这里插入图片描述

字段的实际显示名,可用verbose来处理
迁移下数据
打开首页编辑
在这里插入图片描述

目前首页是没有内容的,因为对应的渲染模板页面还没有处理。
在模板中,所有的变量被封存到了page对象中。
在这里插入图片描述
{{ }}用来显示变量。

在这里插入图片描述

文本字段

在models中增加一个问本字段
在这里插入图片描述
数据迁移下
刷新管理页面
多了一个content1字段
在这里插入图片描述
添加内容
在这里插入图片描述
模板页
在这里插入图片描述

在这里插入图片描述

带有下拉选择的文本字段

在blogpage中增加一个open_type字段,这个字段是通过下拉菜单选择,下拉菜单内容在 TYPE_CHIOCES 中定义死了。

    TYPE_CHIOCES = (
        ('switchTab', '底部标签'),
        ('navigate', '页面导航'),
    )
    open_type = models.CharField(max_length=20, choices=TYPE_CHIOCES, null=True, verbose_name='打开类型')
    # Export fields over the API
    api_fields = [
        APIField('image'),
        APIField('body'),
        APIField('open_type'),
    ]


    content_panels = Page.content_panels + [
        MultiFieldPanel([
            FieldPanel('date'),
            FieldPanel('authors', widget=forms.CheckboxSelectMultiple),
        ], heading="Blog information"),
        FieldPanel('image'),
        FieldPanel('open_type'),
        FieldPanel('body'),

富文本字段

添加一个富文本字段
在这里插入图片描述
迁移数据

在这里插入图片描述
发布出去
在这里插入图片描述
模板增加标签加载和富文本的过滤器
在这里插入图片描述
刷新页面

在这里插入图片描述

图片字段

图片的处理
在这里插入图片描述
迁移数据,刷新管理页面
在这里插入图片描述

渲染模板增加标签加载和指定图片加载尺寸
在这里插入图片描述
在这里插入图片描述

图片添加cta

文件字段

文件处理
在这里插入图片描述

迁移数据刷新管理页面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

引入bootstrap

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

优化模板文档

公共部分都移入到base.html中
mysite\templates\base.html

{% load static wagtailuserbar %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- CSS -->
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css" rel="stylesheet">
    <!-- jQuery and JavaScript Bundle with Popper -->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.slim.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.js"></script>
</head>
<body>

<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="#">Navbar</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item active">
                <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Link</a>
            </li>
            <li class="nav-item dropdown">
                <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
                   aria-haspopup="true" aria-expanded="false">
                    Dropdown
                </a>
                <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                    <a class="dropdown-item" href="#">Action</a>
                    <a class="dropdown-item" href="#">Another action</a>
                    <div class="dropdown-divider"></div>
                    <a class="dropdown-item" href="#">Something else here</a>
                </div>
            </li>
            <li class="nav-item">
                <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
            </li>
        </ul>
        <form class="form-inline my-2 my-lg-0">
            <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
            <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
        </form>
    </div>
</nav>

{% block content %}
{% endblock %}
</body>
</html>

定义一个内容块

{% block content %}
{% endblock %}

其他页面非共同内容包含其中,通过上面的定义的块,代入base.html中,如下所示。

home\templates\home\home_page.html

{% extends "base.html" %}

{% block content %}
    {% load wagtailcore_tags %}
    {% load wagtailimages_tags %}
    <div>
        <div class="alert alert-primary" role="alert">
            {{ page.subtitle }}
        </div>
        <p>
            {{ page.content1 }}
        </p>
        <p>
            {{ page.content2 | richtext }}
        </p>
        {% image page.image fill-600x200 %}
        <a href="{{ page.file.url }}">
            {{ page.file }}
        </a>
    </div>
{% endblock %}

模板

加载静态文件

图片
在这里插入图片描述
css
在这里插入图片描述

引用models元素

cta引用需要加上url,才能完整引用路径,否则引用较深目录层次会找不到
在这里插入图片描述

常用web组件的实现

带banner的首页

在这里插入图片描述
在base.html中引入公共部分的导航条,

{% load static wagtailuserbar %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- CSS -->
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css" rel="stylesheet">
    <!-- jQuery and JavaScript Bundle with Popper -->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.slim.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.js"></script>
</head>
<body>

<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="#">Navbar</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item active">
                <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Link</a>
            </li>
            <li class="nav-item dropdown">
                <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
                   aria-haspopup="true" aria-expanded="false">
                    Dropdown
                </a>
                <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                    <a class="dropdown-item" href="#">Action</a>
                    <a class="dropdown-item" href="#">Another action</a>
                    <div class="dropdown-divider"></div>
                    <a class="dropdown-item" href="#">Something else here</a>
                </div>
            </li>
            <li class="nav-item">
                <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
            </li>
        </ul>
        <form class="form-inline my-2 my-lg-0">
            <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
            <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
        </form>
    </div>
</nav>

{% block content %}
{% endblock %}
</body>
</html>

在home的模板页引入banner图片和图片跳转cta

{# templates/home/home_page.html #}
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}


{% block content %}

    {% image self.banner_image fill-900x300 as img %}
    <div class="jumbotron" style="background-image: url('{{ img.url }}'); background-size: cover ">
        <h1 class="display-4" style="color: #e6e6e6">{{ self.banner_title }}</h1>

        <div class="lead" style="color: #e6e6e6">{{ page.banner_subtitle|richtext }}</div>
        <a class="btn btn-primary btn-lg" href="{{ self.banner_cta }}" role="button">详情</a>
    </div>
{% endblock %}

在home的models中定义数据模型

# home/models.py
from django.db import models

from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, PageChooserPanel
from wagtail.images.edit_handlers import ImageChooserPanel


class HomePage(Page):
    '''HomePage models '''
    template = 'home/home_page.html'
    banner_title = models.CharField(max_length=100, blank=True, null=True)
    # max_count = 1
    banner_subtitle = RichTextField(features=['bold', 'italic'], blank=True, null=True)
    banner_image = models.ForeignKey(
        'wagtailimages.Image',
        blank=True,
        null=True,
        on_delete=models.DO_NOTHING,
        related_name="+"
    )
    banner_cta = models.ForeignKey(
        'wagtailcore.Page',
        blank=True,
        null=True,
        on_delete=models.DO_NOTHING,
        related_name="+"

    )

    content_panels = Page.content_panels + [
        FieldPanel('banner_title'),
        FieldPanel('banner_subtitle'),
        ImageChooserPanel('banner_image'),
        PageChooserPanel('banner_cta'),

    ]

    class Meta:
        verbose_name = '首页这里'
        verbose_name_plural = '首页plural'

页面主题

https://bootswatch.com/

轮播图

https://v4.bootcss.com/docs/getting-started/introduction/

StreamField 字段

StreamField字段可实现多媒体的展示
StreamField提供了一种内容编辑模型,适用于不遵循固定结构的页面(如博客文章或新闻故事),其中文本可能会穿插副标题、图像、引述和视频。它也适用于更专业的内容类型,如地图和图表(或者,对于编程博客,代码片段)。在这个模型中,这些不同的内容类型被表示为一系列的“块”,这些块可以以任何顺序重复和排列。
StreamField还提供了一个丰富的API来定义您自己的块类型,范围从简单的子块集合(例如由名字、姓氏和照片组成的“person”块)到完全自定义的具有自己编辑界面的组件。在数据库中,StreamField内容存储为JSON,确保字段的完整信息内容得到保留,而不仅仅是它的HTML表示。
下面展示如何使用StremField

使用StreamField

StreamField是一个模型字段,可以像任何其他字段一样在页面模型中定义:

from django.db import models

from wagtail.models import Page
from wagtail.fields import StreamField
from wagtail import blocks
from wagtail.admin.panels import FieldPanel
from wagtail.images.blocks import ImageChooserBlock

class BlogPage(Page):
    author = models.CharField(max_length=255)
    date = models.DateField("Post date")
    body = StreamField([
        ('heading', blocks.CharBlock(form_classname="title")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),
    ], use_json_field=True)

    content_panels = Page.content_panels + [
        FieldPanel('author'),
        FieldPanel('date'),
        FieldPanel('body'),
    ]

在本例中,BlogPage的body字段被定义为StreamField,在StreamField中,作者可以使用三种不同的块类型组成内容:标题、段落和图像,这些块可以按任何顺序使用和重复。可供作者使用的块类型定义为(name, block_type)元组列表:“name”用于标识模板中的块类型,并且应遵循变量名称的标准Python约定:小写和下划线,无空格。

您可以在StreamField块参照中找到可用块类型的完整列表。

模板呈现

StreamField像展示单个块一样,把stream 的内容作为整一个整体展示为HTML。要将此HTML包含到你的页面中,请使用{% include_block %}标记:

{% load wagtailcore_tags %}

 ...

{% include_block page.body %}

实际中使用
mysite\templates\flex\flex_page.html

{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}


{% block content %}
    <div>
        {{ self.subtitle }}
    </div>
    {% for block in page.content %}
        {% include_block block %}
    {% endfor %}

{% endblock %}

content来自这里
flex\models.py

from django.db import models
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.core import blocks
from streams import myblocks

from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField, StreamField
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, PageChooserPanel, StreamFieldPanel
from wagtail.images.edit_handlers import ImageChooserPanel


class FlexPage(Page):
    template = 'flex/flex_page.html'

    subtitle = models.CharField(max_length=100, blank=True, null=True)
    content = StreamField(
        [
            ('title_and_text', myblocks.TitleAndTextBlock()),
        ],
        blank=True,
        null=True,
    )

    content_panels = Page.content_panels + [
        FieldPanel('subtitle'),
        StreamFieldPanel('content'),

    ]

    class Meta:
        verbose_name = 'Flex Page'
        verbose_name_plural = 'Flex Pages'

为了更好地控制特定块类型的渲染,每个块对象都提供块类型和值特性:

{% load wagtailcore_tags %}

 ...

<article>
    {% for block in page.body %}
        {% if block.block_type == 'heading' %}
            <h1>{{ block.value }}</h1>
        {% else %}
            <section class="block-{{ block.block_type }}">
                {% include_block block %}
            </section>
        {% endif %}
    {% endfor %}
</article>

组合块

除了直接在StreamField中使用内置块类型外,还可以通过各种方式组合子块来构造新的块类型。这方面的例子包括:

  • 由图像选择器和文本字段组成的“带标题的图像”块
  • 一个“相关链接”部分,作者可以提供任何数量的链接到其他网页
  • 一种幻灯片放映块,其中每张幻灯片可以是图像、文本或视频,按任何顺序排列

一旦以这种方式构建了新的块类型,就可以在任何使用内置块类型的地方使用它—包括将它用作另一个块类型的组件。例如,您可以定义一个图像库块,其中每个项目都是一个“带标题的图像”块。

StructBlock

StructBlock允许您将多个“子”块组合在一起,以显示为单个块。子块作为(名称,块类型)元组列表传递给StructBlock:

 body = StreamField([
     ('person', blocks.StructBlock([
         ('first_name', blocks.CharBlock()),
         ('surname', blocks.CharBlock()),
         ('photo', ImageChooserBlock(required=False)),
         ('biography', blocks.RichTextBlock()),
     ])),
     ('heading', blocks.CharBlock(form_classname="full title")),
     ('paragraph', blocks.RichTextBlock()),
     ('image', ImageChooserBlock()),
 ])

其中person对应了一个StructBlock

在读回StreamField的内容时(例如在呈现模板时),StructBlock的值是一个类似dict的对象,其键与定义中给出的块名相对应:

<article>
    {% for block in page.body %}
        {% if block.block_type == 'person' %}
            <div class="person">
                {% image block.value.photo width-400 %}
                <h2>{{ block.value.first_name }} {{ block.value.surname }}</h2>
                {{ block.value.biography }}
            </div>
        {% else %}
            (rendering for other block types)
        {% endif %}
    {% endfor %}
</article>

子类化结构块

在StreamField定义中放置StructBlock的子块列表通常很难读懂,并且使得同一块很难在多个位置重用。因此,StructBlock可以子类化,子块定义为子类上的属性。上例中的“person”块可以重写为:

class PersonBlock(blocks.StructBlock):
    first_name = blocks.CharBlock()
    surname = blocks.CharBlock()
    photo = ImageChooserBlock(required=False)
    biography = blocks.RichTextBlock()

PersonBlock可以在StreamField定义中使用,使用方式与内置块类型相同:

body = StreamField([
    ('person', PersonBlock()),
    ('heading', blocks.CharBlock(form_classname="full title")),
    ('paragraph', blocks.RichTextBlock()),
    ('image', ImageChooserBlock()),
])

Block icons

在内容作者用于向StreamField添加新块的菜单中,每个块类型都有一个关联的图标。对于StructBlock和其他结构块类型,将使用占位符图标,因为这些块的用途特定于您的项目。要设置自定义图标,请将选项图标作为关键字参数传递给StructBlock,或作为元类的属性传递:

 body = StreamField([
     ('person', blocks.StructBlock([
         ('first_name', blocks.CharBlock()),
         ('surname', blocks.CharBlock()),
         ('photo', ImageChooserBlock(required=False)),
         ('biography', blocks.RichTextBlock()),
     ], icon='user')),
     ('heading', blocks.CharBlock(form_classname="full title")),
     ('paragraph', blocks.RichTextBlock()),
     ('image', ImageChooserBlock()),
 ])

其中的icon=‘user’

 class PersonBlock(blocks.StructBlock):
     first_name = blocks.CharBlock()
     surname = blocks.CharBlock()
     photo = ImageChooserBlock(required=False)
     biography = blocks.RichTextBlock()

     class Meta:
         icon = 'user'

ListBlock

ListBlock定义了一个重复块,允许内容作者插入任意多个特定块类型的实例。例如,由多个图像组成的“多媒体资料”块可以定义如下:

 body = StreamField([
     ('gallery', blocks.ListBlock(ImageChooserBlock())),
     ('heading', blocks.CharBlock(form_classname="full title")),
     ('paragraph', blocks.RichTextBlock()),
     ('image', ImageChooserBlock()),
 ])

在读回StreamField的内容时(例如在呈现模板时),ListBlock的值是子值的列表:

<article>
    {% for block in page.body %}
        {% if block.block_type == 'gallery' %}
            <ul class="gallery">
                {% for img in block.value %}
                    <li>{% image img width-400 %}</li>
                {% endfor %}
            </ul>
        {% else %}
            (rendering for other block types)
        {% endif %}
    {% endfor %}
</article>

StreamBlock

StreamBlock定义了一组子块类型,这些子块类型可以通过与StreamField本身相同的机制以任何顺序混合和重复。例如,支持图像和视频幻灯片的轮播图可以定义如下:

 body = StreamField([
     ('carousel', blocks.StreamBlock([
         'image': ImageChooserBlock(),
         'video': EmbedBlock(),
     ])),
     ('heading', blocks.CharBlock(form_classname="full title")),
     ('paragraph', blocks.RichTextBlock()),
     ('image', ImageChooserBlock()),
 ])

StreamBlock也可以与StructBlock相同的方式子类化,子块被指定为类上的属性:

class PersonBlock(blocks.StreamBlock):
    image = ImageChooserBlock()
    video = EmbedBlock()

    class Meta:
        icon = 'image'

以这种方式定义的StreamBlock子类也可以传递给StreamField定义,而不是传递块类型列表。这允许设置一组公共块类型,以便在多个页面类型上使用:

class CommonContentBlock(blocks.StreamBlock):
    heading = blocks.CharBlock(form_classname="full title")
    paragraph = blocks.RichTextBlock()
    image = ImageChooserBlock()


class BlogPage(Page):
    body = StreamField(CommonContentBlock())

当读回StreamField的内容时,StreamBlock的值是具有block类型和值属性的块对象序列,就像StreamField本身的顶级值一样。

<article>
    {% for block in page.body %}
        {% if block.block_type == 'carousel' %}
            <ul class="carousel">
                {% for slide in block.value %}
                    {% if slide.block_type == 'image' %}
                        <li class="image">{% image slide.value width-200 %}</li>
                    {% else %}
                        <li> class="video">{% include_block slide %}</li>
                    {% endif %}
                {% endfor %}
            </ul>
        {% else %}
            (rendering for other block types)
        {% endif %}
    {% endfor %}
</article>

Per-block templates

默认情况下,每个块都使用简单、最小的HTML标记呈现,或者根本不使用任何标记。例如,CharBlock值呈现为纯文本,而ListBlock在

  • 包装器中输出其子块。要使用自己的自定义HTML呈现覆盖此选项,可以将模板参数传递给块,给出要呈现的模板文件的文件名。这对于从StructBlock派生的自定义块类型特别有用:

('person', blocks.StructBlock(
    [
        ('first_name', blocks.CharBlock()),
        ('surname', blocks.CharBlock()),
        ('photo', ImageChooserBlock(required=False)),
        ('biography', blocks.RichTextBlock()),
    ],
    template='myapp/blocks/person.html',
    icon='user'
))

Or, when defined as a subclass of StructBlock:
或者,当定义为结构块的子类时

class PersonBlock(blocks.StructBlock):
    first_name = blocks.CharBlock()
    surname = blocks.CharBlock()
    photo = ImageChooserBlock(required=False)
    biography = blocks.RichTextBlock()

    class Meta:
        template = 'myapp/blocks/person.html'
        icon = 'user'

在模板中,可以将块值作为变量值进行访问:

{% load wagtailimages_tags %}

<div class="person">
    {% image value.photo width-400 %}
    <h2>{{ value.first_name }} {{ value.surname }}</h2>
    {{ value.biography }}
</div>

{% load wagtailcore_tags wagtailimages_tags %}

<div class="person">
    {% image value.photo width-400 %}
    <h2>{% include_block value.first_name %} {% include_block value.surname %}</h2>
    {% include_block value.biography %}
</div>

Like Django’s {% include %} tag, {% include_block %} also allows passing additional variables to the included template, through the syntax {% include_block my_block with foo=“bar” %}:
As well as passing variables from the parent template, block subclasses can pass additional template variables of their own by overriding the get_context method:

重写class定义获取更多api数据

从Wagtail 1.9开始,您可以通过覆盖Block上的get_api_representation()方法来在StreamField中修改Block的API表示形式。

假如在blocks.py中定义了这个类
    # 增加的定义
from wagtail.images.blocks import ImageChooserBlock as DefaultImageChooserBlock

class ImageChooserBlock(DefaultImageChooserBlock):
    def get_api_representation(self, value, context=None):
        if value:
            return {
                'id': value.id,
                'title': value.title,
                # 'large': value.get_rendition('width-1000').attrs_dict,
                'mobile': value.get_rendition('width-320').attrs_dict,
                # 'thumbnail': value.get_rendition('fill-120x120').attrs_dict,
            }


 # 这里引用  ImageChooserBlock          
class ImageBlock(StructBlock):
    """
    Custom `StructBlock` for utilizing images with associated caption and
    attribution data
    """

    image = ImageChooserBlock(required=True)
    caption = CharBlock(required=False)
    attribution = CharBlock(required=False)

    class Meta:
        icon = "image"
        template = "blocks/image_block.html"



# StreamBlocks  这里引用ImageBlock
class BaseStreamBlock(StreamBlock):
    """
    Define the custom blocks that `StreamField` will utilize
    """

    heading_block = HeadingBlock()
    paragraph_block = RichTextBlock(
        icon="pilcrow", template="blocks/paragraph_block.html"
    )
    image_block = ImageBlock()
    block_quote = BlockQuote()
    embed_block = EmbedBlock(
        help_text="Insert an embed URL e.g https://www.baidu.com",
        icon="media",
        template="blocks/embed_block.html",
    )            

然后你在引用使用BaseStreamBlock时,就可以获得较多的image的数据,在api调用中有用。

card

有时,您需要一个可以有多个重复内容区域的StreamField。一个很好的例子是被称为卡片的设计组件。在这里,我们将探讨一个ListBlock,使我们能够使用自定义数据、ImageChooserBlock、PageChooserBlock以及如何在Wagtail CMS模板中循环使用ListBlock创建无限的卡片。
myblocks.py增加以下内容

class Cardblock(blocks.StructBlock):
    title = blocks.CharBlock(required=True,help_text='add your title here')
    # text = blocks.TextBlock(required=True,help_text='add your text here')
    cards = blocks.ListBlock(
        blocks.StructBlock(
            [
                ('image',ImageChooserBlock(required=True)),
                ('title',blocks.CharBlock(required=True, max_length=40)),
                ('text',blocks.TextBlock(required=True,max_length=200)),
                ('button_page',blocks.PageChooserBlock(required=False)),
                ('button_url',blocks.URLBlock(required=False)),
            ]
        )
    )

flex/models.py增加以下内容

class FlexPage(Page):
    template = 'flex/flex_page.html'

    subtitle = models.CharField(max_length=100, blank=True, null=True)
    content = StreamField(
        [
            ('title_and_text', myblocks.TitleAndTextBlock()),
            ('full_richtext', myblocks.RichTextBlock()),
            ('cards',myblocks.Cardblock()),    ## add is here

        ],
        blank=True,
        null=True,
    )

    content_panels = Page.content_panels + [
        FieldPanel('subtitle'),
        StreamFieldPanel('content'),

    ]

    class Meta:
        verbose_name = 'Flex Page'
        verbose_name_plural = 'Flex Pages'

Streams/myblocks.py

class CardBlock(blocks.StructBlock):
    """Cards with image and text and button(s)."""

    title = blocks.CharBlock(required=True, help_text="Add your title")

    cards = blocks.ListBlock(
        blocks.StructBlock(
            [
                ("image", ImageChooserBlock(required=True)),
                ("title", blocks.CharBlock(required=True, max_length=40)),
                ("text", blocks.TextBlock(required=True, max_length=200)),
                ("button_page", blocks.PageChooserBlock(required=False)),
                (
                    "button_url",
                    blocks.URLBlock(
                        required=False,
                        help_text="If the button page above is selected, that will be used first.",  # noqa
                    ),
                ),
            ]
        )
    )

    class Meta:  # noqa
        template = "streams/cards_block.html"
        icon = "placeholder"
        label = "Staff Cards"

刷新管理页面已增加card
在这里插入图片描述

StreamField实例

首先创建一个新的app

(env) C:\djproject\wagprj\mysite>django-admin startapp article

注册到配置文件base.py
在这里插入图片描述
article\models.py

from wagtail.core.models import Page, Orderable
from django.db import models
from wagtail.images.blocks import ImageChooserBlock
from wagtail.core import blocks

from wagtail.core.models import Page
from wagtail.core.fields import RichTextField, StreamField
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, StreamFieldPanel


class Article(Page):
    intro = models.TextField(blank=True, null=True)
    content = StreamField(
        [
            ('subtitle', blocks.TextBlock()),
            ('paragraph', blocks.TextBlock()),
            ('image', ImageChooserBlock()),
        ]
    )

    content_panels = Page.content_panels + [
        FieldPanel('intro'),
        StreamFieldPanel('content'),
    ]

做下数据迁移
管理页面在首页下增加一个子页面,类型选article
在这里插入图片描述

在这里插入图片描述
还不能访问,需要增加对应的模板

在这里插入图片描述
创建模板文件
在这里插入图片描述
article\templates\article\article.html
内容替换如下

{% extends 'base.html' %}

{% block content %}
    {{ page.title }}
    {{ page.intro }}

    {% for item in page.content %}
        <div>
            {{ item }}
        </div>
    {% endfor %}

{% endblock %}

在这里插入图片描述
完善一下,增加单独处理标签的能力

{% extends 'base.html' %}

{% block content %}
    <div>
        <h2>
            {{ page.title }}
        </h2>
    </div>

    {% for item in page.content %}
        {% if item.block_type == 'subtitle' %}
            <div>
                <h4>
                    {{ item }}
                </h4>
            </div>
        {% endif %}
        {% if item.block_type == 'paragraph' %}
            <div>
                <p style="color: #308282">
                    {{ item }}
                </p>
            </div>
        {% endif %}
        {% if item.block_type == 'image' %}
            <div>
                {{ item }}
            </div>
        {% endif %}
    {% endfor %}

{% endblock %}

snippets

以register_snippet 作为装饰器定义Snippet 数据模型

snippets是一小段内容,而不是一个完整的web页面。一般被用来作为第二种内容展示,如,文章头部,注脚,侧部栏目,在管理后台可以进行编辑。snippets是一个Django 数据模型,没有继承Page类,因此没有组织到wagtail的目录树中。但是,他们仍然可以被编辑,在代码中以register_snippet 作为装饰,并通过访问后台面板即可访问。
snippets 缺乏很多pages的特性,比如,wagtail管理后台可排序,有一个确定的url,使用时候需要根据你的内容来权衡是否使用。

from django.db import models

from wagtail.admin.panels import FieldPanel
from wagtail.snippets.models import register_snippet

# ...

@register_snippet
class Advert(models.Model):
    url = models.URLField(null=True, blank=True)
    text = models.CharField(max_length=255)

    panels = [
        FieldPanel("url"),
        FieldPanel("text"),
    ]

    def __str__(self):
        return self.text

如果不清楚是在哪里定义了snippet,可以在后台控制面板中打开snippet编辑页面,从地址栏可定位代码位置。
在这里插入图片描述

使用 register_snippet 作为函数

虽然 @register_snippet装饰器很方便 ,但是推荐使用注册snippets作为函数 在你的 wagtail_hooks.py 文件中,例如;

# myapp/wagtail_hooks.py
from wagtail.snippets.models import register_snippet

from myapp.models import Advert

register_snippet(Advert)

以这种方式注册片段允许您稍后使用自定义的 SnippetViewSet 类进行进一步的自定义。这也提供了 Django 模型和 Wagtail 特定关注点之间的更好分离。例如,不是在模型类上定义面板或 edit_handler,而是可以在 SnippetViewSet 类上定义它们:
这个定义好之后,通过数据迁移,即可在wagtail管理后台看到对应的菜单

# myapp/wagtail_hooks.py
from wagtail.snippets.models import register_snippet
from wagtail.snippets.views.snippets import SnippetViewSet

from myapp.models import Advert


class AdvertViewSet(SnippetViewSet):
    model = Advert

    panels = [
        FieldPanel("url"),
        FieldPanel("text"),
    ]

# Instead of using @register_snippet as a decorator on the model class,
# register the snippet using register_snippet as a function and pass in
# the custom SnippetViewSet subclass.
register_snippet(AdvertViewSet)

在这里插入图片描述
官方的帮助文档是按下面方式处理的

from django.db import models

from wagtail.admin.edit_handlers import FieldPanel
from wagtail.snippets.models import register_snippet

...

@register_snippet
class Advert(models.Model):
    url = models.URLField(null=True, blank=True)
    text = models.CharField(max_length=255)

    panels = [
        FieldPanel('url'),
        FieldPanel('text'),
    ]

    def __str__(self):
        return self.text

Advert model 使用了基本Django数据模型类 model class and defines two properties: text and URL. The editing interface is very close to that provided for Page-derived models, with fields assigned in the panels property. Snippets do not use multiple tabs of fields, nor do they provide the “save as draft” or “submit for moderation” features.

@register_snippet tells Wagtail to treat the model as a snippet. The panels list defines the fields to show on the snippet editing page. It’s also important to provide a string representation of the class through def str(self): so that the snippet objects make sense when listed in the Wagtail admin.

绑定页面到 Snippets

即在页面中引用这个片段。
在这里插入图片描述

在上面的例子中,广告列表是一个固定的列表,它通过自定义模板标签显示,与页面上的任何其他内容无关。这对于侧边栏中的常见面板来说可能是您想要的,但是,在另一种情况下,您可能希望仅在特定页面上显示一个特定的代码片段实例。这可以通过在您的页面模型中定义对代码片段模型的外键并在页面的 content_panels 列表中添加一个 SnippetChooserPanel 来实现。例如,如果您想在 BookPage 实例上显示特定的广告:

from wagtail.snippets.edit_handlers import SnippetChooserPanel
# ...
class BookPage(Page):
    advert = models.ForeignKey(
        'demo.Advert',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )

    content_panels = Page.content_panels + [
        SnippetChooserPanel('advert'),
        # ...
    ]

然后,您可以在模板中通过 page.advert 访问该代码片段。

要将多个广告附加到页面,可以将 SnippetChooserPanel 放置在 BookPage 的内联子对象上,而不是 BookPage 本身。在这里,这个子模型被命名为 BookPageAdvertPlacement(之所以这样命名,是因为每次在 BookPage 上放置广告时都会有一个这样的对象)。

from django.db import models

from wagtail.core.models import Page, Orderable
from wagtail.snippets.edit_handlers import SnippetChooserPanel

from modelcluster.fields import ParentalKey

...

class BookPageAdvertPlacement(Orderable, models.Model):
    page = ParentalKey('demo.BookPage', on_delete=models.CASCADE, related_name='advert_placements')
    advert = models.ForeignKey('demo.Advert', on_delete=models.CASCADE, related_name='+')

    class Meta(Orderable.Meta):
        verbose_name = "advert placement"
        verbose_name_plural = "advert placements"

    panels = [
        SnippetChooserPanel('advert'),
    ]

    def __str__(self):
        return self.page.title + " -> " + self.advert.text


class BookPage(Page):
    ...

    content_panels = Page.content_panels + [
        InlinePanel('advert_placements', label="Adverts"),
        # ...
    ]

These child objects are now accessible through the page’s advert_placements property, and from there we can access the linked Advert snippet as advert. In the template for BookPage, we could include the following:

{% for advert_placement in page.advert_placements.all %}
    <p>
        <a href="{{ advert_placement.advert.url }}">
            {{ advert_placement.advert.text }}
        </a>
    </p>
{% endfor %}

定制管理平台

定制管理界面模板

In your projects with Wagtail, you may wish to replace elements such as the Wagtail logo within the admin interface with your own branding. This can be done through Django’s template inheritance mechanism.

You need to create a templates/wagtailadmin/ folder within one of your apps - this may be an existing one, or a new one created for this purpose, for example, dashboard. This app must be registered in INSTALLED_APPS before wagtail.admin:

INSTALLED_APPS = (
    # ...

    'dashboard',

    'wagtail',
    'wagtail.admin',

    # ...
)

定制品牌

The template blocks that are available to customise the branding in the admin interface are as follows:

branding_logo
To replace the default logo, create a template file dashboard/templates/wagtailadmin/base.html that overrides the block branding_logo:

{% extends "wagtailadmin/base.html" %}
{% load static %}

{% block branding_logo %}
    <img src="{% static 'images/custom-logo.svg' %}" alt="Custom Project" width="80" />
{% endblock %}

The logo also appears in the following pages and can be replaced with its template file:

login page - create a template file dashboard/templates/wagtailadmin/login.html that overwrites the branding_logo block.

404 error page - create a template file dashboard/templates/wagtailadmin/404.html that overrides the branding_logo block.

wagtail userbar - create a template file dashboard/templates/wagtailadmin/userbar/base.html that overwrites the branding_logo block.

branding_favicon
To replace the favicon displayed when viewing admin pages, create a template file dashboard/templates/wagtailadmin/admin_base.html that overrides the block branding_favicon:

{% extends "wagtailadmin/admin_base.html" %}
{% load static %}

{% block branding_favicon %}
    <link rel="shortcut icon" href="{% static 'images/favicon.ico' %}" />
{% endblock %}

branding_title
To replace the title prefix (which is ‘Wagtail’ by default), create a template file dashboard/templates/wagtailadmin/admin_base.html that overrides the block branding_title:

{% extends "wagtailadmin/admin_base.html" %}

{% block branding_title %}Frank's CMS{% endblock %}

定制登录界面

To replace the login message, create a template file dashboard/templates/wagtailadmin/login.html that overrides the block branding_login:

{% extends "wagtailadmin/login.html" %}

{% block branding_login %}Sign in to Frank's Site{% endblock %}

定制欢迎界面

To replace the welcome message on the dashboard, create a template file dashboard/templates/wagtailadmin/home.html that overrides the block branding_welcome:

{% extends "wagtailadmin/home.html" %}

{% block branding_welcome %}Welcome to Frank's Site{% endblock %}

去掉登录界面下面图标

找到虚拟环境安装的站点包,这个项目在这个位置C:\djangoprj\Innovativewagprj\venv\Lib\site-packages\wagtail\admin\templates\wagtailadmin\login.html
注释掉图标部分。
在这里插入图片描述

修改部分没有汉化的部分

在这里插入图片描述
在snipset的小组件上有些地方会没有汉化,可以找到源码位置,如下面路径

/home/gsl/mysite/myvenv/lib/python3.10/site-packages/wagtail/snippets

sudo vi widgets.py

替换这个位置
在这里插入图片描述

把wagtail集成到django项目

安装插件

$ pip install wagtail

安装后的内容


(venv) C:\djangoprj\eshop>pip list
Package             Version
------------------- ---------
anyascii            0.2.0
asgiref             3.3.4
beautifulsoup4      4.9.3
certifi             2020.12.5
chardet             4.0.0
Django              3.2.3
django-cors-headers 3.7.0
django-filter       2.4.0
django-modelcluster 5.1
django-taggit       1.4.0
django-tinymce      3.3.0
django-treebeard    4.5.1
djangorestframework 3.12.4
draftjs-exporter    2.1.7
et-xmlfile          1.1.0
html5lib            1.1
idna                2.10
l18n                2020.6.1
openpyxl            3.0.7
Pillow              8.2.0
pip                 21.1.2
PyMySQL             1.0.2
pytz                2021.1
requests            2.25.1
setuptools          57.0.0
six                 1.16.0
soupsieve           2.2.1
sqlparse            0.4.1
tablib              3.0.0
telepath            0.1.1
urllib3             1.26.5
wagtail             2.13
webencodings        0.5.1
Willow              1.4
xlrd                2.0.1
XlsxWriter          1.4.3
xlwt                1.3.0


Settings配置

INSTALLED_APPS:

'wagtail.contrib.forms',
'wagtail.contrib.redirects',
'wagtail.embeds',
'wagtail.sites',
'wagtail.users',
'wagtail.snippets',
'wagtail.documents',
'wagtail.images',
'wagtail.search',
'wagtail.admin',
'wagtail.core',

'modelcluster',
'taggit',

MIDDLEWARE:

'wagtail.contrib.redirects.middleware.RedirectMiddleware',

Add a STATIC_ROOT setting, if your project does not have one already:

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

Add MEDIA_ROOT and MEDIA_URL settings, if your project does not have these already:

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

Add a WAGTAIL_SITE_NAME - this will be displayed on the main dashboard of the Wagtail admin backend:

WAGTAIL_SITE_NAME = 'My Example Site'

Various other settings are available to configure Wagtail’s behaviour - see Settings.

URL配置

from django.urls import path, re_path, include

from wagtail.admin import urls as wagtailadmin_urls
from wagtail.core import urls as wagtail_urls
from wagtail.documents import urls as wagtaildocs_urls

urlpatterns = [
    ...
    path('cms/', include(wagtailadmin_urls)),
    path('documents/', include(wagtaildocs_urls)),
    path('pages/', include(wagtail_urls)),
    ...
]

wagtailadmin_urls provides the admin interface for Wagtail. This is separate from the Django admin interface (django.contrib.admin); Wagtail-only projects typically host the Wagtail admin at /admin/, but if this would clash with your project’s existing admin backend then an alternative path can be used, such as /cms/ here.

wagtaildocs_urls is the location from where document files will be served. This can be omitted if you do not intend to use Wagtail’s document management features.

wagtail_urls is the base location from where the pages of your Wagtail site will be served. In the above example, Wagtail will handle URLs under /pages/, leaving the root URL and other paths to be handled as normal by your Django project. If you want Wagtail to handle the entire URL space including the root URL, this can be replaced with:

错误

报错如下,可能是Pillow的版本不对,降低下版本即可。

wagtailusers.UserProfile.avatar: (fields.E210) Cannot use ImageField because Pillow is not installed.
        HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "python -m pip install Pillow".


(venv) C:\djangoprj\hotlinewag>pip uninstall Pillow
Found existing installation: Pillow 8.3.0
Uninstalling Pillow-8.3.0:
  Would remove:
    c:\djangoprj\hotlinewag\venv\lib\site-packages\pil\*
    c:\djangoprj\hotlinewag\venv\lib\site-packages\pillow-8.3.0.dist-info\*
Proceed (y/n)? y
  Successfully uninstalled Pillow-8.3.0

(venv) C:\djangoprj\hotlinewag>pip install Pillow==8.2.0
Collecting Pillow==8.2.0
  Using cached Pillow-8.2.0-cp38-cp38-win_amd64.whl (2.2 MB)
Installing collected packages: Pillow
Successfully installed Pillow-8.2.0

wagtail的api

基本配置

使能app

在配置文件中加入api应用

# settings.py

INSTALLED_APPS = [
    ...

    'wagtail.api.v2',

    ...
]

可选的,通过添加rest_frameworkINSTALLED_APPS. 可以通过浏览器来查看api,如果是基本的json格式输出是不必须的。

配置endpoints

下面配置那些内容暴露到API。每种内容类型有对应的endpoint。endpoint结合路由,通过url配置连接项目的其他部分。
三种endpoint

Pages wagtail.api.v2.views.PagesAPIViewSet
Images wagtail.images.api.v2.views.ImagesAPIViewSet
Documents wagtail.documents.api.v2.views.DocumentsAPIViewSet

可以继承他们定制自己的类。也可以从基础类派生

 wagtail.api.v2.views.BaseAPIViewSet

在项目目录新建一个文件api.py
在这里插入图片描述

# api.py

from wagtail.api.v2.views import PagesAPIViewSet
from wagtail.api.v2.router import WagtailAPIRouter
from wagtail.images.api.v2.views import ImagesAPIViewSet
from wagtail.documents.api.v2.views import DocumentsAPIViewSet

# Create the router. "wagtailapi" is the URL namespace
api_router = WagtailAPIRouter('wagtailapi')

# Add the three endpoints using the "register_endpoint" method.
# The first parameter is the name of the endpoint (eg. pages, images). This
# is used in the URL of the endpoint
# The second parameter is the endpoint class that handles the requests
api_router.register_endpoint('pages', PagesAPIViewSet)
api_router.register_endpoint('images', ImagesAPIViewSet)
api_router.register_endpoint('documents', DocumentsAPIViewSet)

注册url

注册url,使Django能够路由请求道API。

# urls.py

from .api import api_router

urlpatterns = [
    ...

    path('api/v2/', api_router.urls),

    ...

    # 确保api_route.urls 必须在wagtail_urls之前。
    re_path(r'^', include(wagtail_urls)),
]

访问api

通过以上配置,可以访问pages通过 /api/v2/pages/, images 通过 /api/v2/images/ 和 documents 通过 /api/v2/documents/
所有页面
在这里插入图片描述
单个一面
在这里插入图片描述

图像、文档同样
在这里插入图片描述

使字段能够访问

从上面看到,不是所有的页面的定制字段都能够通过API访问到,实现这一点,可以通过添加字段列表到api_fields属性来实现。

# models.py

from wagtail.api import APIField

class BlogPageAuthor(Orderable):
    page = models.ForeignKey('blog.BlogPage', on_delete=models.CASCADE, related_name='authors')
    name = models.CharField(max_length=255)

    api_fields = [
        APIField('name'),
    ]


class BlogPage(Page):
    published_date = models.DateTimeField()
    body = RichTextField()
    feed_image = models.ForeignKey('wagtailimages.Image', on_delete=models.SET_NULL, null=True, ...)
    private_field = models.CharField(max_length=255)

    # Export fields over the API
    api_fields = [
        APIField('published_date'),
        APIField('body'),
        APIField('feed_image'),
        APIField('authors'),  # This will nest the relevant BlogPageAuthor objects in the API response
    ]

在models增加导入,

from wagtail.api import APIField

在APIField中加入需要暴露的字段,各种类型字段都可以。

在这里插入图片描述

在这里插入图片描述
图片的详细链接
在这里插入图片描述

按类型获取

在这里插入图片描述

限制获取的字段

可以获取指定的字段,包含在嵌套中的字段。
在这里插入图片描述

限制获取数量

分页使用,每页显示数量。
在这里插入图片描述

偏移量实现分页获取

偏移量指出了每次获取记录的第一条在总记录的中的位置,第一个位置的偏移量是0,这样利用偏移量+每页条数可以分页获取。
在这里插入图片描述

排序

字母顺序
在这里插入图片描述

order=-title将获得反向排序。

获得子页面

在这里插入图片描述

有个问题,获取子页面,没法获取子页面上的image,所以没法按索引页面和它的子页面来分类获取

分类获取数据

发现wagtail只能通过page的type来获取数据时候,才能获取到更多的数据,如果是获得子页面的方法获得的分类数据,就没法获取更多深层次数据,所以需要在页面中增加一个分类字段,通过页面类型和分类字段获取image

http://localhost:8000/api/v2/pages/?type=blog.BlogPage&open_type=switchTab&fields=image,open_type

其中type=blog.BlogPage指定获取的页面类型,没有这个前提,没法获取详细页面数据;open_type=switchTab是自定义的页面分类依据字段,通过这个字段获取到相同分类的页面;fields=image,open_type定义了需要详细显示的字段,没有可能是不显示的。当然api暴露也是要有的。

获取orderable数据

把需要暴露的orderable添加到APIfield
在这里插入图片描述
在orderable中添加需要暴露的字段到APIfiled
在这里插入图片描述

只查询orderable的数据

http://192.168.2.21/api/v2/pages/3/?fields=_,mycarousel

在这里插入图片描述

获取StreamField 中的image的url

通常的方法只能获得StreamField 中图片的id,不能满足需要,做一下改动
page中引用的StreamField 如下

from streams.blocks import BaseStreamBlock
。。。

    body = StreamField(
        BaseStreamBlock(), verbose_name="Page body", blank=True
。。。

blocks中的BaseStreamBlock

# 原有的导入
# from wagtail.images.blocks import ImageChooserBlock
# 增加的导入
from wagtail.images.blocks import ImageChooserBlock as DefaultImageChooserBlock


# 增加的定义
class ImageChooserBlock(DefaultImageChooserBlock):
    def get_api_representation(self, value, context=None):
        if value:
            return {
                'id': value.id,
                'title': value.title,
                'large': value.get_rendition('width-1000').attrs_dict,
                'mobile': value.get_rendition('width-320').attrs_dict,
                'thumbnail': value.get_rendition('fill-120x120').attrs_dict,
            }

# 原有的结构
class ImageBlock(StructBlock):
    """
    Custom `StructBlock` for utilizing images with associated caption and
    attribution data
    """
    image = ImageChooserBlock(required=True)
    caption = CharBlock(required=False)
    attribution = CharBlock(required=False)

    class Meta:
        icon = 'image'
        template = "blocks/image_block.html"

相当于重写了get_api_representation,可以灵活定义图片的大小。
返回结果
在这里插入图片描述

表单

https://stackoverflow.com/questions/61289214/wagtail-form-file-upload

https://github.com/spapas/wagtail-multi-upload

models的序列化

对于无法获取的api展现的库表可以采用drf的序列化方法

一般models序列化

按drf要求即可,看参考django的文章
需要配置url,view,和serializers的py文件

serializers.py


from rest_framework import serializers
from base.blocks import Country
from home.models import Category
from wagtail.images.models import Image

class CountrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Country
        fields = ('id', 'title',)


class MyImageSerializer(serializers.ModelSerializer):
    class Meta:
        model =  Image
        fields = ('id', 'title','file',)

class CategorySerializer(serializers.ModelSerializer):
    cxcategory = CountrySerializer()
    image = MyImageSerializer()
    class Meta:
        model = Category
        fields = ('id', 'title', 'image', 'cxcategory',)

urls.py

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from home import views


urlpatterns = [
    path('CategoryLIst/', views.CategoryListView.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

views.py

from rest_framework import generics
from rest_framework import permissions
from rest_framework.authtoken.admin import User
from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination
from home.models import Category
from home.serializers import CategorySerializer
import os
from django.conf import settings
from django.http import JsonResponse
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt


class CategoryListView(generics.ListAPIView):
    '''分类导航图标列表'''
    serializer_class = CategorySerializer
    permissin_classes = (permissions.AllowAny,)
    pagination_class = LimitOffsetPagination  # 分页 请求加 ?limit = xx

    def get_queryset(self):
        # user = self.request.user
        queryset = Category.objects.all()
        return queryset


内置model序列化

查到model名字
比如wagtail.images.models在这里插入图片描述
从下面定制示例可知是image

# models.py
from django.db import models

from wagtail.images.models import Image, AbstractImage, AbstractRendition


class CustomImage(AbstractImage):
    # Add any extra fields to image here

    # To add a caption field:
    # caption = models.CharField(max_length=255, blank=True)

    admin_form_fields = Image.admin_form_fields + (
        # Then add the field names here to make them appear in the form:
        # 'caption',
    )


class CustomRendition(AbstractRendition):
    image = models.ForeignKey(CustomImage, on_delete=models.CASCADE, related_name='renditions')

    class Meta:
        unique_together = (
            ('image', 'filter_spec', 'focal_point_key'),
        )

然后可以这样进行serializers

class MyImageSerializer(serializers.ModelSerializer):
    class Meta:
        model =  Image
        fields = ('id', 'title','file',)

Django数据模型的纳管

Wagtail 的数据模型管理类和Django的是不同的,虽然有相同的名字和实现相同的功能,但还是有些不同。添加和编辑表单仍然通过panels和edit_handlers来实现。
在Wagtail中控制那些字段显示或可编辑在数据模型中,以及如何分组和排序,不管你的数据模型是页类型或片段或是标准的django模型。 Wagtail’s ModelAdmin 类更多的关心列表配置。例如,列表显示,列表过滤,搜索字段类似与Django,同时字段,字段设置,排除等其他属性是wagtail不支持的。

配置

添加 wagtail.contrib.modeladmin to your INSTALLED_APPS:

INSTALLED_APPS = [
   ...
   'wagtail.contrib.modeladmin',
]

使用

可以定义普通的Django 模型,然后用ModelAdmin创建一个菜单来查看和编辑这个模型。

models.py looks like this:

from django.db import models
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.images.edit_handlers import ImageChooserPanel

class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.CharField(max_length=255)
    cover_photo = models.ForeignKey(
        'wagtailimages.Image',
        null=True, blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )

    panels = [
        FieldPanel('title'),
        FieldPanel('author'),
        ImageChooserPanel('cover_photo')
    ]

You can specify FieldPanels like ImageChooserPanel, PageChooserPanel, and DocumentChooserPanel within the panels attribute of the model. This lets you use Wagtail-specific features in an otherwise traditional Django model.

创建wagtail_hooks.py in your app directory would look something like this:
也可以利用admin.py文件。在其中录入以下内容。

from wagtail.contrib.modeladmin.options import (
    ModelAdmin, modeladmin_register)
from .models import Book


class BookAdmin(ModelAdmin):
    model = Book
    menu_label = 'Book'  # ditch this to use verbose_name_plural from model
    menu_icon = 'pilcrow'  # change as required
    menu_order = 200  # will put in 3rd place (000 being 1st, 100 2nd)
    add_to_settings_menu = False  # or True to add your model to the Settings sub-menu
    exclude_from_explorer = False # or True to exclude pages of this type from Wagtail's explorer view
    list_display = ('title', 'author')
    list_filter = ('author',)
    search_fields = ('title', 'author')

# Now you just need to register your customised ModelAdmin class with Wagtail
modeladmin_register(BookAdmin)

实际的例子:
models.py

from django.db import models

# 图片上传模型
class Image(models.Model):
    eventID = models.CharField(max_length=200, verbose_name='事件id')
    images = models.ImageField(upload_to='images/uploads/%Y/%m/%d/', blank=True)

# 投诉类数据模型
class ComplainFirstCategory(models.Model):
    """
    投诉一级类别
    """
    name = models.CharField(max_length=200, verbose_name='名称')

    class Meta:
        verbose_name = '投诉一级类别'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name  # 被引用时返回的值


class ComplainSecondCategory(models.Model):
    """
    投诉二级类别
    """
    name = models.CharField(max_length=200, verbose_name='名称')
    ComplainFirstCategory = models.ForeignKey(ComplainFirstCategory, related_name='CompSecondCate',
                                              on_delete=models.CASCADE, verbose_name='类别')

    class Meta:
        verbose_name = '投诉二级类别'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name  # 被引用时返回的值


class Complain(models.Model):
    """
    投诉
    """
    eventID = models.CharField(max_length=200, verbose_name='Id', blank=True, null=True)
    title = models.CharField(max_length=200, verbose_name='标题', blank=True, null=True)
    telnum = models.CharField(max_length=200, verbose_name='联系人电话', blank=True, null=True)
    address = models.CharField(max_length=200, verbose_name='事件地点', blank=True, null=True)
    contents = models.TextField(verbose_name='投诉内容')
    ComplainFirstCategory = models.ForeignKey(ComplainFirstCategory, related_name='CompFirstCate',
                                              on_delete=models.CASCADE, verbose_name='类别', blank=True, null=True)
    ComplainSecondCategory = models.ForeignKey(ComplainSecondCategory, related_name='CompSecondCate',
                                               on_delete=models.CASCADE, verbose_name='类别', blank=True, null=True)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = '投诉'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

admin.py

# Register your models here.
from wagtail.contrib.modeladmin.options import (
    ModelAdmin, modeladmin_register)
# import imageuploadapps
from .models import Image

class ImageAdmin(ModelAdmin):
    model = Image
    menu_label = 'Image'  # ditch this to use verbose_name_plural from model
    menu_icon = 'placeholder'  # change as required
    menu_order = 200  # will put in 3rd place (000 being 1st, 100 2nd)
    add_to_settings_menu = False  # or True to add your model to the Settings sub-menu
    exclude_from_explorer = False # or True to exclude pages of this type from Wagtail's explorer view
    list_display = ('eventID', 'images')
    list_filter = ('eventID',)
    search_fields = ('title', 'images')

# Now you just need to register your customised ModelAdmin class with Wagtail
modeladmin_register(ImageAdmin)

管理平台显示这样:
在这里插入图片描述

更为复杂些的例子

假设你定义了Book, Author, and Genre 模型在models.py.
你的app目录的admins.py 像这样:

from wagtail.contrib.modeladmin.options import (
    ModelAdmin, ModelAdminGroup, modeladmin_register)
from .models import (
    Book, Author, Genre)


class BookAdmin(ModelAdmin):
    model = Book
    menu_label = 'Book'  # ditch this to use verbose_name_plural from model
    menu_icon = 'pilcrow'  # change as required
    list_display = ('title', 'author')
    list_filter = ('genre', 'author')
    search_fields = ('title', 'author')


class AuthorAdmin(ModelAdmin):
    model = Author
    menu_label = 'Author'  # ditch this to use verbose_name_plural from model
    menu_icon = 'user'  # change as required
    list_display = ('first_name', 'last_name')
    list_filter = ('first_name', 'last_name')
    search_fields = ('first_name', 'last_name')


class GenreAdmin(ModelAdmin):
    model = Genre
    menu_label = 'Genre'  # ditch this to use verbose_name_plural from model
    menu_icon = 'group'  # change as required
    list_display = ('name',)       # 单个字段需要后面加逗号。
    list_filter = ('name',)
    search_fields = ('name',)


class LibraryGroup(ModelAdminGroup):       #制作一个菜单组,三个数据模型放在一起
    menu_label = 'Library'
    menu_icon = 'folder-open-inverse'  # change as required
    menu_order = 200  # will put in 3rd place (000 being 1st, 100 2nd)
    items = (BookAdmin, AuthorAdmin, GenreAdmin)

# When using a ModelAdminGroup class to group several ModelAdmin classes together,
# you only need to register the ModelAdminGroup class with Wagtail:
modeladmin_register(LibraryGroup)

在这里插入图片描述

增加Django数据模型的用户档案

新建一个通用的应用common

python manage.py startapp common

models.py

from django.conf import settings
from django.db import models

# Create your models here.
# 用户信息
# @python_2_unicode_compatible
class UserProfile(models.Model):
    """
    用户档案
    """
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='userprofile', )
    mobile_phone = models.CharField(blank=True, null=True, max_length=200, verbose_name='电话号码')
    nickname = models.CharField(blank=True, null=True, max_length=200)
    address = models.CharField(blank=True, null=True, max_length=400)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

admin.py

# Register your models here.
from wagtail.contrib.modeladmin.options import (
    ModelAdmin, ModelAdminGroup, modeladmin_register)
from .models import (
    UserProfile, )


class UserProfileAdmin(ModelAdmin):
    model = UserProfile
    menu_label = '用户档案'  # ditch this to use verbose_name_plural from model
    menu_icon = 'placeholder'  # change as required
    list_display = ('user', 'mobile_phone','nickname','address')
    list_filter = ('user',)
    search_fields = ('user', 'mobile_phone')

# Now you just need to register your customised ModelAdmin class with Wagtail
# modeladmin_register(ImageAdmin)

modeladmin_register(UserProfileAdmin)

在这里插入图片描述

通过接口添加用户档案

通过接口认证获得token方法

流程

前端通过用户名密码访问token获取接口,获取token,存储在本地,以后再访问需要认证的资源,可以带着这个token访问接口,后端即认为是此用户名密码用户访问。
目前常用的为客户端webstorage,服务端token;cookie和session方法不再常用

使用方法

要使用 TokenAuthentication 方案 ,需要在setting包含rest_framework.authtoken应用项;并且配置认证类包含TokenAuthentication。

INSTALLED_APPS = [
    ...
    'rest_framework.authtoken'
]

增加认证类

在这里插入图片描述

当使用 TokenAuthentication, 你可能需要通过提供用户名和密码而获得token这样一个机制。REST framework提供了一个内置的 view 来实现这个功能。只需要添加 obtain_auth_token view 到你的 URLconf就可以使用它。

from rest_framework.authtoken import views
urlpatterns += [
    path('api-token-auth/', views.obtain_auth_token)
]

用户前端访问这个接口,只需要这一行代码即完成token的生成并返回。
Note that the URL part of the pattern can be whatever you want to use.

The obtain_auth_token view will return a JSON response when valid username and password fields are POSTed to the view using form data or JSON:

{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }

配置完成后的数据库迁移

manage.py makemigrations 
manage.py migrate


rest_framework.authtoken
应用提供了数据库迁移。

C:\djproject\eshop>python manage.py makemigrations
Migrations for 'computerapp':
  computerapp\migrations\0002_auto_20210404_1048.py
    - Change Meta options on category
    - Change Meta options on product
    - Alter field name on category
    - Alter field category on product
    - Alter field model on product
    - Alter field price on product

C:\djproject\eshop>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, authtoken, computerapp, contenttypes, sessions
Running migrations:
  Applying computerapp.0002_auto_20210404_1048... OK

在根urls增加

在这里插入图片描述

第一行为引入views
第二行为增加认证功能
第三行为获得token功能,利用了rest自带功能

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值