Skip to content

Commit 265e610

Browse files
committed
feat: 增加获取诗词作者接口
1 parent 2e7e359 commit 265e610

4 files changed

Lines changed: 90 additions & 261 deletions

File tree

app/plugins/poem/README.md

Lines changed: 48 additions & 252 deletions
Original file line numberDiff line numberDiff line change
@@ -1,237 +1,17 @@
1-
---
2-
title: 开发一个小插件
3-
---
1+
# 古诗词插件接口文档
42

5-
# <H2Icon /> 开发一个小插件(目前处于测试状态)
3+
>本接口文档仅供插件开发者阅读,如果你想学习如何开发此插件,请阅读
4+
[插件开发](http://doc.cms.7yue.pro/lin/server/plugin_create.html)
65

7-
Lin 的插件很灵活,你可以在本地的 plugins 目录下使用一个插件,或者通过`pip`安装一个插件到 site-packages 中。
6+
## 获取所有诗词接口
7+
URL:
8+
>GET http://localhost:5000/plugin/poem/demo/all
89
10+
Parameters:
11+
- count: (可选)获取的数量,最小1,最大100,默认5
12+
- author: (可选)按照作者查找,作者列表从获取所有作者API获取
913

10-
> 在大家已经了解了插件机制和插件的使用方法后,本小结我们来手把手带着大家来开发一个简易的古诗词插件,我们选择在本地的`app/plugins`目录下进行开发,你将学到一个插件的整个开发流程和注意事项。好了,话不多说,我们开始吧!
11-
12-
:::tip
13-
定义只涉及前端的插件为`A型插件`,只涉及后端的插件为`B型插件`,需要前后端对接 API 共同完成一个业务逻辑的插件为`AB型插件`。本插件为前后端共同协作的`AB型插件`,所以为了规范统一,请特别注意你们的插件命名一致哦。
14-
:::
15-
16-
## 插件目录结构
17-
在开发前,我们首先来浏览一下插件的目录结构。
18-
`app/plugins`目录下创建以插件名称命名的目录,我们所开发的示例插件为古诗词展示插件,所以以`poem`来命名。我们在`app/plugins/poem/app`目录下进行插件的开发,其余文件含义请查看下面的注释。
19-
```bash
20-
├───poem
21-
│ │ config.py // 配置文件(必需),记录关于插件的可用配置
22-
│ │ info.py // 插件基本信息
23-
│ │ README.md // 插件文档
24-
│ │
25-
│ └───app // 应用开发目录
26-
│ controller.py // 控制层文件
27-
│ forms.py // 校验层文件
28-
│ model.py // 模型层文件
29-
│ __init__.py // 导出文件(必需)。重要!!!
30-
```
31-
32-
## 定义路由
33-
首先,在控制层文件`controller.py`下定义一个红图`redprint`,以及业务相关的视图函数。
34-
35-
```python
36-
from flask import jsonify
37-
from lin.redprint import Redprint
38-
39-
from app.plugins.poem.app.forms import PoemSearchForm
40-
from .model import Poem
41-
42-
api = Redprint('poem')
43-
44-
45-
@api.route('/all', methods=['GET'])
46-
def get_list():
47-
poems = Poem().get_all()
48-
return jsonify(poems)
49-
50-
...
51-
```
52-
53-
:::warning
54-
由于本插件只有一个控制器,也就是只有一个`redprint`,那么Lin内核所生成的路由不会有二级前缀,只有一个一级前缀,即/plugin,对此如果你还不够了解,请移步阅读[插件中的路由规范](./plugin_practice.md#上传图片到本地)
55-
:::
56-
57-
## 定义数据库模型类
58-
由于poem插件所实现的业务需要依托数据库,所以我们下面需要在模型层文件设计一个名为Poem的模型类,这里请先检查你的数据库中是否已存在同名表再确认命名。
59-
60-
61-
```python
62-
from lin.core import lin_config
63-
from lin.exception import NotFound
64-
from lin.interface import InfoCrud as Base
65-
from sqlalchemy import Column, String, Integer, Text
66-
67-
68-
class Poem(Base):
69-
id = Column(Integer, primary_key=True, autoincrement=True)
70-
title = Column(String(50), nullable=False, comment='标题')
71-
author = Column(String(50), default='未名', comment='作者')
72-
dynasty = Column(String(50), default='未知', comment='朝代')
73-
content = Column(Text, nullable=False, comment='内容')
74-
image = Column(String(255), default='', comment='配图')
75-
76-
def get_all(self):
77-
poems = self.query.filter_by(delete_time=None).limit(
78-
lin_config.get_config('poem.limit')
79-
).all()
80-
if not poems:
81-
raise NotFound(msg='没有找到相关诗词')
82-
return poems
83-
```
84-
同样的,本插件只需要一个模型类,如果你所设计的插件需要多个模型类,请定义多个模型层文件。如果你对模型类的开发还不够了解,可以先去学习一下[模型管理]('./authority_and_models.md')小节
85-
86-
## 关于配置文件
87-
在上面的模型层代码中,你或许已经发现,我们从lin的核心库中导入了`lin_config`,并且使用`lin_config.get_config('poem.limit')`获取到了配置,那么配置文件定义在哪里呢?
88-
在插件目录结构中,我们可以发现`config.py`文件的位置。我们打开`config.py`配置文件,可以发现,Lin建议用户定义的配置项是小写的。如果你开发的插件有更多的配置,都可以在这个文件中添加。
89-
```python
90-
# app/plugins/poem/config.py
91-
limit = 20
92-
```
93-
94-
## 定义数据校验类
95-
当我们需要校验用户传递过来的参数时,别忘了数据校验层的存在,我们在`forms.py`中定义一个简单的数据校验类`PoemSearchForm`。并在视图函数中调用。
96-
97-
```python
98-
# app/plugins/poem/app/forms.py
99-
from lin.forms import Form
100-
from wtforms import StringField
101-
from wtforms.validators import DataRequired
102-
103-
104-
class PoemSearchForm(Form):
105-
q = StringField(validators=[
106-
DataRequired(message='必须传入搜索关键字')
107-
])
108-
109-
110-
# app/plugins/poem/app/controller.py
111-
...
112-
113-
@api.route('/search', methods=['GET'])
114-
def search():
115-
form = PoemSearchForm().validate_for_api()
116-
poems = Poem().search(form.q.data)
117-
return jsonify(poems)
118-
119-
```
120-
121-
## 导出文件
122-
123-
基本业务逻辑已经实现,最关键的一步还要导出文件,也就是在`poem/app/__init__.py`中写入要被 Lin 的`loader`(加载器)自动加载的`红图 api``数据模`型类
124-
125-
```python
126-
# app/plugins/poem/app/__init__.py
127-
from .controller import api
128-
from .model import Poem
129-
```
130-
131-
如果你有多个红图或者多个模型类,同样在这个文件下导入即可,但请注意`不要重名`
132-
133-
## 用配置开启插件
134-
135-
> 最后,在`app/config/setting.py`中添加如下配置:
136-
137-
```python
138-
# setting.py
139-
140-
PLUGIN_PATH = {
141-
'poem': {'path': 'app.plugins.poem', 'enable': True, 'limit': 5},
142-
}
143-
```
144-
145-
对于该配置项的含义,已经在[插件使用](./plugin_practice.md)一小节详细介绍。此处设置了limit为5,他的权级会高于上述`config.py`中的权级,所以在调用配置时,会优先调用`setting.py`中的配置。
146-
147-
至此,这个简单插件的全部开发已经完成了。运行starter.py,我们会发现数据库中会出现我们想要创建的`poem`表。下面请打开项目根目录下的fake.py,替换成下面的代码并执行,向该表中添加一些初始数据。
148-
149-
```python
150-
from app.app import create_app
151-
from app.plugins.poem.app.model import Poem
152-
from lin.db import db
153-
154-
app = create_app()
155-
with app.app_context():
156-
with db.auto_commit():
157-
# 添加诗歌
158-
poem1 = Poem()
159-
poem1.title = '生查子·元夕'
160-
poem1.author = '欧阳修'
161-
poem1.dynasty = '宋代'
162-
poem1.content = """去年元夜时,花市灯如昼。
163-
月上柳梢头,人约黄昏后。
164-
今年元夜时,月与灯依旧。
165-
不见去年人,泪湿春衫袖。"""
166-
db.session.add(poem1)
167-
168-
poem2 = Poem()
169-
poem2.title = '临江仙·送钱穆父'
170-
poem2.author = '苏轼'
171-
poem2.dynasty = '宋代'
172-
poem2.content = """一别都门三改火,天涯踏尽红尘。依然一笑作春温。无波真古井,有节是秋筠。
173-
惆怅孤帆连夜发,送行淡月微云。尊前不用翠眉颦。人生如逆旅,我亦是行人。"""
174-
db.session.add(poem2)
175-
176-
poem3 = Poem()
177-
poem3.title = '春望词四首'
178-
poem3.author = '薛涛'
179-
poem3.dynasty = '唐代'
180-
poem3.content = """花开不同赏,花落不同悲。
181-
欲问相思处,花开花落时。
182-
揽草结同心,将以遗知音。
183-
春愁正断绝,春鸟复哀吟。
184-
风花日将老,佳期犹渺渺。
185-
不结同心人,空结同心草。
186-
那堪花满枝,翻作两相思。
187-
玉箸垂朝镜,春风知不知。"""
188-
db.session.add(poem3)
189-
190-
poem4 = Poem()
191-
poem4.title = '长相思'
192-
poem4.author = '纳兰性德'
193-
poem4.dynasty = '清代'
194-
poem4.content = """山一程,水一程,身向榆关那畔行,夜深千帐灯。
195-
风一更,雪一更,聒碎乡心梦不成,故园无此声。"""
196-
db.session.add(poem4)
197-
198-
poem5 = Poem()
199-
poem5.title = '离思五首·其四'
200-
poem5.author = '元稹'
201-
poem5.dynasty = '唐代'
202-
poem5.content = """曾经沧海难为水,除却巫山不是云。
203-
取次花丛懒回顾,半缘修道半缘君。"""
204-
db.session.add(poem5)
205-
206-
poem6 = Poem()
207-
poem6.title = '浣溪沙·一曲新词酒一杯'
208-
poem6.author = '晏殊'
209-
poem6.dynasty = '宋代'
210-
poem6.content = """一曲新词酒一杯,去年天气旧亭台。夕阳西下几时回?
211-
无可奈何花落去,似曾相识燕归来。小园香径独徘徊。"""
212-
db.session.add(poem6)
213-
214-
poem7 = Poem()
215-
poem7.title = '浣溪沙·残雪凝辉冷画屏'
216-
poem7.author = '纳兰性德'
217-
poem7.dynasty = '清代'
218-
poem7.content = """残雪凝辉冷画屏,落梅横笛已三更,更无人处月胧明。
219-
我是人间惆怅客,知君何事泪纵横,断肠声里忆平生。 """
220-
db.session.add(poem7)
221-
222-
poem8 = Poem()
223-
poem8.title = '蝶恋花·春景'
224-
poem8.author = '苏轼'
225-
poem8.dynasty = '宋代'
226-
poem8.content = """花褪残红青杏小。燕子飞时,绿水人家绕。枝上柳绵吹又少。天涯何处无芳草。
227-
墙里秋千墙外道。墙外行人,墙里佳人笑。笑渐不闻声渐悄。多情却被无情恼。"""
228-
db.session.add(poem8)
229-
```
230-
231-
## 用poseman调试接口
232-
233-
### 调用获取所有诗词接口
234-
使用GET方法访问`http://localhost:5000/plugin/poem/demo/all`url,将会得到如下数据:
14+
Response:
23515
```json
23616
[
23717
{
@@ -251,37 +31,53 @@ with app.app_context():
25131
"id": 2,
25232
"image": "",
25333
"title": "临江仙·送钱穆父"
254-
},
34+
}
35+
]
36+
```
37+
38+
39+
## 获取所有作者接口
40+
URL:
41+
>GET http://localhost:5000/plugin/poem/authors
42+
43+
Response:
44+
```json
45+
[
46+
"元稹",
47+
"晏殊",
48+
"欧阳修",
49+
"纳兰性德",
50+
"苏轼",
51+
"薛涛"
52+
]
53+
```
54+
55+
## 搜索诗词接口
56+
>GET http://localhost:5000/plugin/poem/search?q=浣溪沙
57+
58+
Parameters:
59+
- q: (必填)查询关键字
60+
61+
Response:
62+
```json
63+
[
25564
{
256-
"author": "薛涛",
257-
"content": "花开不同赏,花落不同悲。\n 欲问相思处,花开花落时。\n 揽草结同心,将以遗知音。\n 春愁正断绝,春鸟复哀吟。\n 风花日将老,佳期犹渺渺。\n 不结同心人,空结同心草。\n 那堪花满枝,翻作两相思。\n 玉箸垂朝镜,春风知不知",
65+
"author": "晏殊",
66+
"content": "一曲新词酒一杯,去年天气旧亭台。夕阳西下几时回?\n无可奈何花落去,似曾相识燕归来。小园香径独徘徊",
25867
"create_time": 1548581209000,
259-
"dynasty": "唐代",
260-
"id": 3,
68+
"dynasty": "宋代",
69+
"id": 6,
26170
"image": "",
262-
"title": "春望词四首"
71+
"title": "浣溪沙·一曲新词酒一杯"
26372
},
26473
{
26574
"author": "纳兰性德",
266-
"content": "山一程,水一程,身向榆关那畔行,夜深千帐灯。\n 风一更,雪一更,聒碎乡心梦不成,故园无此声。",
75+
"content": "残雪凝辉冷画屏,落梅横笛已三更,更无人处月胧明。\n我是人间惆怅客,知君何事泪纵横,断肠声里忆平生。 ",
26776
"create_time": 1548581209000,
26877
"dynasty": "清代",
269-
"id": 4,
78+
"id": 7,
27079
"image": "",
271-
"title": "长相思"
272-
},
273-
{
274-
"author": "元稹",
275-
"content": "曾经沧海难为水,除却巫山不是云。\n取次花丛懒回顾,半缘修道半缘君。",
276-
"create_time": 1548581209000,
277-
"dynasty": "唐代",
278-
"id": 5,
279-
"image": "",
280-
"title": "离思五首·其四"
80+
"title": "浣溪沙·残雪凝辉冷画屏"
28181
}
28282
]
28383
```
284-
285-
## 小结
286-
287-
在本节中,我们遵守插件的开发规范,很容易地开发出了古诗词插件的后端API部分,如果你想了解前端插件部分的实际开发,请移步[前端插件](#../client/plugin.md)

app/plugins/poem/app/controller.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
from flask import jsonify
22
from lin.redprint import Redprint
33

4-
from app.plugins.poem.app.forms import PoemSearchForm
4+
from app.plugins.poem.app.forms import PoemListForm, PoemSearchForm
55
from .model import Poem
66

77
api = Redprint('poem')
88

99

1010
@api.route('/all', methods=['GET'])
1111
def get_list():
12-
poems = Poem().get_all()
12+
form = PoemListForm().validate_for_api()
13+
poems = Poem().get_all(form)
1314
return jsonify(poems)
1415

1516

@@ -18,3 +19,9 @@ def search():
1819
form = PoemSearchForm().validate_for_api()
1920
poems = Poem().search(form.q.data)
2021
return jsonify(poems)
22+
23+
24+
@api.route('/authors', methods=['GET'])
25+
def get_authors():
26+
authors = Poem.get_authors()
27+
return jsonify(authors)

app/plugins/poem/app/forms.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
from lin.forms import Form
2-
from wtforms import StringField
3-
from wtforms.validators import DataRequired
2+
from wtforms import StringField, IntegerField
3+
from wtforms.validators import DataRequired, Optional, NumberRange
4+
5+
6+
class PoemListForm(Form):
7+
count = IntegerField()
8+
author = StringField(validators=[Optional()])
9+
10+
def validate_count(self, row):
11+
if not row.data:
12+
return True
13+
if int(row.data) > 100 or int(row.data) < 1:
14+
raise ValueError('必须在1~100之间取值')
415

516

617
class PoemSearchForm(Form):

0 commit comments

Comments
 (0)