wagtail的使用

文章目录

  • 安装虚拟环境
    • 新建项目时指定虚拟环境
    • 打开已有项目添加虚拟环境
  • 安装wagtail
    • 查看安装后的包
  • 创建wagtail项目
    • 安装依赖
    • 迁移
    • 创建超级用户
    • 运行项目
  • 管理工作台
  • 内容
  • 扩展首页的数据模型
    • 更新数据库
    • 修改模板页
    • 创建一个页面的过程
  • models中的基本字段
    • templates
    • 字符型
    • 文本字段
    • 富文本字段
    • 图片字段
    • 图片添加cta
    • 文件字段
    • 引入bootstrap
    • 优化模板文档
  • 模板
    • 加载静态文件
    • 引用models元素
  • 常用web组件的实现
    • 带banner的首页
    • 页面主题
    • 轮播图
  • StreamField 字段
    • 使用StreamField
    • 模板呈现
    • 组合块
    • StructBlock
    • 子类化结构块
    • Block icons
    • ListBlock
    • StreamBlock
    • Per-block templates
  • card
  • StreamField实例
  • 一个blog应用
    • 一个简单的起始页
      • 配置修改
      • 管理页面操作
    • blog页
      • 配置修改
      • 父与子
    • 加入图片
      • 添加BlogPageGalleryImage 模型到 models.py
      • 管理页面添加图片
      • 展示
    • 缩略图样式
  • snippets
    • Snippet Models
    • Binding Pages to Snippets
  • 定制管理平台
    • Customising admin templates
  • 把wagtail集成到django项目
    • 安装插件
    • Settings配置
    • URL配置
  • 错误
  • wagtail的api
    • 基本配置
      • 使能app
      • 配置endpoints
      • 注册url
    • 访问api
    • 使字段能够访问
    • 按类型获取
    • 限制获取的字段
    • 限制获取数量
    • 偏移量
    • 排序
    • 获得子页面
    • 获取orderable数据
      • 只查询orderable的数据
    • 获取StreamField 中的image的url
    • 表单
  • Django数据模型的纳管
    • 配置
    • 使用
    • 更为复杂些的例子
  • 增加Django数据模型的用户档案
  • 通过接口添加用户档案
  • 通过接口认证获得token方法
    • 流程
    • 使用方法
    • 配置完成后的数据库迁移
    • 在根urls增加

安装虚拟环境

新建项目时指定虚拟环境

在这里插入图片描述

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

添加虚拟环境
在这里插入图片描述

安装wagtail

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

查看安装后的包


(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项目

wagtail start mysite

安装依赖

pip install -r requirements.txt

迁移

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

创建超级用户

(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.

运行项目

python manage.py runserver

在这里插入图片描述

管理工作台

内容

扩展首页的数据模型

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

from django.db import modelsfrom wagtail.core.models import Page
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanelclass HomePage(Page):body = RichTextField(blank=True)content_panels = Page.content_panels + [FieldPanel('body', classname="full"),]

更新数据库

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

python manage.py makemigrations
python manage.py migrate 

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

在这里插入图片描述

修改模板页

home/templates/home/home_page.html
{% 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规定。
其中的

{% extends "base.html" %}

表示扩展基础模板

{% 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

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

{% 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 modelsfrom 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 ImageChooserPanelclass HomePage(Page):'''HomePage models '''template = 'home/home_page.html'banner_title = models.CharField(max_length=100, blank=True, null=True)# max_count = 1banner_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 modelsfrom 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 ImageChooserBlockclass 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 %}标记:

{% 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 myblocksfrom 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 ImageChooserPanelclass 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:

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:  # noqatemplate = "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 blocksfrom wagtail.core.models import Page
from wagtail.core.fields import RichTextField, StreamField
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, StreamFieldPanelclass 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 %}

一个blog应用

(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

from wagtail.core.models import Page
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanelclass BlogIndexPage(Page):intro = RichTextField(blank=True)content_panels = Page.content_panels + [FieldPanel('intro', classname="full")]

迁移数据
展示模板,新建文件
blog/templates/blog/blog_index_page.html

{% extends "base.html" %}{% load wagtailcore_tags %}{% block body_class %}template-blogindexpage{% endblock %}{% block content %}<h1>{{ page.title }}</h1><div class="intro">{{ page.intro|richtext }}</div>{% for post in page.get_children %}<h2><a href="{% pageurl post %}">{{ post.title }}</a></h2>{{ post.specific.intro }}{{ post.specific.body|richtext }}{% endfor %}{% endblock %}

管理页面操作

在Wagtail的admin界面, 创建一个BlogIndexPage作为Homepage的子叶,确保slug “blog” 在Promote页, 发布. 现在可以访问 url /blog 在你的站点上。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

blog页

配置修改

blog/models.py

from django.db import modelsfrom 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"),]

迁移数据

 python manage.py makemigrationspython manage.py migrate

blog/templates/blog/blog_page.html

{% extends "base.html" %}{% load wagtailcore_tags %}{% block body_class %}template-blogpage{% endblock %}{% block content %}<h1>{{ page.title }}</h1><p class="meta">{{ page.date }}</p><div class="intro">{{ page.intro }}</div>{{ page.body|richtext }}<p><a href="{{ page.get_parent.url }}">Return to blog</a></p>     {% endblock %}

现在创建一些BlogIndexPage的子页
在这里插入图片描述

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

发布它
在这里插入图片描述

父与子

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

加入图片

添加BlogPageGalleryImage 模型到 models.py

blog\models.py

from django.db import models# New imports added for ParentalKey, Orderable, InlinePanel, ImageChooserPanelfrom modelcluster.fields import ParentalKeyfrom 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'),]

迁移数据

 python manage.py makemigrationspython manage.py migrate

blog\templates\blog\blog_page.html


{% 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 modelsfrom 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 modelsfrom wagtail.core.models import Page, Orderable
from wagtail.snippets.edit_handlers import SnippetChooserPanelfrom 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.textclass 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项目

安装插件

$ 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, includefrom wagtail.admin import urls as wagtailadmin_urls
from wagtail.core import urls as wagtail_urls
from wagtail.documents import urls as wagtaildocs_urlsurlpatterns = [...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)? ySuccessfully uninstalled Pillow-8.3.0(venv) C:\djangoprj\hotlinewag>pip install Pillow==8.2.0
Collecting Pillow==8.2.0Using 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.pyINSTALLED_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.pyfrom 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.pyfrom .api import api_routerurlpatterns = [...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.pyfrom wagtail.api import APIFieldclass 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 APIapi_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中加入需要暴露的字段,各种类型字段都可以。

在这里插入图片描述

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

按类型获取

在这里插入图片描述

限制获取的字段

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

限制获取数量

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

偏移量

分页显示,每页开始位置。
在这里插入图片描述

排序

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

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

获得子页面

在这里插入图片描述

获取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 andattribution 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-uploadhttps://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:

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 ImageChooserPanelclass 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 Bookclass BookAdmin(ModelAdmin):model = Bookmenu_label = 'Book'  # ditch this to use verbose_name_plural from modelmenu_icon = 'pilcrow'  # change as requiredmenu_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-menuexclude_from_explorer = False # or True to exclude pages of this type from Wagtail's explorer viewlist_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_namedef __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_namedef __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_namedef __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 Imageclass ImageAdmin(ModelAdmin):model = Imagemenu_label = 'Image'  # ditch this to use verbose_name_plural from modelmenu_icon = 'placeholder'  # change as requiredmenu_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-menuexclude_from_explorer = False # or True to exclude pages of this type from Wagtail's explorer viewlist_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 = Bookmenu_label = 'Book'  # ditch this to use verbose_name_plural from modelmenu_icon = 'pilcrow'  # change as requiredlist_display = ('title', 'author')list_filter = ('genre', 'author')search_fields = ('title', 'author')class AuthorAdmin(ModelAdmin):model = Authormenu_label = 'Author'  # ditch this to use verbose_name_plural from modelmenu_icon = 'user'  # change as requiredlist_display = ('first_name', 'last_name')list_filter = ('first_name', 'last_name')search_fields = ('first_name', 'last_name')class GenreAdmin(ModelAdmin):model = Genremenu_label = 'Genre'  # ditch this to use verbose_name_plural from modelmenu_icon = 'group'  # change as requiredlist_display = ('name',)       # 单个字段需要后面加逗号。list_filter = ('name',)search_fields = ('name',)class LibraryGroup(ModelAdminGroup):       #制作一个菜单组,三个数据模型放在一起menu_label = 'Library'menu_icon = 'folder-open-inverse'  # change as requiredmenu_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 = UserProfilemenu_label = '用户档案'  # ditch this to use verbose_name_plural from modelmenu_icon = 'placeholder'  # change as requiredlist_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%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

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

通过接口认证获得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自带功能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/180448.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[动态规划] (六) 路径问题 LeetCode 63.不同路径II

[动态规划] (六) 路径问题: LeetCode 63.不同路径II 文章目录 [动态规划] (六) 路径问题: LeetCode 63.不同路径II题目解析解题思路状态表示状态转移方程初始化和填表返回值 代码实现总结 63. 不同路径 II 题目解析 (1) 机器人从左上角移动到右下角 (2) 机器人只能向右或者向…

速学数据结构 | 链表实现队列究竟有什么优势?

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! &#x1f4cb; 前言 &#x1f308;hello&#xff01; 各位宝子们大家好啊&#xff0c;栈区的实现我们前面已经讲了&#…

PointNet 论文阅读

论文链接 PointNet Abstract 对于点云问题&#xff0c;由于其格式不规则&#xff0c;大多数研究人员将此类数据转换为规则的 3D 体素网格或图像集合。然而&#xff0c;这会导致数据不必要地庞大并导致问题在本文中&#xff0c;我们设计了一种直接消耗点云的新型神经网络&…

vue二维码生成插件qrcodejs2-fix、html生成图片插件html2canvas、自定义打印内容插件print-js的使用及问题总结

一、二维码生成插件qrcodejs2-fix 1.安装命令 npm i qrcodejs2-fix --save2.页面使用 import { nextTick } from vue; import QRCode from qrcodejs2-fix; nextTick(() > {let codeView document.querySelector("#codeView");codeView.innerHTML ""…

PDF 表单直接保存到您的文档中--TX Text Control

TX Text Control .NET Server for ASP.NET Document Viewer 32.0.2 允许用户保存包含已填写表单字段的文档&#xff0c;从而更轻松地协作和共享信息。 TX Text Control .NET Server for ASP.NET 是一个适用于 ASP.NET 和 ASP.NET Core 的综合服务器端文档处理库。功能包括 PDF …

C++多态基础

文章目录 1.多态概念2.多态使用3.多态析构4.多态隐藏5.多态原理5.1.单类继承5.1.1.问题一&#xff1a;非指针或引用无法调用多态5.1.2.问题二&#xff1a;同类对象共用虚表5.1.3.问题三&#xff1a;子类对象拷贝父类对象虚表5.1.4.问题四&#xff1a;打印虚表地址和虚表内容 5.…

Java8 Stream API全面解析——高效流式编程的秘诀

文章目录 什么是 Stream Api?快速入门流的操作创建流中间操作filter 过滤map 数据转换flatMap 合并流distinct 去重sorted 排序limit 限流skip 跳过peek 操作 终结操作forEach 遍历forEachOrdered 有序遍历count 统计数量min 最小值max 最大值reduce 聚合collect 收集anyMatch…

CorelDRAW2023最新版本号24.5.0.731

CDR2023是一款近年来备受瞩目的工具软件&#xff0c;它提供了数据存储、分析以及处理的能力。但是&#xff0c;对于许多用户来说&#xff0c;CDR2023到底好用不好用还需要进行深入的分析和探讨。在本文中&#xff0c;我们将从多个角度分析CDR2023这款软件。 CorelDRAW2023版win…

springboot--外部环境配置

外部环境配置 前言1、配置优先级配置文件优先级如下&#xff08;后面的覆盖前面的&#xff09;测试 2、外部配置3、导入配置4、属性占位符 前言 场景&#xff1a;线上应用如何快速修改配置&#xff0c;并引用最新配置&#xff1f; springBoot 使用配置优先级外部配置 简化配置…

《网络协议》01. 基本概念

title: 《网络协议》01. 基本概念 date: 2022-08-30 09:50:52 updated: 2023-11-04 07:28:52 categories: 学习记录&#xff1a;网络协议 excerpt: 互联网、网络互连模型&#xff08;OSI&#xff0c;TCP/IP&#xff09;、计算机通信基础。 comments: false tags: top_image: /i…

请求地址‘/operlog‘,发生未知异常

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是全栈工…

vue2和vue3区别

Vue2 和 Vue3 双向绑定方法不同 总结 vue3中没有$set Vue2 和 Vue3 双向绑定方法不同 Vue2 : Object.defineProperty() Vue3 : new Proxy()vue3 实例 数据会更新 const addBtn () >{obj.c 3; } vue2实例 问题&#xff1a;数据更新了视图没更新 Object.defineProperty…

Swift语言配合HTTP写的一个爬虫程序

下段代码使用Embassy库编写一个Swift爬虫程序来爬取jshk的内容。我会使用proxy_host为duoip&#xff0c;proxy_port为8000的爬虫IP服务器。 使用Embassy库编写一个Swift爬虫程序可以实现从网页上抓取数据的功能。下面是一个简单的步骤&#xff1a; 1、首先&#xff0c;需要在X…

【踩坑及思考】浏览器存储 cookie 最大值超过 4kb,或 http 头 cookie 超过限制值

背景 本地生产环境&#xff1a;超过最大值 cookie token 不存储&#xff1b;客户生产环境&#xff1a;打开系统空白&#xff0c;且控制台报 http 400 错误&#xff1b; 出现了两种现象 现象一&#xff1a;浏览器对大于 4kb 的 cookie 值不存储 导致用户名密码登录&#xff…

开发知识点-PHP从小白到拍簧片

从小白到拍簧片 位异或运算&#xff08;^ &#xff09;引用符号(&)strlen() 函数base64_encode预定义 $_POST 变量session_start($array);操作符php 命令set_time_limit(7200)isset()PHP 命名空间(namespace)new 实例化类extends 继承 一个类使用另一个类方法error_reporti…

FreeRTOS_事件标志组

目录 1. 事件标志组简介 2. 创建事件标志组 2.1 函数 xEventGroupCreate() 2.2 函数 xEventGroupCreateStatic() 3. 设置事件位 3.1 函数 xEventGroupClearBits() 3.2 函数 xEventGroupClearBitsFromISR() 3.3 函数 xEventGroupSetBits() 3.4 函数 xEventGroupSetB…

leetcode:387. 字符串中的第一个唯一字符

一、题目 函数原型 int firstUniqChar(char* s) 二、算法 设置一个大小为26的字符数组&#xff0c;位置0 - 25 分别对应字符 a - z 。遍历两次字符串&#xff0c;第一次记录下每个字符出现的次数&#xff0c;第二次检查哪个字符最先遍历到且出现次数为1&#xff0c;返回该字符即…

uniapp新建的vuecli项目启动报错并且打包失败的问题(已解决)

我的项目新建流程如下 运行之后就是如下报错 解决办法&#xff1a; 安装如下依赖&#xff1a; npm i postcss-loader autoprefixer8.0.0 npm run build 编译失败 安装如下依赖&#xff1a; npm install postcss8.2.2 最终package.json文件如下 {"name": "ls…

【Vue】vant上传封装方法,van-uploader上传接口封装

项目场景&#xff1a; 问题描述 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 在移动端项目中&#xff0c;使用vant组件上传&#xff0c;但是vant没有上传方法&#xff0c;需要自己写。 html代码 <van-uploader v-model"fileList" :max-size"50…

jvm实践

说一下JVM中的分代回收 堆的区域划分 1.堆被分为了两份:新生代和老年代[1:2] 2.对于新生代&#xff0c;内部又被分为了三个区域。Eden区&#xff0c;幸存者区survivor(分成from和to)[8:1:1] 对象回收分代回收策略 1.新创建的对象&#xff0c;都会先分配到eden区 2.当伊园内存…