查看原文
其他

Sanic教程: 3.项目结构

老胡的储物柜 老胡的储物柜 2022-06-01

项目结构

通过前面的讲解,我们了解了 Sanic的运行方式以及编写一个好的配置方案,是不是想要立马编写一个应用练练手呢?别急,请先看完这一章节,了解一下你要写的应用得用什么样的结构。

在 github上也看了不少的 Python项目吧,相信你也清楚,一个项目,在最外层他们应该是一样的,简单概括下,大概是下面这样的结构:

  1. pro_name

  2. ├── docs            # 项目文档说明

  3. ├── src or pro_name/# 项目名称

  4. ├── tests           # 测试用例

  5. ├── README.md       # 项目介绍

  6. └──requirements.txt # 该项目依赖的第三方库

那接下来需要讨论的,就是 src 或者说 pro_name(这个就看你心情命名了,一般与最外层一样的名字)的内部结构该是什么样的呢?

本章将写一个 rss 解析展示的项目用做演示。

普通的项目结构

一个普通的项目:

  • 不需要添加后续模块功能

  • 快速开发使用,不需要维护

  • 无较复杂的前端需求

  • 用完就走

那么就可以像 demo01 中一样,只需要添加一个 run.py 或者叫做 app.py 文件(反正这是一个启动文件,命名可随意),不论是配置、路由都写在一起就好了。

新建一个项目如下:

  1. sample01

  2. ├── docs

  3. │   └── demo.md

  4. ├── src

  5. │   └── run.py

  6. ├── tests

  7. ├── .gitignore

  8. └──requirements.txt

任意一个 rss 源,假设项目需要将其中的文章标题以及链接提取并展示出来,比如以json格式返回,这属于很简单的功能,可以说只有一段逻辑, run.py 内容如下:

  1. #!/usr/bin/env python

  2. from sanic import Sanic

  3. from sanic.response import json

  4. from feedparser import parse

  5. app = Sanic()

  6. @app.route("/")

  7. async def index(request):

  8.    url = "http://blog.howie6879.cn/atom.xml"

  9.    feed = parse(url)

  10.    articles = feed['entries']

  11.    data = []

  12.    for article in articles:

  13.        data.append({"title": article["title_detail"]["value"], "link": article["link"]})

  14.    return json(data)

  15. if __name__ == "__main__":

  16.    app.run(host="0.0.0.0", port=8000)

访问 http://0.0.0.0:8000/,会返回一串json,如下:

和我们想象地一样,返回了一串 json,接下来,问题升级,我想要将标题链接用页面展示,该怎么弄?

很容易想到,我们需要一个页面模板来承载数据,然后将json数据写入到页面模板中,最后用 jinja2 的 template 将其渲染。

道理我们都懂, Sanic具体需要怎么渲染呢?说白了就是对 jinja2的使用,如下:

  1. #!/usr/bin/env python

  2. from sanic import Sanic

  3. from sanic.response import json, text, html

  4. from feedparser import parse

  5. from jinja2 import Template

  6. app = Sanic()

  7. # 后面会使用更方便的模板引用方式

  8. template = Template(

  9.    """

  10.    <!DOCTYPE html>

  11. <html lang="en">

  12. <head>

  13.    <meta charset="UTF-8">

  14.    <title>rss阅读</title>

  15.    <meta name="viewport" content="width=device-width, initial-scale=1">

  16. </head>

  17. <body>

  18. <article class="markdown-body">

  19.    {% for article in articles %}

  20.    <b><a href="{{article.link}}">{{article.title}}</a></b><br/>

  21.    <i>{{article.published}}</i><br/>

  22.    <hr/>

  23.    {% endfor %}

  24. </article>

  25. </body>

  26. </html>

  27.    """

  28. )

  29. @app.route("/")

  30. async def index(request):

  31.    url = "http://blog.howie6879.cn/atom.xml"

  32.    feed = parse(url)

  33.    articles = feed['entries']

  34.    data = []

  35.    for article in articles:

  36.        data.append({"title": article["title_detail"]["value"], "link": article["link"]})

  37.    return json(data)

  38. @app.route("/html")

  39. async def rss_html(request):

  40.    url = "http://blog.howie6879.cn/atom.xml"

  41.    feed = parse(url)

  42.    articles = feed['entries']

  43.    data = []

  44.    for article in articles:

  45.        data.append(

  46.            {"title": article["title_detail"]["value"], "link": article["link"], "published": article["published"]})

  47.    html_content = template.render(articles=data)

  48.    return html(html_content)

  49. if __name__ == "__main__":

  50.    app.run(host="0.0.0.0", port=8000)

具体结构代码见sample01,运行起来,然后输入 http://0.0.0.0:8000/html 就可以看到被展示出来的页面^_^

假设需要编写前端页面比较多,那么你就需要添加 statics 以及 templates 文件夹用来管理各个界面模块,具体下面会介绍。

项目结构具体说明

当编写的项目过于复杂,我都会将其当做一个第三方包来管理项目中涉及的各种模块,比如 sample02,目录下面你会发现有个 __init__.py 文件,它初始化了当前目录下的应用,然后代码中引用某个函数可以这么写:

  1. from src.views import app

这样,你的应用下面的模块引用起来就会特别方便,就像使用一个第三方模块一样,灵巧且方便。

每个项目的内部分布以及命名可能不一样(甚至目录比应该或多或少),但大体意思可能差不多,下面介绍本次项目 src 下的一些文件目录结构:

  1. sample02

  2. ├── docs

  3. │   └── demo.md

  4. ├── src

  5. │   ├── config # 配置

  6. │   ├── statics # css、js、img

  7. │   ├── templates # Jinja2模板

  8. │   └── views # 路由、逻辑处理

  9. │   ├── __init__.py

  10. │   ├── run.py # 启动文件

  11. ├── tests

  12. └── requirements.txt

此处就可以将 sample02 当成一个包了,实践是检验真理的唯一标准,让我们来试试看:

首先新建文件 /views/rss.py ,具体代码可以看这里 sample02,下面的代码片段可没办法很好的运行:

  1. enable_async = sys.version_info >= (3, 6)

  2. app = Sanic()

  3. # jinjia2 config

  4. env = Environment(

  5.    loader=PackageLoader('views.rss', '../templates'),

  6.    autoescape=select_autoescape(['html', 'xml', 'tpl']),

  7.    enable_async=enable_async)

  8. async def template(tpl, **kwargs):

  9.    template = env.get_template(tpl)

  10.    rendered_template = await template.render_async(**kwargs)

  11.    return html(rendered_template)

  12. @app.route("/html")

  13. async def rss_html(request):

  14.    url = "http://blog.howie6879.cn/atom.xml"

  15.    feed = parse(url)

  16.    articles = feed['entries']

  17.    data = []

  18.    for article in articles:

  19.        data.append(

  20.            {"title": article["title_detail"]["value"], "link": article["link"], "published": article["published"]})

  21.    return await template('rss.html', articles=articles)

这里使用异步的方式引入了 jinja2 ,需要注意的是python版本必须3.6+,否则就得使用同步的方式来引入 jinja2 ,后面章节会继续介绍。

此时启动文件 run.py 只要引入 /views/rss.py 的 app 实例即可:

  1. # !/usr/bin/env python

  2. import sys

  3. import os

  4. sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

  5. from src.views import app

  6. from src.config import CONFIG

  7. app.statics('/statics', CONFIG.BASE_DIR + '/statics')

  8. if __name__ == "__main__":

  9.    app.run(host="0.0.0.0", port=8000)

还有一些css文件这里就不介绍,具体代码请看sample02,运行起来,然后输入 http://0.0.0.0:8000/html 就好,效果如下图:

说明

关于 views templates statics 内部的构造也是值得一写,这就涉及到蓝图 Blueprint ,后面介绍蓝图的时候会进行介绍。

代码地址:demo03


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存