当前位置:   article > 正文

使程序易于维护

使程序易于维护

一。配置文件

代码命令:

视图函数:

env = get_env()
config = configs[env]
app.config.from_object(config)
config.init_app(app)

配置文件(config.py):

import os

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


class Config:
    SECRET_KEY =
'Chapter6'
   
SQLALCHEMY_TRACK_MODIFICATIONS = False

   
@staticmethod
   
def init_app(app):
       
pass

class
DevelopmentConfig(Config):
    DEBUG =
True
   
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')

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

class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI =
'sqlite:///' + os.path.join(basedir, 'data.sqlite')

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

效果图:

操作步骤: 

首先在pychram的终端输入:

操作代码命令:

Set FLASK_ENV=development

然后在pychram上找到“运行\配置调试”,并点击;

在“运行\配置调试”界面中,点击“编辑配置”;

在“编辑配置”界面中,点击“添加新配置”;

在“添加新配置”界面中点击“Flask 服务器”;

在新建的flask服务器界面中,找到flask环境,将其改为指定内容;

改正内容:testing

常见问题:

暂无。

注意事项:

暂无。

二。项目结构

应用管理入口

代码命令:

应用管理入口模板文件(manage.py):

from flask.helpers import get_env
from flask_migrate import MigrateCommand
from flask_script import Manager, Shell

from app import create_app, db, dh
from app.commands import register_manage_commands

# 根据相应的环境创建应用实例,并初始化 Manager
env = get_env()
app = create_app(env)
manager = Manager(app)


def make_shell_context():
    context =
dict(
       
db=db,
       
dh=dh,
    )
   
# 将所有模型注册到交互 Shell
   
context.update((cls.__name__, cls) for cls in dh.get_all_model_classes())
   
return context

# 注册指令到 Manager
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command(
'db', MigrateCommand)

register_manage_commands(manager)


if __name__ == '__main__':
    manager.run()

应用“工厂”函数

代码命令:

应用“工厂”函数(app\__init__.py)代码:

from flask import Flask
from flask_bootstrap import Bootstrap
from flask_login import LoginManager
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from config import configs
from .database import DatabaseHelper
from .errors import register_app_errors

# 对象在函数外创建,以便于在其它文件中引用。
bootstrap = Bootstrap()
db = SQLAlchemy()
migrate: Migrate
login_manager = LoginManager()


# 创建数据库助手类实例
dh = DatabaseHelper(db)

# 将创建应用的流程封装成函数,便于管理。
def create_app(env):
    app = Flask(__name__)

   
# 加载应用配置
    config = configs.get(env)
    app.config.from_object(config)
    config.init_app(app)

   
# 初始化数据库操作实例
    db.init_app(app)

   
# 初始化 Flask-Migrate
   
global migrate
    migrate = Migrate(app, db)
    migrate.init_app(app,
render_as_batch=True)

   
# 初始化 Flask-BootStrap
   
bootstrap.init_app(app)
    bootstrap_cdns = app.extensions[
'bootstrap']['cdns']
    bootstrap_cdns[
'bootstrap'] = bootstrap_cdns['local']
    bootstrap_cdns[
'jquery'] = bootstrap_cdns['local']

   
# 初始化 Flask-Login
   
login_manager.login_view = 'user.login'
   
login_manager.login_message_category = 'danger'
   
login_manager.login_message = '请登录后再进行操作'
   
login_manager.init_app(app)

   
# 将各模块的蓝图(Blueprint)注册到应用实例
    from .user import user
   
# url_prefix参数可以为用户模块添加上级位置
    app.register_blueprint(user, url_prefix='/user')

   
from .admin import admin, admin_user
    app.register_blueprint(admin,
url_prefix='/admin')
    app.register_blueprint(admin_user,
url_prefix='/admin/user')

   
from .portal import portal
    app.register_blueprint(portal)

   
# 将所有模型类注册到模板全局变量,以便于后续调用
    # 注册模型须放置在所有模型加载之后,否则将获取不到相应模型
    for cls in dh.get_all_model_classes():
        app.add_template_global(cls)

   
# 注册应用错误视图
    register_app_errors(app)

   
return app

数据库(app\database.py)代码:

from flask_sqlalchemy import BaseQuery, Model, SQLAlchemy

# 定义所有模型的基类
from sqlalchemy.orm import Session

class BaseModel(Model):
   
# 声明 query 属性的类型,以获得代码提示补全
    query: BaseQuery

   
# 定义模型初始化方法
    def __init__(self, **kwargs):
       
# 将参数传入父类初始化方法
        super(BaseModel, self).__init__(**kwargs)

   
# 定义对象输出格式,方便输出预览
    def __repr__(self):
        fields = []
       
# 获取对象原始数据
        for k, v in self.__dict__.items():
           
# 只输出相关属性
            if k[0] != '_':
               
# 防止模型关联后无限递归
                if isinstance(v, BaseModel):
                    fields.append(
'%s=<%s ...>' % (k, v.__class__.__name__))
               
elif isinstance(v, str):
                    fields.append(
"%s='%s'" % (k, v))
               
else:
                    fields.append(
'%s=%a' % (k, v))

       
# 拼接显示结果
        result = '<%s %s>' % (self.__class__.__name__, ' '.join(fields))
       
return result

# 定义数据库助手类,以便于进行一些常用操作
class DatabaseHelper:
   
def __init__(self, db: SQLAlchemy):
       
self._db = db

   
# 获取所有基于 BaseModel 类的模型,以便于注册到交互 Shell
    def get_all_model_classes(self):
        classes = []
       
for cls in self._db.Model._decl_class_registry.values():
           
if hasattr(cls, '__tablename__') and issubclass(cls, BaseModel):
                classes.append(cls)
       
return classes

   
# 定义获取 db.session 的属性,以获得完整的代码补全
    @property
   
def session(self) -> Session:
       
return self._db.session

常见问题:

ModuleNotFoundError: No module named 'flask_login'

问题原因:

因为在其Python 环境中没有安装 flask_login 这个模块。

解决方法:

pip install Flask-Login

ImportError: cannot import name 'BaseResponse' from 'werkzeug.wrappers'

问题原因:

这个错误指从 werkzeug.wrappers 模块中导入 BaseResponse 类,但是在该模块中找不到这个名称。

解决方法:

pip install Werkzeug==2.0.3

ImportError: cannot import name 'url_encode' from 'werkzeug'

问题原因:

这个错误指在werkzeug的当前版本过高。

解决方法:

pip install Werkzeug==0.16.0

注意事项:

暂无。

三。模块化开发

使用蓝图

代码命令:

蓝图初始化(app/user/__init__.py):

from flask import Blueprint

user = Blueprint(
'user', __name__)

# 加载相关的模型、视图、错误视图、公共方法
from . import models, views, errors, common

数据模型(app/user/models.py):

from flask_login import UserMixin

from app import db
from app.database import BaseModel

# 使用户模型继承 Flask-Login 的标准用户实现(UserMixin
class UserModel(UserMixin, db.Model, BaseModel):
    __tablename__ =
'user'

   
id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
    username = db.Column(db.VARCHAR,
unique=True)
    password = db.Column(db.VARCHAR)
    is_admin = db.Column(db.BOOLEAN,
default=False, nullable=False)

    info = db.relationship(
'UserInfoModel', backref='user', uselist=False, cascade='all')

class UserInfoModel(db.Model, BaseModel):
    __tablename__ =
'user_info'

   
user: UserModel

    user_id = db.Column(db.INTEGER, db.ForeignKey(UserModel.id),
primary_key=True)
    phone = db.Column(db.VARCHAR)
    email = db.Column(db.VARCHAR)
    introduce = db.Column(db.TEXT)

表单模型(app/user/forms.py):

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, TextAreaField, BooleanField
from wtforms.validators import DataRequired, Email, Optional, Regexp

class LoginForm(FlaskForm):
    username = StringField(
label='用户名', validators=[DataRequired()])
    password = PasswordField(
label='密码', validators=[DataRequired()])
    remember = BooleanField(
label='记住该用户')
    submit = SubmitField(
label='登录')


class RegisterForm(FlaskForm):
    username = StringField(
label='用户名', validators=[DataRequired()])
    password = PasswordField(
label='密码', validators=[DataRequired()])
   
# 此处使用 11位数字 的正则表达式检测手机号
    phone = StringField(label='手机号', validators=[Optional(), Regexp(r'\d{11}')])
    email = StringField(
label='邮箱', validators=[Optional(), Email()])
    introduce = TextAreaField(
label='自我介绍')
    submit = SubmitField(
label='注册')

视图函数模型(app/user/views.py):

from flask import render_template, request, redirect, flash, abort
from flask_login import login_user, logout_user, login_required
from app import dh
from . import user
from .common import get_current_user
from .forms import *
from .models import *

# 用户注册
@user.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm()

   
if request.method == 'POST' and form.validate_on_submit():
        item = UserModel(
           
username=form.username.data,
           
password=form.password.data,
           
info=UserInfoModel(
               
phone=form.phone.data,
               
email=form.email.data,
               
introduce=form.introduce.data,
            ),
        )

       
try:
            dh.session.add(item)
            dh.session.commit()

            flash(
'注册成功', 'success')
           
return redirect('login')
       
except Exception as e:
            flash(
'注册失败 - %s' % e, 'danger')
           
return abort(500)
   
else:
       
return render_template('user/register.html', form=form)

# 用户登录
@user.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()

   
if request.method == 'POST' and form.validate_on_submit():
       
try:
            item = UserModel.query.filter_by(
username=form.username.data, password=form.password.data).first()
           
if item is not None:
               
# 使用Flask-Login完成用户登录认证
                login_user(item)
                flash(
'登录成功', 'success')
               
return redirect(request.path)
           
else:
               
raise Exception('用户名或密码错误')
       
except Exception as e:
            flash(
'登录失败 - %s' % e, 'danger')
           
return abort(500)
   
else:
       
return render_template('user/login.html', form=form)

# 查看用户信息
@user.route('/<int:id>')
def view(id: int):
   
try:
        item = UserModel.query.get(id) 
# type: UserModel
       
if item is not None:
           
return render_template('user/view.html', item=item)
       
else:
           
raise Exception('用户不存在')
   
except Exception as e:
        flash(
'查看用户 - %s' % e, 'danger')
       
return abort(404)

@user.route('/logout')
@login_required
def logout():
   
if logout_user():
        flash(
'注销成功', 'success')
       
return redirect('/user/login')

# 查看用户信息(当前用户)
@user.route('/')
@login_required
def view_current():
    item = get_current_user()
   
return render_template('user/view.html', item=item)

“工厂”函数模型(app/__init__.py)修改的代码部分:(已改)

# 将各模块的蓝图(Blueprint)注册到应用实例
from .user import user
# url_prefix参数可以为用户模块添加上级位置
app.register_blueprint(user, url_prefix='/user')

操作步骤:

首先在打开pychram的终端;

在终端中输入指定运行的环境代码指令;

代码命令:

(.venv) PS C:\Users\钟离\PycharmProjects\flaskProject1> set FLASK_ENV='development'

然后输入生成数据库的代码指令;

代码命令:

(.venv) PS C:\Users\钟离\PycharmProjects\flaskProject1> python manage.py db init

仍然在终端中输入更新数据库结构的代码指令;

代码命令:

(.venv) PS C:\Users\钟离\PycharmProjects\flaskProject1> python manage.py db migrate

还是在终端中输入用于匹配数据库迁移版本的代码指令;

代码命令:

(.venv) PS C:\Users\钟离\PycharmProjects\flaskProject1> python manage.py db upgrade

最后在终端中输入执行应用管理入口文件(manage.py)文件的代码指令;

代码命令:

(.venv) PS C:\Users\钟离\PycharmProjects\flaskProject1> python manage.py runserver

效果图:

用户注册界面

用户登录界面

查看用户信息界面

子模块

代码命令:

管理模块(admin/__init__.py)初始化:

from flask import Blueprint

admin = Blueprint(
'admin', __name__)

from .user import user as admin_user

from . import views

用户管理模块(admin/user/__init__.py)初始化:

from flask import Blueprint

user = Blueprint(
'admin.user', __name__)

from . import views, errors

“工厂”函数(app/__init__.py)添加的代码部分:(已添加)

# 将各模块的蓝图(Blueprint)注册到应用实例
from .user import user
# url_prefix参数可以为用户模块添加上级位置
app.register_blueprint(user, url_prefix='/user')

from .admin import admin, admin_user
app.register_blueprint(admin,
url_prefix='/admin')
app.register_blueprint(admin_user,
url_prefix='/admin/user')

from .portal import portal
app.register_blueprint(portal)

效果图:

后台界面

用户管理功能

常见问题:

Error: Directory migrations already exists and is not empty

问题原因:

数据库以存在,但没有及时更新。

解决方法:

将删除migrations文件夹,及创建的数据库,然后重新执行一遍操作步骤的对应命令。

注意事项:

注册用户前先要检查数据库是否存在,且是否可以正常使用。

四。Flask-Login

代码命令:

应用初始化文件(app/__init__.py)添加的代码部分:(已添加)

# 初始化 Flask-Login
login_manager.login_view = 'user.login'
login_manager.login_message_category = 'danger'
login_manager.login_message = '请登录后再进行操作'
login_manager.init_app(app)

用户模型()添加继承Flask-Login的代码部分:(已添加)

class UserModel(UserMixin, db.Model, BaseModel):
    __tablename__ =
'user'

   
id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
    username = db.Column(db.VARCHAR,
unique=True)
    password = db.Column(db.VARCHAR)
    is_admin = db.Column(db.BOOLEAN,
default=False, nullable=False)

    info = db.relationship(
'UserInfoModel', backref='user', uselist=False, cascade='all')

公共方法模块(user/common.py)添加用户加载器的代码部分:

from flask_login import current_user

from app import login_manager

# 用于 Flask-Login 获取用户
from .models import UserModel

@login_manager.user_loader
def _load_user(id: int):
   
return UserModel.query.get(id)

# 获取当前用户,指定类型以便于获取代码补全
def get_current_user() -> UserModel:
   
return current_user

视图函数(user/views.py):

from flask_login import login_user, logout_user, login_required

# 用户登录
@user.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()

   
if request.method == 'POST' and form.validate_on_submit():
       
try:
            item = UserModel.query.filter_by(
username=form.username.data, password=form.password.data).first()
           
if item is not None:
               
# 使用Flask-Login完成用户登录认证
                login_user(item)
                flash(
'登录成功', 'success')
               
return redirect(request.path)
           
else:
               
raise Exception('用户名或密码错误')
       
except Exception as e:
            flash(
'登录失败 - %s' % e, 'danger')
           
return abort(500)
   
else:
       
return render_template('user/login.html', form=form)

要求认证访问

代码命令:

视图函数(user/views.py):

from flask_login import login_user, logout_user, login_required
from .common import get_current_user

@user.route('/logout')
@login_required
def logout():
   
if logout_user():
        flash(
'注销成功', 'success')
       
return redirect('/user/login')

# 查看用户信息(当前用户)
@user.route('/')
@login_required
def view_current():
    item = get_current_user()
   
return render_template('user/view.html', item=item)

查看用户登录信息模板(app/templates/common/nav.html):

<nav class="navbar navbar-inverse navbar-static-top">
    <div
class="container">
        <div
class="navbar-header">
            <button
type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                   
aria-expanded="false" aria-controls="navbar">
                <span
class="sr-only">Toggle navigation</span>
                <span
class="icon-bar"></span>
                <span
class="icon-bar"></span>
                <span
class="icon-bar"></span>
            </button>
            <a
class="navbar-brand" href="/">模块化开发</a>
        </div>
        <div
id="navbar" class="collapse navbar-collapse">
            <ul
class="nav navbar-nav">
                <li
class="dropdown">
                    <a
href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                      
aria-expanded="true">
                       
后台管理
                        <span class="caret"></span></a>
                    <ul
class="dropdown-menu">
{#                        <li><a href="{{ url_for('admin.index') }}">后台首页</a></li>#}
{#                        <li><a href="{{ url_for('admin.user.index') }}">
用户管理</a></li>#}
                         
<li><a>后台首页</a></li>
                          <li><a>
用户管理</a></li>
                    </ul>
                </li>
            </ul>
            <ul
class="nav navbar-nav navbar-right">
                <li
class="dropdown">
                   
{% if current_user.is_anonymous %}
                       
{# 如果用户没有登录,即显示游客用的菜单 #}
                       
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                          
aria-expanded="true">
                           
游客
                            <span class="caret"></span></a>
                        <ul
class="dropdown-menu">
                            <li><a
href="{{ url_for('user.login') }}">登录</a></li>
                            <li><a
href="{{ url_for('user.register') }}">注册</a></li>
                        </ul>
                   
{% else %}
                       
{# 登录以后显示用户名 #}
                       
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                          
aria-expanded="true">
                           
{{ current_user.info.nickname or current_user.username }}
                           
<span class="caret"></span></a>
                        <ul
class="dropdown-menu">
                            <li><a
href="{{ url_for('user.view_current') }}">用户信息</a></li>
                            <li
class="divider"></li>
                            <li><a
href="{{ url_for('user.logout') }}">注销</a></li>
                        </ul>
                    
{% endif %}
               
</li>
            </ul>
        </div>
    </div>
</nav>

效果图:

查看当前登录用户信息的效果

注销用户后,查看当前登录用户信息的效果

管理员认证访问

代码命令:

“admin_required”装饰器(admin/common.py):

from functools import wraps

from flask import abort, flash
from flask_login import login_required

from app.user.common import get_current_user

def admin_required(func):
   
@wraps(func)
   
def decorated_view(*args, **kwargs):
       
if not get_current_user().is_admin:
            flash(
'拒绝访问', 'danger')
           
return abort(403)
       
return func(*args, **kwargs)

   
return login_required(decorated_view)

后台首页(admin/views.py):

from flask import render_template
from . import admin
from .common import admin_required

@admin.route('/')
@admin_required
def index():
   
return render_template('admin/index.html')

效果图:

普通用户访问后台

管理员用户访问后台

操作步骤:

首先创建一个名为“admin”的用户;

然后在pychram的终端中输入代码指令;

代码命令:

(.venv) PS C:\Users\钟离\PycharmProjects\flaskProject1> python manage.py shell

再然后在打开的shell命令框中输入代码指令;

代码命令:

>>> admin = UserModel.query.filter_by(username='admin').first()

>>> admin.is_admin = True

>>> db.session.add(admin)

>>> db.session.commit()  

>>> exit()

最后重新进入浏览器中访问“http://127.0.0.1:5000/admin/”即可。

常见问题:

暂无。

注意事项:

需要先建一个名为“admin”的用户,不然无法指定“admin”用户为管理员用户。

五。管理员注册

代码命令:

管理指令(app/commands.py):

from flask_script import Manager

from app import dh
from app.user.models import UserModel, UserInfoModel

def register_manage_commands(manager: Manager):
   
@manager.command
   
def create_admin():
        username =
input('Input Username:')
        password =
input('Input Password:')
        item = UserModel(
username=username, password=password, is_admin=True,
                        
info=UserInfoModel(introduce='Create By Manager.'))
        dh.session.add(item)
        dh.session.commit()

应用管理入口(manage.py):

from flask.helpers import get_env
from flask_migrate import MigrateCommand
from flask_script import Manager, Shell
from app import create_app, db, dh
from app.commands import register_manage_commands

# 根据相应的环境创建应用实例,并初始化 Manager
env = get_env()
app = create_app(env)
manager = Manager(app)


def make_shell_context():
    context =
dict(
       
db=db,
       
dh=dh,
    )
   
# 将所有模型注册到交互 Shell
   
context.update((cls.__name__, cls) for cls in dh.get_all_model_classes())
   
return context

# 注册指令到 Manager
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command(
'db', MigrateCommand)

register_manage_commands(manager)


if __name__ == '__main__':
    manager.run()

效果图:

执行代码:

(.venv) PS C:\Users\钟离\PycharmProjects\flaskProject1> python manage.py create_admin

使用Manager注册管理员

常见问题:

暂无。

注意事项:

暂无。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/587186
推荐阅读
相关标签
  

闽ICP备14008679号