python web建立flask项目

flask是python的一个web应用框架,django很多人听过,flask比较少见,连创始人一开始写出来只是个笑话而已,对python3支持不太好,flask的好处是微应用,有需求的插件才使用,精简而灵活,由于灵活,要怎么创建一个项目要自己去思考怎么搭建,不像django帮你做完项目搭建的步骤。个人觉得怎么搭建一个项目也是一个学习后台架构的过程。这里不会详细介绍flask和flask各个模块的功能,留作思考与学习。

|-flasky
  |-apps/
    |-templates/
    |-static/
    |-main/
      |-__init__.py
      |-errors.py
      |-forms.py
      |-views.py
    |-__init__.py
    |-email.py
    |-models.py
  |-migrations/
  |-tests/
    |-__init__.py
    |-test*.py
  |-venv/
  |-requirements.txt
  |-config.py
  |-manage.py
  |-command.py

项目有八个顶层目录:

  • app的目录下是放Flask应用
  • migrations目录包含数据库迁移脚本
  • test目录下是放单元测试
  • venv是Python虚拟环境
  • requirements.txt是Python运行的依赖包列表
  • config.py是配置设置脚本
  • manage.py 用于启动应用程序和其他应用程序任务
  • command.py 控制

app层

app下面有main、static、templates三个文件夹以及init.py、email.py、models.py

  • main文件夹用来保存蓝本,此文件夹下 init.py文件里面创建蓝本,(蓝本和程序类似,也可以定义路由。不同的是,在蓝本中定义的路由处于休眠状态,直到蓝本注册到程序上后,路由才真正成为程序的一部分。)main文件夹下views.py用来保存程序的路由,errors.py用来处理错误,forms.py是存放表单定义。

    • init.py

      和路由相关联的蓝图都在休眠状态,只有当蓝图在应用中被注册后,此时的路由才会成为它的一部分。使用定义在全局作用域下的蓝图,定义应用程序的路由就几乎可以和单脚本应用程序一样简单了。

      蓝图可以定义在一个文件或一个包中与多个模块一起创建更结构化的方式。为了追求最大的灵活性,可以在应用程序包中创建子包来持有蓝图。

      # app/main/ _init__.py:创建蓝图_
      from flask import Blueprint
      main = Blueprint('main', __name__) 
      from . import views, errors
      

      蓝图是通过实例化Blueprint类对象来创建的。这个类的构造函数接收两个参数:蓝图名和蓝图所在的模块或包的位置。与应用程序一样,在大多数情况下,对于第二个参数值使用Python的__name__变量。

      应用程序的路由都保存在app/main/views.py模块内部,而错误处理程序则保存在app/main/errors.py中。导入这些模块可以使路由、错误处理与蓝图相关联。重要的是要注意,在app/init.py脚本的底部导入模块要避免循环依赖,因为view.pyerrors.py都需要导入main蓝图。

      # app/ _init__.py:蓝图注册_
      def create_app(config_name): 
          
          from .main import main as main_blueprint 
          app.register_blueprint(main_blueprint)
      
          return app
      
    • errors.py

      在蓝图中写错误处理的不同之处是,如果使用了errorhandler装饰器,则只会调用在蓝图中引起的错误处理。而应用程序范围内的错误处理则必须使用app_errorhandler

      # app/main/errors.py:蓝图的错误处理
      from flask import render_template 
      from . import main
      
      @main.app_errorhandler(404) 
      def page_not_found(e):
          return render_template('404.html'), 404
      
      @main.app_errorhandler(500) 
      def internal_server_error(e):
          return render_template('500.html'), 500
      
    • views.py

      # app/main/views.py:带有蓝图的应用程序路由
      from datetime import datetime
      from flask import render_template, session, redirect, url_for
      
      from . import main
      from .forms import NameForm 
      from ..models import User
      
      @main.route('/',methods = ['POST','GET'])   #请求方式不管是post还是get都执行这个视图函数
      def index():
          form = NameForm()  #表单实例
          if form.validate_on_submit():   #提交按钮是否成功点击
               # 从数据库中查找和表单数据一样的数据,如果有,取第一个数据
              user = User.query.filter_by(username = form.name.data).first()
              if user is None:   #如果数据库中没有对应的数据
                  user = User(username = form.name.data)  #在数据库中对应的表中创建数据
                  db.session.add(user)  #加入到用户会话,以便数据库进行提交
                  session['known'] = False  #这是一个新用户
                  if current_app.config['FLASKY_ADMIN']:  #如果收件人已经定义,则调用发送邮件函数
                      send_email(current_app.config['FLASKY_ADMIN'],'New User','mail/new_user',user = user)
                      flash('The mail has been sent out')
              else:
                  session['known'] = True  #这是一个老用户
              session['name'] = form.name.data   #从表单获取数据
              return redirect(url_for('.index'))
          return render_template('index.html',current_time = datetime.utcnow(),
                                 form = form,name=session.get('name'),known
      

在蓝图中写视图函数有两大不同点。第一,正如之前的错误处理一样,路由装饰器来自于蓝图。第二个不同是url_for()函数的使用。该函数的第一个参数为路由节点名,它给基于应用程序的路由指定默认视图函数。例如,单脚本应用程序中的index()视图函数的URL可以通过url_for('index')来获得。

Flask名称空间适用于来自蓝图的所有节点,这样多个蓝图可以使用相同节点定义视图函数而不会产生冲突。名称空间就是蓝图名(Blueprint构造函数中的第一个参数),所以index()视图函数注册为main.index且它的URL可以通过url_for(main.index)获得。

rl_for()函数同样支持更短格式的节点,省略蓝图名,例如url_for('.index')。有了这个,就可以这样使用当前请求的蓝图了。这实际意味着相同蓝图内的重定向可以使用更短的形式,如果重定向跨蓝图则必须使用带名称空间的节点名。

完成了应用程序页面更改,表单对象也保存在app/main/forms.py模块中的蓝图里面。

  • static**存放静态文件

  • templates用来存放响应的html文件,mail子文件里面的用来保存发送邮件所需的.html和.txt文件

  • init.py文件里面包含create_app()函数,已经app的各种初始化。

    在单个文件中创建应用程序的方式非常方便,但是它有一个大缺点。因为应用程序创建在全局范围,没有办法动态的适应应用配置的更改:脚本运行时,应用程序实例已经创建,所以它已经来不及更改配置。解决这一问题的方法就是将应用程序放入一个工厂函数中来延迟创建,这样就可以从脚本中显式的调用。这不仅给脚本充足的时间来设置配置,也能用于创建多个应用程序实例。

    这个构造函数导入大部分当前需要使用的扩展,但因为没有应用程序实例初始化它们,它可以被创建但不初始化通过不传递参数给它们的构造函数。create_app()即应用程序工厂函数,需要传入用于应用程序的配置名。配置中的设置被保存在config.py中的一个类中,可以使用Flask的app.config配置对象的from_object()方法来直接导入。配置对象可以通过对象名从config字典中选出。一旦应用程序被创建且配置好,扩展就可以被初始化。调用扩展里的init_app()之前先创建并完成初始化工作。

    # app/ _init__.py:应用程序包构造函数
    from flask import Flask, render_template 
    from flask.ext.bootstrap import Bootstrap 
    from flask.ext.mail import Mail
    from flask.ext.moment import Moment
    from flask.ext.sqlalchemy import SQLAlchemy 
    from config import config
    
    bootstrap = Bootstrap()
    mail = Mail()
    moment = Moment()
    db = SQLAlchemy()
    
    def create_app(config_name):
        app = Flask(__name__) 
        app.config.from_object(config[config_name]) 
        config[config_name].init_app(app)
        
        bootstrap.init_app(app)
        mail.init_app(app)
        moment.init_app(app)
        db.init_app(app)
    
        return app
    

  • email.py包含send_email()发送文件函数(异步)

  • models.py包含User和Role两个表定义

config.py 应用配置示例

config.py中含有一个基类Config定义,三个继承类定义DevlopmentConfig、TestingConfig、ProductionConfig和一个字典config

import os
basedir = os.path.abspath(os.path.dirname(__file__))

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string' 
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
    FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>' 
    FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
    
    @staticmethod
    def init_app(app): 
        pass

class DevelopmentConfig(Config): 
    DEBUG = True

    MAIL_SERVER = 'smtp.googlemail.com'
    MAIL_PORT = 587
    MAIL_USE_TLS = True
    MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
    MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD') 
    SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')

class TestingConfig(Config): 
    TESTING = True
    SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')

class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'data.sqlite')

config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'production': ProductionConfig,
    'default': DevelopmentConfig
}

Config基类包含一些相同配置;不同的子类定义不同的配置。额外配置可以在需要的时候在加入。

为了让配置更灵活更安全,一些设置可以从环境变量中导入。例如,SECRET_KEY,由于它的敏感性,可以在环境中设置,但如果环境中没有定义就必须提供一个默认值。

在三个配置中SQLALCHEMY_DATABASE_URI变量可以分配不同的值。这样应用程序可以在不同的配置下运行,每个可以使用不同的数据库。

配置类可以定义一个将应用程序实例作为参数的init_app()静态方法。这里特定于配置的初始化是可以执行的。这里Config基类实现一个空init_app()方法。

在配置脚本的底部,这些不同的配置是注册在配置字典中(config)。将其中一个配置(开发配置)注册为默认配置。

manage.py

包含app创建,manage、migrate初始化,以及make_shell_context()函数在命令行获取上下文,避免频繁导入还有test()函数,用来测试。

# manage.py
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

from wsgi import app
from models.base import db

migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command("db", MigrateCommand)

if __name__ == "__main__":
    manager.run()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容