Welcome to DeerU’s documentation!¶
Deeru¶
DeerU是一个开源博客系统,它基于django开发

- github : https://github.com/gojuukaze/DeerU
- docs : https://deeru.readthedocs.io
- demo : https://www.ikaze.cn
依赖¶
python 3+
django 2.0+
安装¶
安装前先确保你已经安装了以下程序:
- Python 3.5+ – 安装教程 https://www.ikaze.cn/article/28
- pip 10+
- git
- libjpeg,zlib – pillow包的依赖
- ubuntu:
apt-get install libjpeg8-dev zlib1g-dev libfreetype6-dev
- centos:
yum -y install python-devel zlib-devel libjpeg-turbo-devel
- ubuntu:
另外安装之前建议配置虚拟环境
pip3 install virtualenv
virtualenv --no-site-packages deeru_env
source deeru_env/bin/activate
# in windows, run this:
# deeru_env/Scripts/activate
使用pip安装¶
pip install deeru
deeru-admin install DeerU
从git仓库安装¶
git clone -b dev https://github.com/gojuukaze/DeerU.git
cd DeerU
pip install -r requirements.txt
手动创建 deeru/urls_local.py
文件,内容如下:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('app.urls')),
]
手动创建 deeru/settings_local.py
文件,内容如下:
DEBUG = True
ALLOWED_HOSTS = ['*']
CUSTOM_EXPRESSION = []
CUSTOM_APPS = [
]
升级¶
你可以用升级命令进行升级,然后重启DeerU项目:
python manage.py upgrade
upgrade命令参考: 升级命令
DeerU采用git仓库进行升级,因此改动源码可能导致升级失败,项目中 deeru/settings_local.py
, deeru/urls_local.py
可以任意修改
使用指南¶
快速入门¶
你阅读以下文档,帮你快速熟悉DeerU,部署你的博客
完成了第一步,现在你可以先试试你的博客了,不过你可能会发现一些问题,为什么我的文章作者是gojuukaze? 如何修改博客标题? 下面让我们开始第二步。
现在你已经学会如何自定义你的博客了,接下来就把你的博客部署放到网上吧
第三步 : 部署DeerU | 使用Gunicorn部署项目
- 该如何部署DeerU呢?来,看这里 部署DeerU
- 完成基本的设置,静态文件部署后,让我们用Gunicorn运行你的项目吧 !使用Gunicorn部署项目 。 什么Oo0?你不想用Gunicorn! 没关系,这一步你可以自由发挥
运行DeerU¶
安装完成后下面我们测试一下DeerU是否能正常运行
初始化¶
- 运行下面命令初始化项目,注意:如果你更改了数据库的配置,或者修改了主题的静态文件 则需要再次运行初始化
cd DeerU # 如果你没进入工程目录先进入
python manage.py init_deeru
2. 在 deeru/urls_local.py
中修改后台管理的url,
这一步可以跳过,但使用默认url会把你的登录界面暴露在网络中,造成一些安全隐患
urlpatterns = [
path('admin123/', admin.site.urls),
]
debug模式运行¶
- 在正式部署前,先用debug模式运行看看
python manage.py runserver 0.0.0.0:8000
警告
不要生产环境中使用 python manage.py runserver
运行项目,这是不安全的。
在生产环境中部署参考 部署DeerU
如果一切正常你可以打开浏览器访问 http://127.0.0.1:8000 ,正常情况下你将看到如下页面。
如果你使用的是服务器ip访问,某些服务商默认的防火墙规则里可能不允许8000端口,你需要修改一下

配置¶
DeerU的所有配置都采用json格式文件进行配置,你可以在 http://127.0.0.1:8000/admin/app/config/ 中查看修改配置
注解
需要注意的是配置文件中bool值采用0,1表示,0-flase, 1-true
如图:

你一共有3大部分可以进行配置:
全局变量配置¶
这里配置的是全局变量,比如:站点名称、作者名称等
title¶
- 是否必须 : 是
- 值类型 : 字符串
html <head>中的title
blog_name¶
- 是否必须 : 是
- 值类型 : 字符串
站点名称
nickname¶
- 是否必须 : 是
- 值类型 : 字符串
文章作者及站点所有者名称
通用配置¶
配置一些其他的东西,主题、插件的一些专业配置也会放到里面
theme¶
- 是否必须 : 是
- 值类型 : 字符串
- 默认值 : base_theme
使用的主题
baidu_auto_push¶
- 是否必须 : 是
- 值类型 : bool
- 默认值 : 0
base_theme的配置,是否自动推送url到百度 (使用的是百度的自动推送脚本,不过这个脚本貌似已经好久没维护,不能正常运行了)
ui配置¶
DeerU把界面分为5大块,如图

DeerU为 顶部导航栏 、顶部图标栏 这两部分提供了一种通用的配置方式,不过并不强制要求主题一定要使用这两个配置。 有的主题可能会有自己的配置
顶部导航栏¶
顶部导航栏
的配置是一个list,每部分是一个dict,它的结构如下
[ { 'url': '/', 'name': '首页', 'img': { 'type': 'fa', 'class': 'fas fa-home ', 'attrs': {} } }, { 'name': '折叠菜单', 'img': { 'type': 'fa', 'class': 'fas fa-list ', 'attrs': {} }, 'children': [ { 'name': '默认分类', 'url': '/category/1' }, { 'line': 'line' }, { 'name': 'DeerU', 'url': '/tag/1' } ] } ]list中item的结构:
从上面的例子中可以看出item一共有两种结构
第一种结构:
- name ( str | 必须项 ) : 显示的名字
- url ( str | 必须项 ) : 跳转链接
- img ( dict ) : 图片,值是一种特殊的图片类型,默认的图片类型有3种type,详细在 图片类型 中查看
- children ( list ) : 子目录 ,(子目录中可以包含子目录,但你使用的主题不一定支持)
第二种结构:
- line : 分割线,只能在children中
顶部图标栏¶
顶部图标栏分为左右两块,结构如下
{
'left': {
'logo': {
'type': 'img',
'src': '/media/logo_white.png',
'attrs': {}
},
'blog_name': {
'text': ' 文字标题 ',
'attrs': {
'style': 'font-size:18px'
}
}
},
'right': [
{
'url': 'https://github.com/gojuukaze/DeerU',
'img': {
'type': 'fa',
'class': 'fab fa-github',
'attrs': {
'style': 'color:#ffffff;font-size:24px'
}
}
},
]
}
left ( dict | 必须项 ) :
左边部分,内容可为空,其结构为:
- logo ( dict ) : logo图片,值是为图片类型
- blog_name ( dict ) : 文本标题,值是为文本类型,文本类型说明见 文本类型
right ( list | 必须项 ) :
右边部分,内容可为空,每个item是一个dict,结构为:
- img ( dict ) : 参照前面的img
- url ( str ) : url
注解
如果你看了初始化的配置会发现其中有一些特殊的表达式,
比如这个 {% fa|fas fa-home %}
表达式将返回生成一个type为fa的图片,
表达式分为 全局变量表达式 {{}}
和 代码表达式 {% %}
全局变量表达式返回全局变量中配置的值,代码表达式返回str或dict 等
所有的配置项都可以替换为表达式,不过值为str的只能用返回str的表达式替换
表达式的使用你可以在表达式章节中查看: 表达式
配置的值类型¶
DeerU规定了两种配置的值类型,并为每种类型都提供了返回对应结构的表达式
图片类型¶
图片类型有三个type:
- img : 对应img标签
- svg : 对应svg标签
- fa : 对应fontawesome的图标
结构:
{ "type":'img', "src":'/media/logo_white.png', "attrs":{ "style":'xx', "hight":'xx', } }
- src : 图片地址
- attrs : 其他属性
结构:
{ "type":'svg', "svg":'xxx', "attrs":{ "style":'xx', "hight":'xx', } }
- svg : svg图片标签
- attrs : 其他属性
fa类型使用的是 fontawesome 图标, base_theme使用的是fontawesome5版本,你可以在其官网中获取需要的图片,其他主题使用的版本参照主题说明
结构:
{ "type":'fa', "class_":'fa xx', "attrs":{ "style":'xx', "hight":'xx', } }
- class_ : fontawesome图标<i>标签class的值,如这个图标 address-book ,其内容就是
fas fa-address-book
- attrs : 其他属性
表达式¶
表达式可以帮你更快的配置界面,表达式分为:
全局变量表达式 {{ }}
,返回全局变量中的配置
代码表达式 {% %}
,返回一个字符串或字典 等,代码表达式第一个参数以’|’分割
警告
代码表达式第一个参数是表达式名,一定是 小写
例子:
{{ title }} 返回全局变量中title 的值
{% fa|fas fa-home %} 返回type为fa的图片
{% cat|name=默认分类|url%} 返回name=默认分类 的分类的url
内置代码表达式¶
你可以在 app/deeru_expression/expression.py
中找到内置代码表达式
img¶
-
class
Img
¶ 图片表达式,返回一个”type”为’img’的图片字典
- get_result():
返回一个图片字典
{ "type":'img', "src":'xxx', "attrs":{ "style":'xx', } }
- help:
{% img| src/id/name = xx [|其他属性] %}
- src/id/name: 必须项 src或id或图片名,若为id,name将从上传的图片中查找
- 其他属性: 可选,图片的属性
- 例子:
{% img|src= xx %} {% img|id= 1 %} --> 匹配 id {% img|name= xx %} --> 匹配 name.startswith('xx') {% img|id=xx | style= height: 100px; width: 120px | alt= 图片 %}
fa¶
-
class
Fa
¶ fontawesome 图标表达式,返回一个”type”为’fa’的图片字典, base_theme使用的是fontawesome5版本,你可以在其官网中获取需要的图片,其他主题使用的版本参照主题说明
- get_result():
返回一个图片字典
{ "type":'fa', "class_":'xxx', "attrs":{ "style":'xx', } }
- help:
{% fa| xx [|其他属性] %}
- 第1个参数: 必须项 fontawesome图标<i>标签class的值, 如这个图标 address-book 第二个参数就是 ‘fas fa-address-book’
- 其他属性: 可选,其他属性
- 例子:
{% fa|fas fa-address-book %} {% fa|fas fa-address-book | style= color:red;font-size:16px; %}
svg¶
-
class
Svg
¶ svg图片表达式,返回一个”type”为’svg’的图片字典
- get_result():
返回一个图片字典
{ "type":'fa', "svg":'xxx', "attrs":{ "style":'xx', } }
- help:
{% svg| <svg>...</svg> [|其他属性] %}
- 第1个参数: 必须项 svg图片
- 其他属性: 可选,其他属性
- 例子:
{% svg| <svg width="100%" height="100%" version="1.1"xmlns="http://www.w3.org/2000/svg"><path d="M250 150 L150 350 L350 350 Z" /></svg> %}
cat¶
-
class
Cat
¶ 分类表达式,返回分类的url或名字
- get_result():
- 根据第2个参数,返回url,或名字
- help:
{% cat| id_or_name | 返回值 name/url %}
- id_or_name: 必须项 id或分类名,若不指定id还是name,优先匹配id
- name/url: 必须项 指定返回值
- 例子:
{% cat| xx | name %} --> 匹配 id=xx 或 name.startswith(xx) 返回name {% cat| name = xx | name %} --> 匹配name.startswith(xx) 返回name {% cat| id = xx | url %} --> 匹配id=xx 返回url
tag¶
-
class
Tag
¶ 标签表达式,返回标签的url或名字
- get_result():
- 根据第2个参数,返回url,或名字
- help:
{% tag| id_or_name | 返回值 name/url %}
- id_or_name: 必须项 id或标签名,若不指定id还是name,优先匹配id
- name/url: 必须项 指定返回值
- 例子:
{% tag| xx | name %} --> 匹配 id=xx 或 name.startswith(xx) 返回name {% tag| name = xx | name %} --> 匹配name.startswith(xx) 返回name {% tag| id = xx | url %} --> 匹配id=xx 返回url
部署DeerU¶
部署DeerU和部署Django项目一样,你可以自选查阅网上的Django部署文档。 这里提供一个部署方法。
部署一共有3步:
修改settings¶
把 derru/settings_local.py
中的 DEBUG
改为 False
,ALLOWED_HOSTS
改为你的ip或域名
DEBUG = False
ALLOWED_HOSTS = ['www.xxx.com','111.xx.xx.xx']
部署静态、媒体文件¶
django 非debug模式下并不会返回静态、媒体文件,你可以用下面两个方法部署他们文件:
使用nginx/apache 代理,这里给出nginx的配置示例:
location ~ ^/(static|media)/ { root /home/xxx/project/DeerU; # 静态文件返回需要增加跨域头,以便支持http访问https add_header Access-Control-Allow-Origin *; expires 864000; }
注解
如果你没修改过静态文件,媒体文件配置,
则默认的静态文件url是
/static/
,保存在工程目录下的static/
文件夹,默认的媒体文件url是
/media/
,保存在工程目录下的media/
文件夹,关于静态文件,媒体文件配置参考Setting中的 STATIC_URL , MEDIA_URL
你也可以选择把静态、媒体文件上传到七牛或其他cdn服务商,然后修改
STATIC_URL
,MEDIA_URL
为对应的url
注解
什么是静态文件、媒体文件?
静态文件 : 前端的js、css等文件
媒体文件 : 你上传的图片、视频、音频文件
部署项目¶
你可以使用下面三种方法部署项目:
django官方推荐使用Apache + mod_wsgi的方式部署,因为个人喜好的原因这里介绍的是使用Gunicorn部署的方法,详见:使用Gunicorn部署项目
使用Gunicorn部署项目¶
Gunicorn 是一个由纯Python实现的UNIX类操作系统平台下WSGI服务,它非常容易部署和使用,而且没有别的依赖
我使用的是nginx+gunicorn进行部署,如果你愿意,你用可以只使用Gunicorn
另外,这个方法只适用于linux系统下
安装Gunicorn¶
pip install gunicorn
运行gunicorn¶
你可以单纯使用命令 ``nohup gunicorn deeru.wsgi & `` 运行,但这样一旦gunicorn意外停止,你的网站就无法访问。 官方介绍了Gaffer、Runit、Supervisor等多种工具帮助你以守护进程的方式运行,详见:http://docs.gunicorn.org/en/stable/deploy.html#monitoring
这里使用的是 Systemd 的方式运行
新建
/etc/systemd/system/gunicorn.service
:[Unit] Description=gunicorn daemon Requires=gunicorn.socket After=network.target [Service] PIDFile=/run/gunicorn/pid # 改为你自己的用户 User=someuser Group=someuser RuntimeDirectory=gunicorn # DeerU路径 WorkingDirectory=/home/xxx/DeerU # 如果是用了虚拟环境,需要用虚拟环境中gunicorn的绝对路径 '/home/xx/deeru_env/bin/gunicorn' # workers单核cpu建议不超过2 # 其他参数参照gunicorn文档 # 我socket来进行nginx与gunicorn之间的的通信,你也可以改为tcp方式--bind 127.0.0.1:9001 ,这里不再叙述tcp通信方式的配置 ExecStart=/home/xx/deeru_env/bin/gunicorn --workers 2 --pid /run/gunicorn/pid --bind unix:/run/gunicorn/socket deeru.wsgi ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID PrivateTmp=true [Install] WantedBy=multi-user.target
新建
/etc/systemd/system/gunicorn.socket
:[Unit] Description=gunicorn socket [Socket] ListenStream=/run/gunicorn/socket [Install] WantedBy=sockets.target
新建
/etc/tmpfiles.d/gunicorn.conf
:d /run/gunicorn 0755 someuser somegroup -
设置开机启动并开始gunicorn services:
systemctl enable gunicorn.socket systemctl start gunicorn.socket
修改nginx配置:
... http { ... upstream app_server { server unix:/tmp/gunicorn.sock fail_timeout=0; # TCP 方式改为 # server 192.168.0.7:8000 fail_timeout=0; } server { ... listen 80; location / { try_files $uri @proxy_to_app; } location @proxy_to_app { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; # we don't want nginx trying to do something clever with # redirects, we set the Host: header above already. proxy_redirect off; proxy_pass http://app_server; } # 静态文件 location ~ ^/(static|media)/ { root /home/xxx/project/DeerU; add_header Access-Control-Allow-Origin *; expires 864000; } } }
重启nginx:
nginx -s reload
Settings¶
下面只是列举了一些常见配置,以及DeerU的特殊配置,完整配置参考django文档 https://docs.djangoproject.com/zh-hans/2.0/ref/settings/
DeerU所有的配置请在 deeru/settings_local.py
中添加或修改
数据库配置¶
DeerU默认使用sqlite,如果你需要使用mysql,在
settings_local.py
中添加# settings_local.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'OPTIONS': { 'read_default_file': '/path/to/my.cnf', }, } } # my.cnf [client] database = NAME user = USER password = PASSWORD default-character-set = utf8注意:如果你使用mysql,需要手动创建mysql database,django并不会帮你自动创建,
如果你更改了数据库配置需要再次初始化项目
其他说明以及数据库支持参考
https://docs.djangoproject.com/zh-hans/2.0/ref/settings/#databases
CACHES¶
默认使用文件缓存,
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', } }你也可以使用内存、数据库、redis等作为缓存,参考 https://docs.djangoproject.com/zh-hans/2.0/ref/settings/#caches
FLATPAGE_URL¶
默认: /p/
单页面url前缀
ALLOWED_HOSTS¶
默认: [‘*’]
允许的hosts
DEBUG¶
默认:True
debug模式下会返回错误信息,不要在生产环境开启
CUSTOM_APPS¶
就是INSTALLED_APPS ,如果你添加了新的app,在CUSTOM_APPS
中加入
CUSTOM_EXPRESSION¶
自定义表达式查找路径
STATIC_URL¶
默认:/static/
静态文件的url
STATIC_ROOT¶
默认:工程目录下的 static 文件夹
静态文件保存目录,如果你更改了这一项需要再次初始化项目,或者运行
python manage.py collectstatic
收集静态文件
MEDIA_URL¶
默认:/media/
媒体文件的url
MEDIA_ROOT¶
默认:工程目录下的 media 文件夹
媒体文件保存目录
jet配置¶
DEERU_RICH_EDITOR¶
默认:
DEERU_RICH_EDITOR = { 'filed': 'app.ex_fields.fields.MFroalaField', 'article_kwargs': { ... }, 'flatpage_kwargs': { ... } }admin使用的富文本编辑器配置
- filed : 富文本编辑器filed路径
- article_kwargs : 文章filed的参数
- flatpage_kwargs : 单页面filed的参数
备份和恢复¶
你可以使用django内置命令备份、恢复数据库,
- 备份命令:
python manage.py dumpdata >mybk.json
- 恢复命令:
python manage.py loaddata mybk.json
除了备份数据库你还需拷贝 deeru/settings_local.py
, deeru/urls_local.py
和 媒体文件
内置命令¶
所有内置命令放在 app/management/commands
, ``deeru_cmd/management/commands `` 下
安装¶
-
install
¶ 下载DeerU:
deeru-admin install name [--branch master]
- name:
- 项目的文件夹名称
- branch:
- 从哪个分支下载,默认master
升级¶
-
upgrade
¶ 升级DeerU:
python manage.py upgrade
DeerU使用的是git进行升级,因此改动源码可能会导致升级失败。如改动了源码你需要手动运行
git pull origin master
升级,并解决冲突。另外升级后你需要手动重启DeerU
创建第三方模块¶
-
start
¶ 升级DeerU:
python manage.py start type name
给开发者用的命令,创建DeerU的第三方主题或插件,使用这个命令会自动生成
setup.py
,README.md
,.gitignore
等必要的文件,方便开发- type:
- 类型,可选项 theme、plugin
- name:
- 第三方模块名
从wordprees导入¶
-
import_wordpress
¶ 从wordprees的xml文件导入:
python manage.py import_wordpress xml_path [--mode (a|c|t)] [--nwp ] [--ncontent] [--cover (y|n|ask)]
- xml_path:
- xml文件路径
- mode:
导入的内容,默认:a
- a : 文章、评论、分类、标签
- c : 分类
- t : 标签
- nwp:
- xml文件中 命名空间wp的内容,默认:
{http://wordpress.org/export/1.2/}
- ncontent:
- xml文件中 命名空间content的内容,默认:
{http://purl.org/rss/1.0/modules/content/}
- cover:
是否使用xml文件中的内容覆盖数据库中的内容,默认:ask
- y : 是
- n : 否
- ask : 询问我
注解
1.评论暂不支持审核,所有不会导入未审核的评论,如果需要去掉get_comment()中对应的部分
2.wordprees的日期格式必须为: 2018-05-02 15:23:22
3.对评论的回复会自动在内容前添加 “回复 xx:”,如果不需要去掉save_comment()中对应部分
4.不会导入草稿
sitemap¶
DeerU提供了一个简单的sitemap,访问 http://127.0.0.1:8000/sitemap.xml 获取
开发指南¶
给开发者的开发指南¶
一些约定
开发代码时请遵从以下约定 开发约定
model说明
代码表达式开发
第三方模块开发
开发约定¶
导入model
你应该从
app.app_models
中导入,而不是从app.models
中,如:from app.app_models.content_model import Article
Config命名
如果你需要用到config,建议给config取一个本地化语言的名字,而不是英语名。 你可以用一个dict保存英语名与本地化名,如:
config_name={ 'top_menu':'顶部导航栏' } # 使用时 create_config( name=config_name['top_menu'], config='' ) config = get_config( name=config_name['top_menu'] )
如何获取Config
获取config时,你应该从
config.cache
中读取配置,config.config
中的配置是未解析表达式的:from ast import literal_eval from app.db_manager.config_manager import get_config_by_name from app.consts import app_config_context top_menu_config=get_config_by_name(app_config_context['top_menu']) top_menu=literal_eval(top_menu_config.cache)
dispatch_uid
如果你要使用绑定model的信号,需要注意DeerU预定了 “model名_信号名” 格式的 dispatch_uid ,如:”article_pre_save”,”article_post_save”。 建议在dispatch_uid前加上你的昵称或插件名,放在冲突。
Model¶
Content Model¶
Article¶
-
class
Article
¶ 文章
-
title
¶ 标题
-
content
¶ 正文
-
summary
¶ 简介
-
image
¶ 封面图片
-
classmethod
url
()¶ 返回文章url
-
classmethod
get_absolute_url
()¶ 返回文章url
-
classmethod
last_article
()¶ 上一篇,返回:
{ 'title': 'xx', 'id' : 12, 'url' : '/article/12' }
-
classmethod
next_article
()¶ 下一篇,返回:
{ 'title': 'xx', 'id' : 12, 'url' : '/article/12' }
-
classmethod
meta_data
()¶ 返回ArticleMeta
-
classmethod
category
()¶ 返回文章的分类
返回文章的tag
-
classmethod
comments
()¶ 返回评论
-
classmethod
format_comments
()¶ 返回按父子关系整理后的评论:
[ { 'comment' : Comment , 'children':[ {'comment' : Comment, 'to_nickname':'xx'} , { ... } ] }, {...} ]
-
Category¶
Tag¶
Comment¶
-
class
Comment
¶ 评论
-
nickname
¶
-
email
¶
-
content
¶ 正文
-
type
¶ 评论类型
- 201 : 对文章评论
- 202 : 对评论评论
-
root_id
¶ 根评论id。对文章评论时,这一项无意义。对评论回复时就是评论的id,对回复回复时,是最早的那条评论id。
-
to_id
¶ 给谁的评论。对文章评论时,这一项无意义。
注解
以下说的 评论、回复 其实是一个东西,方便区分用了两个词 评论:对文章的评论称作 "评论"; 回复:对评论的评论称作 "回复",对回复的回复也叫 "回复"; 注意区分root_id和to_id, 如: 文章-0 |__ 评论-1 |__ 回复-2 |__ 回复-3 |__ 回复-3-1 评论-1 :root_id是 文章-0 的id 回复-2 :root_id是 评论-1 的id; to_id是 评论-1 的id; 回复-3 :root_id是 评论-1 的id; to_id是 评论-1 的id; 回复-3-1 :root_id是 评论-1 的id; to_id是 回复-3 的id;
-
表达式开发¶
如何解析、自定义表达式
解析表达式¶
app.deeru_expression.manager
定义了一个解析表达式的函数
-
format_expression
(value)¶ - 返回:
全局变量表达式返回全局变量字符串,
代码表达式返回对应的表达式对象
非表达式返回value
- 参数:
- value: 表达式字符串
代码表达式返回的是代码表达式的实例化对象,需要在外部调用
get_result()
获取解析结果
自定义代码表达式¶
DeerU只提供了几个简单的代码表达式,你可以根据需要自定义你的表达式,
另外:表达式的名字最终会转为小写,因此IMG和Img是重名的,为了防止重名,自定义的表达式建议以自己的名字开头
注解
为了方便,以下所说的表达式特指代码表达式
编写自定义表达式¶
下面我们开始创建自定义表达式:
- 新建一个python包,以及py文件,
my_ex/
__init__.py
custom_expression.py
- 把你的py文件加入
settings_local.py
的CUSTOM_EXPRESSION
中
CUSTOM_EXPRESSION=['my_ex.custom_expression']
- 编写一个你的表达式类,继承
app.deeru_expression.expressions.BaseExpression
,并重写calculate()
函数 format_expression()
解析表达式时会把表达式分为 表达式名、参数 两部分,
这里再次强调以下,表达式名(也就是类名)最终会转为小写
参数 会放到类的成员变量 args 里
表达式名、参数 一定是用’|’分割开,如: {% text | some args %}
参数部分没有限制,你可以仍然用’|’分割,也可自定义你的参数格式
calculate()
的作用是解析参数,并返回需要的结果,它会在执行 get_result()
时调用。注意: calculate()
只会在第一次调用 get_result()
时执行,
后面将返回缓存的结果,因此同一个表达式实例不能重复使用
from app.deeru_expression.expressions import BaseExpression,get_attrs
class MText(BaseExpression):
"""
字符表达式
{% text| 值 [ | 其他属性] %}
返回{
'text':'xx',
'attrs':{
'style':'xx'
}
}
"""
def calculate(self):
if not self.args:
self.args = ''
# 这里默认用'|'分割
args = self.args.split('|')
if len(args) == 0:
raise ExpressionTypeError('表达式 text 至少需要一个参数')
text = args[0]
if len(args) > 1:
attrs = get_attrs(args[1:])
else:
attrs = {}
return {
'text': text,
'attrs': attrs
}
至此你已经成功编写了一个表达式,载入表达式需要重启工程
注解
函数 calculate()
并没有限制返回的数据类型,你可以返回字符串、字典或者html标签(在最早版本的表达式中,就是这样做的)
不过建议返回字典或字符串,这样更利于主题开发者使用你的表达式返回结果
第三方模块开发¶
DeerU为第三方开发者提供了一个 start
命令,用于快速生成Django app以及一些必要的文件,
这个命令也一样可以用于开发其他Django项目的app,不过在DeerU项目外运行这个命令你需要这样使用 deeru-admin start xxx
下面将用一个示例说明开发第三方模块的基本流程
新建项目:
deeru-admin install m_deeru
运行start命令
python manage.py start plugin content_detection
如果一切正常,那么你会看到下面的目录结构(这里只选取了重要的文件):
m_deeru/ content_detection/ apps.py consts.py ... content_detection_setup.py README.rst MANIFEST.in git_add.sh
-
content_detection/apps.py
在apps.py的中
AppConfig
中你可以看到一些专属的变量- deeru_config_context:
config_context的路径
-
content_detection/consts.py
在consts.py的中有一个dict
content_detection_config_context
, 这个只有主题开发才用得到。 在这个dict中的配置,会在访问页面时从数据库读取放入context中传给前端,这个dict的key为context中的名字,value为数据库中保存的名字,如:
{ 'top_ico2' : '顶部图标栏2' }
在 admin - 配置 - 新建 名为”顶部图标栏2” 的配置,此配置放入context中时名字为”top_ico2”
-
content_detection_setup.
py
¶ 此命令自动生成了打包的setup.py文件,你需要填写里面空的地方
-
git_add.
sh
¶ 如果不想把DeerU的代码一同上传到git仓库,可以查看里面的add示例。
-
编写你的代码
打包发布:
python content_detection_setup.py sdist bdist_wheel twine upload dist/*
提交到DeerU插件、主题列表里
fork项目 https://github.com/gojuukaze/deeru_plugin_theme 把你的插件、主题加到py、readme 中,提交 合并请求
开发主题¶
你可以使用django的模板开发主题, 如果你不想用django模板,你可以新建一个独立的前端工程,然后使用 api插件 从后端获取数据。
如果你需要使用django的模板开发,下面给出了一些必要说明
创建django app:
python manage.py start theme m_theme
和插件不同,主题的目录下多了两个文件夹:
m_theme/ templates/ m_theme static/ m_theme编写代码时,你的html文件应放在
templates/m_theme
下,静态文件应放在static/m_theme
下。
编写html
你需要编写5个html模板,分别是(注意,模板名不能改变):
- home.html : 博客首页
- detail_article.html : 文章页面
- category.html : 分类下的文章列表页
- tag.html : 标签下的文章列表页
- detail_flatpage.html : 单页面
url与html的对应关系
/
: home.html/article/<int:article_id>
: detail_article.html/category/<int:category_id>
: category.html/tag/<int:tag_id>
: tag.html/你的单页面前缀/<path:flatpage_url>
: detail_flatpage.html
view传递的context结构
查看 Context , Url View接口文档
在模板中使用软连接
如果你需要在模板中引入静态文件,你应该这样做:
{% load static %} <link href="{% static '/m_theme/css/m_theme.css' %}" /> <script src="{% static '/m_theme/js/m_theme.js' %}"></script>
如果你需要使用文章url或者其他url,你应该这样做:
<a href="{% url 'app:detail_article' article.id %}> <a href="{{ article.url }}> <a href="{% url 'app:tag' 23 %}> <a href="{{ tag.url }}> <form action="{% url 'app:create_comment' %}" method="post"></form>
如何使用ui配置?
如果你看了使用者指南你应该清楚,DeerU内置了”顶部导航栏”、”顶部图标栏”两个配置,你可以在view传到的context[‘config’]中找到他们
如果你的主题还需要其他配置,你可以把配置放到”通用配置”中,你也可以新建一个自己的配置。
如何新建配置?
内置的配置满足不了你的需要,想增加一个”侧边栏配置”?
首先你需要在
consts.py
的m_theme_config_context
中加入你的配置:m_theme_config_context = { 'm_theme_aside_config' : 'M_Theme侧边栏配置' }
然后在admin中添加名为”M_Theme侧边栏配置”的配置,这样context就会传递你的配置,位置在
context['config']['m_theme_aside_config']
关于评论的form
文章详情页面传了一个 CommentForm ,但并不建议直接用它来生成form。另外,该form评论内容content生成的
<textarea>
并不是富文本编辑器。下面给了一个form的示例:
{% csrf_token %} <div class="fieldWrapper"> {{ comment_form.nickname }} {% if comment_form.nickname.help_text %} <p class="help">{{ comment_form.nickname.help_text|safe }}</p> {% endif %} </div> <div class="fieldWrapper"> {{ comment_form.email }} {% if comment_form.email.help_text %} <p class="help">{{ comment_form.email.help_text|safe }}</p> {% endif %} </div> <div class="fieldWrapper"> {{ comment_form.content }} {% if comment_form.content.help_text %} <p class="help">{{ comment_form.content.help_text|safe }}</p> {% endif %} </div> <input type="hidden" name="article_id" id="id_article_id" value="{{ article.id }}"> <input type="hidden" name="root_id" id="id_root_id" value="-1"> <input type="hidden" name="to_id" id="id_to_id" value="-1"> <input type="hidden" name="type" id="id_type" value="201"> <input type="hidden" name="anchor" value="#comment">
Context¶
如果你要开发主题,那么阅读这篇文档,可以帮您了解view的context中都包含了些什么
基础context格式¶
每个view都返回了一个基础的context,他的格式如下:
context = {
'config' : {
'global_value' : { ... },
'top_menu' : { ... },
'top_ico' : [ ... ],
'common_config' : { ... }
}
'category' : [
{
'category' : Category, # Category - Category model的实例化对象
'children' :[
{
'category':Category
},
{ ... }
]
},
{ ... }
]
'tags' :[ Tag, Tag ,] # Tag - Tag model的实例化对象
}
- config : 配置中需要添加在context中的所有配置,默认返回的配置有 ‘global_value’,’top_menu’,’top_ico’,’common_config’
- category : 按父子结构整理后的分类
- tag : 按文章数量排序的tag list,返回20个
context中的对象¶
context中返回的article,category,tag等都是对应model的实例化对象,你可以直接在模板中使用对象的成员变量及函数,如:
# 假设context = { 'article':Article }
# html:
<h1>Article.title</h1>
<div>Article.content</div>
# 获取文章的分类
{% for c in Article.category %}
<span>c.name</span> |
{% end for %}
DeerU为每个model都提供了丰富的成员函数,你轻易从对象中获取你需要的数据。 每个model的变量、函数说明参照 Model 这里不再叙述。
除了model里的对象,context还有一些特殊的对象:
Url View接口文档¶
下面每个context中都包含 基础context ,下面文档中不再重复说明, 另外,context中对象的方法参考 Context ,Model
首页¶
url :
/
view :
views_class.Home
name :
index
template :
home.html
请求方式 :
GET
- 参数 :
- page : 页码,默认: 1
- pre_page : 每页文章数,默认:7
context
{ 'paginator' : DeerUPaginator, 'article_list' : [ Article, Article ] }
文章列表 – 根据分类筛选¶
url :
category/<int:category_id>
view :
views_class.CategoryArticle
name :
category_article
template :
category.html
请求方式 :
GET
- 参数 :
- page : 页码,默认: 1
- pre_page : 每页文章数,默认:7
context
{ 'paginator' : DeerUPaginator, 'article_list' : [ Article, Article ] }
文章列表 – 根据标签筛选¶
url :
tag/<int:tag_id>
view :
views_class.TagArticle
name :
tag_article
template :
tag.html
请求方式 :
GET
- 参数 :
- page : 页码,默认: 1
- pre_page : 每页文章数,默认:7
context
{ 'paginator' : DeerUPaginator, 'article_list' : [ Article, Article ] }
文章详情¶
url :
article/<int:article_id>
view :
views_class.DetailArticle
name :
detail_article
template :
detail_article.html
请求方式 :
GET
参数 :
context
{ 'article' : Article 'comments' : [ Comment, Comment, ] 'comment_form' : CommentForm, # 评论的form 'form_error' : 'xx' # 提交comment_form的错误信息 }
创建评论¶
需要注意,创建评论接口返回的html是文章详情的html,如果有错误,会添加
form_error
中
url :
comment/create
view :
views.create_comment
name :
create_comment
template :
detail_article.html
请求方式 :
POST
- 参数 :
anchor : 锚,如果需要评论后跳转到相关的地方,则带上这个参数,如 “#comment”
content : 内容
email : 可不填
nickname : nickname
type : 类型,可选项如下:
- 201 : 对文章评论
- 202 : 对评论评论
to_id : 回复的评论id,具体说明参见 Comment model说明,以及DeerU源码
root_id : 根评论id,具体说明参见 Comment model说明,以及DeerU源码
context
{ 'article' : Article 'comments' : [ Comment, Comment, ] 'comment_form' : CommentForm, # 评论的form 'form_error' : 'xx' # 提交comment_form的错误信息 }
单页面¶
url :
你的单页面前缀/<path:url>
view :
views_class.DetailFlatPage
name :
detail_flatpage
template :
detail_flatpage.html
请求方式 :
GET
参数 :
context
{ 'flatpage' : FlatPage, }
贡献代码¶
你发现了bug或者优化了代码并且想合并到主分支?
首先你需要fork代码到你的仓库,然后切换到dev分支,在dev分支上开发完成后再github中提交 pull request,合并到dev分支。
注解
只接受合并到dev分支的pull request
License¶
DeerU使用 GNU General Public License v3.0 协议 , 你可以在遵循此协议的情况下免费使用DeerU
警告
需要注意的是,DeerU本身是免费的,但后台管理使用了富文本编辑器froala,其扩展插件并不免费,你可以在以下链接中查看收费信息:
https://github.com/froala/django-froala-editor#license
https://froala.com/wysiwyg-editor/pricing
你可以自己更换其他编辑器( 参考 富文本编辑器 ),我也会在之后内置一些富文本编辑器的替代方案