Welcome to DeerU’s documentation!

Deeru

DeerU是一个开源博客系统,它基于django开发

_images/deeru_green.png

依赖

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

另外安装之前建议配置虚拟环境

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

安装完成后下面我们测试一下DeerU是否能正常运行

初始化
  1. 运行下面命令初始化项目,注意:如果你更改了数据库的配置,或者修改了主题的静态文件 则需要再次运行初始化
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端口,你需要修改一下

_images/home1.png

配置

DeerU的所有配置都采用json格式文件进行配置,你可以在 http://127.0.0.1:8000/admin/app/config/ 中查看修改配置

注解

需要注意的是配置文件中bool值采用0,1表示,0-flase, 1-true

如图:

_images/config.png

你一共有3大部分可以进行配置:

全局变量配置

这里配置的是全局变量,比如:站点名称、作者名称等

title
  • 是否必须 : 是
  • 值类型 : 字符串

html <head>中的title

blog_name
  • 是否必须 : 是
  • 值类型 : 字符串

站点名称

nickname
  • 是否必须 : 是
  • 值类型 : 字符串

文章作者及站点所有者名称

通用配置

配置一些其他的东西,主题、插件的一些专业配置也会放到里面

theme
  • 是否必须 : 是
  • 值类型 : 字符串
  • 默认值 : base_theme

使用的主题

baidu_auto_push
  • 是否必须 : 是
  • 值类型 : bool
  • 默认值 : 0

base_theme的配置,是否自动推送url到百度 (使用的是百度的自动推送脚本,不过这个脚本貌似已经好久没维护,不能正常运行了)

ui配置

DeerU把界面分为5大块,如图

_images/ui_config.png

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的图标
img类型

结构:

{
    "type":'img',
    "src":'/media/logo_white.png',
    "attrs":{
        "style":'xx',
        "hight":'xx',
    }

}
  • src : 图片地址
  • attrs : 其他属性
svg类型

结构:

{
    "type":'svg',
    "svg":'xxx',
    "attrs":{
        "style":'xx',
        "hight":'xx',
    }

}
  • svg : svg图片标签
  • attrs : 其他属性
fa类型

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 : 其他属性
文本类型

文本类型结构如下:

{
    "text":'xx',
    "attrs":{
        "style":'xx',
    }

}
  • text : 文本值
  • 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
text
class Text

text表达式,返回text字典

get_result():

返回一个text字典

{
    "text":'xx',
    "attrs":{
        "style":'xx',
    }

}
help:

{% text| [| 其他属性] %}

  • 第一个参数: 必须项 text内容
  • 其他属性: 可选
例子:
{% text| 1122 %}

{% text| 1122 | style="color:red;" %}

部署DeerU

部署DeerU和部署Django项目一样,你可以自选查阅网上的Django部署文档。 这里提供一个部署方法。

部署一共有3步:

修改settings

derru/settings_local.py 中的 DEBUG 改为 FalseALLOWED_HOSTS 改为你的ip或域名

DEBUG = False
ALLOWED_HOSTS = ['www.xxx.com','111.xx.xx.xx']
部署静态、媒体文件

django 非debug模式下并不会返回静态、媒体文件,你可以用下面两个方法部署他们文件:

  1. 使用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

  2. 你也可以选择把静态、媒体文件上传到七牛或其他cdn服务商,然后修改 STATIC_URL , MEDIA_URL 为对应的url

    推荐有两个插件自动上传到七牛的插件:

    • deeru-qiniu : github-deeru-qiniu

    • django-qiniu-storage : github-django-qiniu-storage doc-django-qiniu-storage

注解

什么是静态文件、媒体文件?

静态文件 : 前端的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 的方式运行

  1. 新建 /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
    
  2. 新建 /etc/systemd/system/gunicorn.socket:

    [Unit]
    Description=gunicorn socket
    
    [Socket]
    ListenStream=/run/gunicorn/socket
    
    [Install]
    WantedBy=sockets.target
    
  3. 新建 /etc/tmpfiles.d/gunicorn.conf:

    d /run/gunicorn 0755 someuser somegroup -
    
  4. 设置开机启动并开始gunicorn services:

    systemctl enable gunicorn.socket
    
    systemctl start gunicorn.socket
    
  5. 修改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;
        }
    
      }
    }
    
  6. 重启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

https://docs.djangoproject.com/zh-hans/2.0/ref/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配置

jet 是django的后台管理界面扩展

相关配置有:
  • JET_DEFAULT_THEME : 主题

其他配置参考: http://jet.readthedocs.io/en/latest/

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的参数
froala编辑器配置

DeerU后台富文本编辑器使用 froala编辑器

相关配置有:
  • FROALA_EDITOR_PLUGINS : 插件
  • FROALA_EDITOR_OPTIONS : 编辑器默认选项,包括语言、上传目录等

具体说明参考: https://github.com/froala/django-froala-editor

备份和恢复

你可以使用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:
第三方模块名
初始化
init_deeru

初始化DeerU:

python manage.py init_deeru

初始化数据库,收集静态文件

从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.不会导入草稿

备份数据库
dumpdata

django自带的备份命令:

python manage.py dumpdata >mybk.json
恢复数据库
loaddata

django自带的恢复命令:

python manage.py loaddata  mybk.json

sitemap

DeerU提供了一个简单的sitemap,访问 http://127.0.0.1:8000/sitemap.xml 获取

开发指南

给开发者的开发指南

开发约定

  1. 导入model

    你应该从 app.app_models 中导入,而不是从 app.models 中,如:

    from app.app_models.content_model import Article
    
  2. Config命名

    如果你需要用到config,建议给config取一个本地化语言的名字,而不是英语名。 你可以用一个dict保存英语名与本地化名,如:

    config_name={
    
        'top_menu':'顶部导航栏'
    }
    
    # 使用时
    
    create_config( name=config_name['top_menu'], config='' )
    
    config = get_config( name=config_name['top_menu'] )
    
  3. 如何获取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)
    
  4. 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()

返回文章的分类

classmethod tags()

返回文章的tag

classmethod comments()

返回评论

classmethod format_comments()

返回按父子关系整理后的评论:

[
    {
        'comment' : Comment ,
        'children':[
                {'comment' : Comment, 'to_nickname':'xx'} ,

                { ... }
        ]
    },

    {...}
]
ArticleMeta
class ArticleMeta
article_id

article_id

read_num

阅读量

comment_num

评论数

Category
class Category
name

name

father_id

父级目录

m_order

排序

classmethod url()

返回文章列表页的url

classmethod get_absolute_url()

返回文章列表页的url

classmethod get_article_category_list()

返回ArticleCategory queryset

classmethod get_article_list()

返回分类下的文章 queryset

ArticleCategory
class ArticleCategory

文章分类关系表

article_id
category_id
Tag
class Tag
name

name

classmethod url()

返回文章列表页的url

classmethod get_absolute_url()

返回文章列表页的url

classmethod get_article_tag_list()

返回ArticleTag queryset

classmethod get_article_list()

返回tag下的文章 queryset

ArticleTag
class ArticleTag

文章tag关系表

article_id
tag_id
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;
FlatPage
class FlatPage

单页面

title

标题

content

正文

url

url

classmethod get_absolute_url()

返回文章url

表达式开发

如何解析、自定义表达式

解析表达式

app.deeru_expression.manager 定义了一个解析表达式的函数

format_expression(value)
返回:

全局变量表达式返回全局变量字符串,

代码表达式返回对应的表达式对象

非表达式返回value

参数:
value: 表达式字符串

代码表达式返回的是代码表达式的实例化对象,需要在外部调用 get_result() 获取解析结果

自定义代码表达式

DeerU只提供了几个简单的代码表达式,你可以根据需要自定义你的表达式,

另外:表达式的名字最终会转为小写,因此IMG和Img是重名的,为了防止重名,自定义的表达式建议以自己的名字开头

注解

为了方便,以下所说的表达式特指代码表达式


编写自定义表达式

下面我们开始创建自定义表达式:

  1. 新建一个python包,以及py文件,
my_ex/
    __init__.py
    custom_expression.py
  1. 把你的py文件加入 settings_local.pyCUSTOM_EXPRESSION
CUSTOM_EXPRESSION=['my_ex.custom_expression']
  1. 编写一个你的表达式类,继承 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

下面将用一个示例说明开发第三方模块的基本流程

  1. 新建项目:

    deeru-admin install m_deeru
    
  2. 运行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示例。

  3. 编写你的代码

  4. 打包发布:

    python content_detection_setup.py sdist bdist_wheel
    
    twine upload dist/*
    
  1. 提交到DeerU插件、主题列表里

    fork项目 https://github.com/gojuukaze/deeru_plugin_theme 把你的插件、主题加到py、readme 中,提交 合并请求

开发插件
1. 创建django app::
python manage.py start plugin your_name
#. 编写代码
开发主题

你可以使用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结构

  • 在模板中使用软连接

    如果你需要在模板中引入静态文件,你应该这样做:

    {% 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.pym_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还有一些特殊的对象:

class DeerUPaginator

deeru的Paginator

end_index

末尾页码

current_page_num

当前页码

class CommentForm

评论的form

Url View接口文档

下面每个context中都包含 基础context ,下面文档中不再重复说明, 另外,context中对象的方法参考 ContextModel

首页
  • 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

Change Log

0.1.0 - alpha

  • 第一个测试版完成

0.2.0 - alpha

  • 单页面
  • 修改安装方式
  • 重新设计表达式
  • 添加了一些内置命令
  • 完善文档
  • 修改bug

1.0.0

  • 第一个正式版发布

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

你可以自己更换其他编辑器( 参考 富文本编辑器 ),我也会在之后内置一些富文本编辑器的替代方案

截图

首页

_images/home.png

文章详情

_images/detail.png

admin

_images/admin.png

admin2

_images/admin2.png

admin3

_images/admin3.png

手机端页面

_images/p1.png

手机端页面

_images/p2.png

手机端页面

_images/p3.png

Indices and tables