wagtail的使用

文章目录

安装虚拟环境

新建项目时指定虚拟环境

打开已有项目添加虚拟环境

添加虚拟环境

安装wagtail

bash 复制代码
(wagenv) C:\djproject\wagprj>pip list
Package    Version
---------- -------
pip        21.0.1
setuptools 54.2.0
wheel      0.36.2


(wagenv) C:\djproject\wagprj>pip install wagtail
Collecting wagtail
  Downloading wagtail-2.12.3-py3-none-any.whl (11.2 MB)
     |████████████████████████████████| 11.2 MB 84 kB/s
Collecting l18n>=2018.5
  Downloading l18n-2020.6.1.tar.gz (50 kB)
     |████████████████████████████████| 50 kB 93 kB/s
Collecting django-modelcluster<6.0,>=5.1
  Downloading django_modelcluster-5.1-py2.py3-none-any.whl (26 kB)
Collecting Willow<1.5,>=1.4
  Downloading Willow-1.4-py2.py3-none-any.whl (106 kB)
     |████████████████████████████████| 106 kB 111 kB/s
Collecting djangorestframework<4.0,>=3.11.1
  Using cached djangorestframework-3.12.4-py3-none-any.whl (957 kB)
Collecting django-taggit<2.0,>=1.0
  Downloading django_taggit-1.3.0-py3-none-any.whl (45 kB)
     |████████████████████████████████| 45 kB 126 kB/s
Collecting xlsxwriter<2.0,>=1.2.8
  Downloading XlsxWriter-1.3.9-py2.py3-none-any.whl (145 kB)
     |████████████████████████████████| 145 kB 92 kB/s
Collecting anyascii>=0.1.5
  Downloading anyascii-0.2.0-py3-none-any.whl (283 kB)
     |████████████████████████████████| 283 kB 152 kB/s
Collecting beautifulsoup4<4.9,>=4.8
  Downloading beautifulsoup4-4.8.2-py3-none-any.whl (106 kB)
     |████████████████████████████████| 106 kB 139 kB/s
Collecting draftjs-exporter<3.0,>=2.1.5
  Downloading draftjs_exporter-2.1.7-py3-none-any.whl (43 kB)
     |████████████████████████████████| 43 kB 131 kB/s
Collecting html5lib<2,>=0.999
  Using cached html5lib-1.1-py2.py3-none-any.whl (112 kB)
Collecting requests<3.0,>=2.11.1
  Using cached requests-2.25.1-py2.py3-none-any.whl (61 kB)
Collecting Django<3.2,>=2.2
  Using cached Django-3.1.8-py3-none-any.whl (7.8 MB)
Collecting tablib[xls,xlsx]>=0.14.0
  Downloading tablib-3.0.0-py3-none-any.whl (47 kB)
     |████████████████████████████████| 47 kB 156 kB/s
Collecting Pillow<9.0.0,>=4.0.0
  Using cached Pillow-8.2.0-cp38-cp38-win_amd64.whl (2.2 MB)
Collecting django-filter<3.0,>=2.2
  Downloading django_filter-2.4.0-py3-none-any.whl (73 kB)
     |████████████████████████████████| 73 kB 130 kB/s
Collecting django-treebeard!=4.5,<5.0,>=4.2.0
  Using cached django_treebeard-4.5.1-py3-none-any.whl (103 kB)
Collecting soupsieve>=1.2
  Downloading soupsieve-2.2.1-py3-none-any.whl (33 kB)
Collecting pytz
  Using cached pytz-2021.1-py2.py3-none-any.whl (510 kB)
Collecting asgiref<4,>=3.2.10
  Using cached asgiref-3.3.4-py3-none-any.whl (22 kB)
Collecting sqlparse>=0.2.2
  Using cached sqlparse-0.4.1-py3-none-any.whl (42 kB)
Collecting six>=1.9
  Using cached six-1.15.0-py2.py3-none-any.whl (10 kB)
Collecting webencodings
  Using cached webencodings-0.5.1-py2.py3-none-any.whl (11 kB)
Collecting urllib3<1.27,>=1.21.1
  Using cached urllib3-1.26.4-py2.py3-none-any.whl (153 kB)
Collecting idna<3,>=2.5
  Downloading idna-2.10-py2.py3-none-any.whl (58 kB)
     |████████████████████████████████| 58 kB 155 kB/s
Collecting certifi>=2017.4.17
  Downloading certifi-2020.12.5-py2.py3-none-any.whl (147 kB)
     |████████████████████████████████| 147 kB 148 kB/s
Collecting chardet<5,>=3.0.2
  Downloading chardet-4.0.0-py2.py3-none-any.whl (178 kB)
     |████████████████████████████████| 178 kB 198 kB/s
Collecting openpyxl>=2.6.0
  Downloading openpyxl-3.0.7-py2.py3-none-any.whl (243 kB)
     |████████████████████████████████| 243 kB 242 kB/s
Collecting xlrd
  Downloading xlrd-2.0.1-py2.py3-none-any.whl (96 kB)
     |████████████████████████████████| 96 kB 293 kB/s
Collecting xlwt
  Downloading xlwt-1.3.0-py2.py3-none-any.whl (99 kB)
     |████████████████████████████████| 99 kB 293 kB/s
Collecting et-xmlfile
  Downloading et_xmlfile-1.0.1.tar.gz (8.4 kB)
Building wheels for collected packages: l18n, et-xmlfile
  Building wheel for l18n (setup.py) ... done
  Created wheel for l18n: filename=l18n-2020.6.1-py3-none-any.whl size=51577 sha256=4672c5c34b8cdb840a4b10982cc7e669169e4741e0624878a24e92e89380f421
  Stored in directory: c:\users\administrator\appdata\local\pip\cache\wheels\ea\5f\6f\2e7864d49b0f7badda5c1402b11254b62a3eadb949e3fc2ab9
  Building wheel for et-xmlfile (setup.py) ... done
  Created wheel for et-xmlfile: filename=et_xmlfile-1.0.1-py3-none-any.whl size=8913 sha256=65ee864758d0c127daa8ea81099d063653500e1d576fe59126c6aff5f260a29b
  Stored in directory: c:\users\administrator\appdata\local\pip\cache\wheels\6e\df\38\abda47b884e3e25f9f9b6430e5ce44c47670758a50c0c51759
Successfully built l18n et-xmlfile
Installing collected packages: sqlparse, pytz, et-xmlfile, asgiref, xlwt, xlrd, webencodings, urllib3, tablib, soupsieve, six, openpyxl, idna, Django, chardet, certifi, xlsxwriter, Willow, requests, Pillow, l18n, html5lib, draftjs-exporter, djangorestframework, django-treebeard, django-taggit, django-modelcluster, django-filter, beautifulsoup4, anyascii, wagtail
Successfully installed Django-3.1.8 Pillow-8.2.0 Willow-1.4 anyascii-0.2.0 asgiref-3.3.4 beautifulsoup4-4.8.2 certifi-2020.12.5 chardet-4.0.0 django-filter-2.4.0 django-modelcluster-5.1 django-taggit-1.3.0 django-treebeard-4.5.1 djangorestframework-3.12.4 draftjs-exporter-2.1.7 et-xmlfile-1.0.1 html5lib-1.1 idna-2.10 l18n-2020.6.1 openpyxl-3.0.7 pytz-2021.1 requests-2.25.1 six-1.15.0 soupsieve-2.2.1 sqlparse-0.4.1 tablib-3.0.0 urllib3-1.26.4 wagtail-2.12.3 webencodings-0.5.1 xlrd-2.0.1 xlsxwriter-1.3.9 xlwt-1.3.0

查看安装后的包

bash 复制代码
(wagenv) C:\djproject\wagprj>pip list
Package             Version
------------------- ---------
anyascii            0.2.0
asgiref             3.3.4
beautifulsoup4      4.8.2
certifi             2020.12.5
chardet             4.0.0
Django              3.1.8
django-filter       2.4.0
django-modelcluster 5.1
django-taggit       1.3.0
django-treebeard    4.5.1
djangorestframework 3.12.4
draftjs-exporter    2.1.7
et-xmlfile          1.0.1
html5lib            1.1
idna                2.10
l18n                2020.6.1
openpyxl            3.0.7
Pillow              8.2.0
pip                 21.0.1
pytz                2021.1
requests            2.25.1
setuptools          54.2.0
six                 1.15.0
soupsieve           2.2.1
sqlparse            0.4.1
tablib              3.0.0
urllib3             1.26.4
wagtail             2.12.3
webencodings        0.5.1
wheel               0.36.2
Willow              1.4
xlrd                2.0.1
XlsxWriter          1.3.9
xlwt                1.3.0

创建wagtail项目

bash 复制代码
wagtail start mysite

安装依赖

bash 复制代码
pip install -r requirements.txt

迁移

powershell 复制代码
(wagenv) C:\djproject\wagprj\mysite>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, home, sessions, taggit, wagtailadmin, wagtailcore, wagtaildocs, wagtailembeds, wagtailforms, wagtailimages, wagtailredirects, wagtailsearch, wagtailusers
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying wagtailcore.0001_squashed_0016_change_page_url_path_to_text_field... OK
  Applying wagtailcore.0017_change_edit_page_permission_description... OK
  Applying wagtailcore.0018_pagerevision_submitted_for_moderation_index... OK
  Applying wagtailcore.0019_verbose_names_cleanup... OK
  Applying wagtailcore.0020_add_index_on_page_first_published_at... OK
  Applying wagtailcore.0021_capitalizeverbose... OK
  Applying wagtailcore.0022_add_site_name... OK
  Applying wagtailcore.0023_alter_page_revision_on_delete_behaviour... OK
  Applying wagtailcore.0024_collection... OK
  Applying wagtailcore.0025_collection_initial_data... OK
  Applying wagtailcore.0026_group_collection_permission... OK
  Applying wagtailcore.0027_fix_collection_path_collation... OK
  Applying wagtailcore.0024_alter_page_content_type_on_delete_behaviour... OK
  Applying wagtailcore.0028_merge... OK
  Applying wagtailcore.0029_unicode_slugfield_dj19... OK
  Applying wagtailcore.0030_index_on_pagerevision_created_at... OK
  Applying wagtailcore.0031_add_page_view_restriction_types... OK
  Applying wagtailcore.0032_add_bulk_delete_page_permission... OK
  Applying wagtailcore.0033_remove_golive_expiry_help_text... OK
  Applying wagtailcore.0034_page_live_revision... OK
  Applying wagtailcore.0035_page_last_published_at... OK
  Applying wagtailcore.0036_populate_page_last_published_at... OK
  Applying wagtailcore.0037_set_page_owner_editable... OK
  Applying wagtailcore.0038_make_first_published_at_editable... OK
  Applying wagtailcore.0039_collectionviewrestriction... OK
  Applying wagtailcore.0040_page_draft_title... OK
  Applying home.0001_initial... OK
  Applying home.0002_create_homepage... OK
  Applying sessions.0001_initial... OK
  Applying taggit.0001_initial... OK
  Applying taggit.0002_auto_20150616_2121... OK
  Applying taggit.0003_taggeditem_add_unique_index... OK
  Applying wagtailadmin.0001_create_admin_access_permissions... OK
  Applying wagtailadmin.0002_admin... OK
  Applying wagtailadmin.0003_admin_managed... OK
  Applying wagtailcore.0041_group_collection_permissions_verbose_name_plural... OK
  Applying wagtailcore.0042_index_on_pagerevision_approved_go_live_at... OK
  Applying wagtailcore.0043_lock_fields... OK
  Applying wagtailcore.0044_add_unlock_grouppagepermission... OK
  Applying wagtailcore.0045_assign_unlock_grouppagepermission... OK
  Applying wagtailcore.0046_site_name_remove_null... OK
  Applying wagtailcore.0047_add_workflow_models... OK
  Applying wagtailcore.0048_add_default_workflows... OK
  Applying wagtailcore.0049_taskstate_finished_by... OK
  Applying wagtailcore.0050_workflow_rejected_to_needs_changes... OK
  Applying wagtailcore.0051_taskstate_comment... OK
  Applying wagtailcore.0052_pagelogentry... OK
  Applying wagtailcore.0053_locale_model... OK
  Applying wagtailcore.0054_initial_locale... OK
  Applying wagtailcore.0055_page_locale_fields... OK
  Applying wagtailcore.0056_page_locale_fields_populate... OK
  Applying wagtailcore.0057_page_locale_fields_notnull... OK
  Applying wagtailcore.0058_page_alias_of... OK
  Applying wagtailcore.0059_apply_collection_ordering... OK
  Applying wagtailcore.0060_fix_workflow_unique_constraint... OK
  Applying wagtaildocs.0001_initial... OK
  Applying wagtaildocs.0002_initial_data... OK
  Applying wagtaildocs.0003_add_verbose_names... OK
  Applying wagtaildocs.0004_capitalizeverbose... OK
  Applying wagtaildocs.0005_document_collection... OK
  Applying wagtaildocs.0006_copy_document_permissions_to_collections... OK
  Applying wagtaildocs.0005_alter_uploaded_by_user_on_delete_action... OK
  Applying wagtaildocs.0007_merge... OK
  Applying wagtaildocs.0008_document_file_size... OK
  Applying wagtaildocs.0009_document_verbose_name_plural... OK
  Applying wagtaildocs.0010_document_file_hash... OK
  Applying wagtaildocs.0011_add_choose_permissions... OK
  Applying wagtaildocs.0012_uploadeddocument... OK
  Applying wagtailembeds.0001_initial... OK
  Applying wagtailembeds.0002_add_verbose_names... OK
  Applying wagtailembeds.0003_capitalizeverbose... OK
  Applying wagtailembeds.0004_embed_verbose_name_plural... OK
  Applying wagtailembeds.0005_specify_thumbnail_url_max_length... OK
  Applying wagtailembeds.0006_add_embed_hash... OK
  Applying wagtailembeds.0007_populate_hash... OK
  Applying wagtailembeds.0008_allow_long_urls... OK
  Applying wagtailforms.0001_initial... OK
  Applying wagtailforms.0002_add_verbose_names... OK
  Applying wagtailforms.0003_capitalizeverbose... OK
  Applying wagtailforms.0004_add_verbose_name_plural... OK
  Applying wagtailimages.0001_squashed_0021... OK
  Applying wagtailimages.0022_uploadedimage... OK
  Applying wagtailimages.0023_add_choose_permissions... OK
  Applying wagtailredirects.0001_initial... OK
  Applying wagtailredirects.0002_add_verbose_names... OK
  Applying wagtailredirects.0003_make_site_field_editable... OK
  Applying wagtailredirects.0004_set_unique_on_path_and_site... OK
  Applying wagtailredirects.0005_capitalizeverbose... OK
  Applying wagtailredirects.0006_redirect_increase_max_length... OK
  Applying wagtailsearch.0001_initial... OK
  Applying wagtailsearch.0002_add_verbose_names... OK
  Applying wagtailsearch.0003_remove_editors_pick... OK
  Applying wagtailsearch.0004_querydailyhits_verbose_name_plural... OK
  Applying wagtailusers.0001_initial... OK
  Applying wagtailusers.0002_add_verbose_name_on_userprofile... OK
  Applying wagtailusers.0003_add_verbose_names... OK
  Applying wagtailusers.0004_capitalizeverbose... OK
  Applying wagtailusers.0005_make_related_name_wagtail_specific... OK
  Applying wagtailusers.0006_userprofile_prefered_language... OK
  Applying wagtailusers.0007_userprofile_current_time_zone... OK
  Applying wagtailusers.0008_userprofile_avatar... OK
  Applying wagtailusers.0009_userprofile_verbose_name_plural... OK

创建超级用户

powershell 复制代码
(wagenv) C:\djproject\wagprj\mysite>python manage.py createsuperuser
Username (leave blank to use 'administrator'): admin
Email address: admin@sina.com
Password:123
Password (again):
Superuser created successfully.

运行项目

powershell 复制代码
python manage.py runserver

管理工作台

内容

扩展首页的数据模型

编辑 home/models.py 添加一个 body 字段到数据模型:

powershell 复制代码
from django.db import models

from wagtail.core.models import Page
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel


class HomePage(Page):
    body = RichTextField(blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('body', classname="full"),
    ]

更新数据库

每次修改完models都要运行以下命令更新数据库。

powershell 复制代码
python manage.py makemigrations
python manage.py migrate 

pages-》home-》编辑,多了一个body

修改模板页

powershell 复制代码
home/templates/home/home_page.html
powershell 复制代码
{% extends "base.html" %}

{% load wagtailcore_tags %}

{% block body_class %}template-homepage{% endblock %}

{% block content %}
    {{ page.body|richtext }}
{% endblock %}

运行项目,首页展示为

创建一个页面的过程

要访问models中HomePage类,需要在home/templates/home/中创建一个home_page.html的对应模板来渲染这个类。这个命名是wagtail规定。

其中的

bash 复制代码
{% extends "base.html" %}

表示扩展基础模板

bash 复制代码
{% include 'home/welcome_page.html' %}

包含欢迎页面

删除此页面后,首页就空白了。

在models中添加另外一个类Article,表示另外一个页面。

做下数据库迁移

选择页面根

添加子页面

出现Article类代表的页面类型。可以以这种类型来创建页面。

Each Wagtail page type is a Django model, represented in the database as a separate table.

说明每个Wagtail的页面类型就是Django的数据模型,在数据库中代表一个表。

以这个类型创建一个article1的页

发布一下

创建一个站点


但是还不能浏览,没有模板文件。

创建一个模板文件,位置一定在models对应的文件夹中的templates/home/下,名字必须和类名相同,小写。

重新浏览

models中的基本字段

templates

powershell 复制代码
template = 'home/home_page.html'

指定模板文件的位置,如果不指定,则模板文件位于本app的models同级templates文件夹下的app文件名下的与class同名的html中

字符型

页面中展示的内容都是models中类的一个字段,在homepage添加一个subtile字段

并把它展示在页面中,如下,使用content_panels 和 FieldPanel关键字

迁移下数据

打开首页编辑

目前首页是没有内容的,因为对应的渲染模板页面还没有处理。

在模板中,所有的变量被封存到了page对象中。

{{ }}用来显示变量。

文本字段

在models中增加一个问本字段

数据迁移下

刷新管理页面

多了一个content1字段

添加内容

模板页

富文本字段

添加一个富文本字段

迁移数据

发布出去

模板增加标签加载和富文本的过滤器

刷新页面

图片字段

图片的处理

迁移数据,刷新管理页面

渲染模板增加标签加载和指定图片加载尺寸

图片添加cta

文件字段

文件处理

迁移数据刷新管理页面


引入bootstrap



优化模板文档

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

mysite\templates\base.html

powershell 复制代码
{% 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>

定义一个内容块

powershell 复制代码
{% block content %}
{% endblock %}

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

home\templates\home\home_page.html

powershell 复制代码
{% 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中引入公共部分的导航条,

powershell 复制代码
{% 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

powershell 复制代码
{# 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中定义数据模型

powershell 复制代码
# 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是一个模型字段,可以像任何其他字段一样在页面模型中定义:

powershell 复制代码
from django.db import models

from wagtail.core.models import Page
from wagtail.core.fields import StreamField
from wagtail.core import blocks
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
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="full title")),    //标题
        ('paragraph', blocks.RichTextBlock()),     //段落
        ('image', ImageChooserBlock()),             //图像
    ])

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

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

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

模板呈现

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

powershell 复制代码
{% load wagtailcore_tags %}

 ...

{% include_block page.body %}

实际中使用

mysite\templates\flex\flex_page.html

powershell 复制代码
{% 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

powershell 复制代码
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'

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

powershell 复制代码
{% 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:

powershell 复制代码
 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的对象,其键与定义中给出的块名相对应:

powershell 复制代码
<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"块可以重写为:

powershell 复制代码
class PersonBlock(blocks.StructBlock):
    first_name = blocks.CharBlock()
    surname = blocks.CharBlock()
    photo = ImageChooserBlock(required=False)
    biography = blocks.RichTextBlock()

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

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

Block icons

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

powershell 复制代码
 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'

powershell 复制代码
 class PersonBlock(blocks.StructBlock):
     first_name = blocks.CharBlock()
     surname = blocks.CharBlock()
     photo = ImageChooserBlock(required=False)
     biography = blocks.RichTextBlock()

     class Meta:
         icon = 'user'

ListBlock

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

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

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

powershell 复制代码
<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本身相同的机制以任何顺序混合和重复。例如,支持图像和视频幻灯片的轮播图可以定义如下:

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

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

powershell 复制代码
class PersonBlock(blocks.StreamBlock):
    image = ImageChooserBlock()
    video = EmbedBlock()

    class Meta:
        icon = 'image'

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

powershell 复制代码
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本身的顶级值一样。

powershell 复制代码
<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派生的自定义块类型特别有用:
powershell 复制代码
('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:

或者,当定义为结构块的子类时

powershell 复制代码
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'

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

powershell 复制代码
{% load wagtailimages_tags %}

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

powershell 复制代码
{% 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:

card

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

myblocks.py增加以下内容

powershell 复制代码
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增加以下内容

powershell 复制代码
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

powershell 复制代码
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

bash 复制代码
(env) C:\djproject\wagprj\mysite>django-admin startapp article

注册到配置文件base.py

article\models.py

bash 复制代码
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

内容替换如下

bash 复制代码
{% extends 'base.html' %}

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

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

{% endblock %}

完善一下,增加单独处理标签的能力

bash 复制代码
{% 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 %}

一个blog应用

powershell 复制代码
(env) C:\djproject\wagprj\mysite>python manage.py startapp blog

Add the new blog app to INSTALLED_APPS in mysite/settings/base.py.

一个简单的起始页

配置修改

blog/models.py

powershell 复制代码
from wagtail.core.models import Page
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel


class BlogIndexPage(Page):
    intro = RichTextField(blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('intro', classname="full")
    ]

迁移数据

展示模板,新建文件

blog/templates/blog/blog_index_page.html

powershell 复制代码
{% 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 %}

管理页面操作

在Wagtail的admin界面, 创建一个BlogIndexPage作为Homepage的子叶,确保slug "blog" 在Promote页, 发布. 现在可以访问 url /blog 在你的站点上。

blog页

配置修改

blog/models.py

bash 复制代码
from django.db import models

from wagtail.core.models import Page
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel
from 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"),
    ]

迁移数据

bash 复制代码
 python manage.py makemigrations
 python manage.py migrate

blog/templates/blog/blog_page.html

bash 复制代码
{% 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 %}

现在创建一些BlogIndexPage的子页


发布它

父与子

在wagtail所作的工作,大部分围绕树的概念,其包含节点和叶子;在这个例子中 BlogIndexPage 是 "node" 单独一个 BlogPage 是"leaves".

加入图片

添加BlogPageGalleryImage 模型到 models.py

blog\models.py

bash 复制代码
from django.db import models

# New imports added for ParentalKey, Orderable, InlinePanel, ImageChooserPanel

from modelcluster.fields import ParentalKey

from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel
from wagtail.images.edit_handlers import ImageChooserPanel
from 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'),
    ]

迁移数据

bash 复制代码
 python manage.py makemigrations
 python manage.py migrate

blog\templates\blog\blog_page.html

powershell 复制代码
{% 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 %}

管理页面添加图片

展示

缩略图样式

既然 gallery images 独立的存在云数据库中, 我们可以独立低使用它,定义一个方法main_image 让它返回查询gallery images的第一个值。

在模板中使用。

给第二和第三帖子各增加gallery images

snippets

Snippet Models

snippets是一小段内容,而不是一个完整的web页面。一般被用来作为第二种内容展示,如,文章头部,注脚,侧部栏目,在管理后台可以进行编辑。snippets是一个Django 数据模型,没有继承Page类,因此没有组织到wagtail的目录树中。但是,他们仍然可以被编辑,通过访问后台面板并且在代码中以register_snippet 作为装饰。

snippets 缺乏很多pages的特性,比如,wagtail管理后台可排序,有一个确定的url,使用时候需要根据你的内容来权衡是否使用。

这个定义好之后,通过数据迁移,即可在wagtail管理后台看到对应的菜单

官方的帮助文档是按下面方式处理的

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 uses the basic 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.

Binding Pages to Snippets

即在页面中引用这个片段。

In the above example, the list of adverts is a fixed list that is displayed via the custom template tag independent of any other content on the page. This might be what you want for a common panel in a sidebar, but, in another scenario, you might wish to display just one specific instance of a snippet on a particular page. This can be accomplished by defining a foreign key to the snippet model within your page model and adding a SnippetChooserPanel to the page's content_panels list. For example, if you wanted to display a specific advert on a BookPage instance:

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'),
        # ...
    ]

The snippet could then be accessed within your template as page.advert.

To attach multiple adverts to a page, the SnippetChooserPanel can be placed on an inline child object of BookPage rather than on BookPage itself. Here, this child model is named BookPageAdvertPlacement (so called because there is one such object for each time that an advert is placed on a 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 %}

定制管理平台

Customising admin templates

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.core',
    'wagtail.admin',

    # ...
)

把wagtail集成到django项目

安装插件

powershell 复制代码
$ pip install wagtail

安装后的内容

powershell 复制代码
(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:

powershell 复制代码
'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:

powershell 复制代码
'wagtail.contrib.redirects.middleware.RedirectMiddleware',

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

powershell 复制代码
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

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

powershell 复制代码
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:

powershell 复制代码
WAGTAIL_SITE_NAME = 'My Example Site'

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

URL配置

powershell 复制代码
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应用

bash 复制代码
# settings.py

INSTALLED_APPS = [
    ...

    'wagtail.api.v2',

    ...
]

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

配置endpoints

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

三种endpoint

bash 复制代码
Pages wagtail.api.v2.views.PagesAPIViewSet
Images wagtail.images.api.v2.views.ImagesAPIViewSet
Documents wagtail.documents.api.v2.views.DocumentsAPIViewSet

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

bash 复制代码
 wagtail.api.v2.views.BaseAPIViewSet

在项目目录新建一个文件api.py

bash 复制代码
# 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。

bash 复制代码
# 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属性来实现。

bash 复制代码
# 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增加导入,

bash 复制代码
from wagtail.api import APIField

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

图片的详细链接

按类型获取

限制获取的字段

可以获取指定的字段,包含在嵌套中的字段。

限制获取数量

分页使用,每页显示数量。

偏移量

分页显示,每页开始位置。

排序

字母顺序

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

获得子页面

获取orderable数据

把需要暴露的orderable添加到APIfield

在orderable中添加需要暴露的字段到APIfiled

只查询orderable的数据

bash 复制代码
http://192.168.2.21/api/v2/pages/3/?fields=_,mycarousel

获取StreamField 中的image的url

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

page中引用的StreamField 如下

bash 复制代码
from streams.blocks import BaseStreamBlock
。。。

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

blocks中的BaseStreamBlock

bash 复制代码
# 原有的导入
# 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,可以灵活定义图片的大小。

返回结果

表单

bash 复制代码
https://stackoverflow.com/questions/61289214/wagtail-form-file-upload

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

Django数据模型的纳管

Wagtail 的数据模型管理类和Django的是不同的,虽然有相同的名字和实现相同的功能,但还是有些不同。添加和编辑表单仍然通过panels和edit_handlers来实现。

在Wagtail中控制那些字段显示或可编辑在数据模型中,以及如何分组和排序,不管你的数据模型是页类型或片段或是标准的django模型。 Wagtail's ModelAdmin 类更多的关心列表配置。例如,列表显示,列表过滤,搜索字段类似与Django,同时字段,字段设置,排除等其他属性是wagtail不支持的。

配置

添加 wagtail.contrib.modeladmin to your INSTALLED_APPS:

bash 复制代码
INSTALLED_APPS = [
   ...
   'wagtail.contrib.modeladmin',
]

使用

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

models.py looks like this:

bash 复制代码
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文件。在其中录入以下内容。

bash 复制代码
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

bash 复制代码
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

bash 复制代码
# 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 像这样:

bash 复制代码
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

bash 复制代码
python manage.py startapp common

models.py

bash 复制代码
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

bash 复制代码
# 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)

通过接口添加用户档案

https://blog.csdn.net/qq_37975685/article/details/81867334

https://blog.csdn.net/weixin_42134789/article/details/80207322?utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-1.control\&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-1.control

https://www.cnblogs.com/lvye001/p/9967421.html

通过接口认证获得token方法

流程

前端通过用户名密码访问token获取接口,获取token,存储在本地,以后再访问需要认证的资源,可以带着这个token访问接口,后端即认为是此用户名密码用户访问。

目前常用的为客户端webstorage,服务端token;cookie和session方法不再常用

使用方法

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

bash 复制代码
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' }

配置完成后的数据库迁移

bash 复制代码
manage.py makemigrations 
manage.py migrate

rest_framework.authtoken

应用提供了数据库迁移。

bash 复制代码
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
bash 复制代码
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自带功能

相关推荐
Null箘1 分钟前
从零创建一个 Django 项目
后端·python·django
搬码后生仔2 分钟前
SQLite 是一个轻量级的嵌入式数据库,不需要安装服务器,直接使用文件即可。
数据库·sqlite
码农君莫笑4 分钟前
Blazor项目中使用EF读写 SQLite 数据库
linux·数据库·sqlite·c#·.netcore·人机交互·visual studio
云空5 分钟前
《解锁 Python 数据挖掘的奥秘》
开发语言·python·数据挖掘
江上挽风&sty5 分钟前
【Django篇】--动手实践Django基础知识
数据库·django·sqlite
玖年37 分钟前
Python re模块 用法详解 学习py正则表达式看这一篇就够了 超详细
python
岑梓铭41 分钟前
(CentOs系统虚拟机)Standalone模式下安装部署“基于Python编写”的Spark框架
linux·python·spark·centos
游客5201 小时前
opencv中的各种滤波器简介
图像处理·人工智能·python·opencv·计算机视觉
Eric.Lee20211 小时前
moviepy将图片序列制作成视频并加载字幕 - python 实现
开发语言·python·音视频·moviepy·字幕视频合成·图像制作为视频
Dontla1 小时前
vscode怎么设置anaconda python解释器(anaconda解释器、vscode解释器)
ide·vscode·python