- Flask常见项目结构
- flask程序编写流程
- 编写配置文件configpy
- 编写app初始化工厂函数在app中的init文件中
- 编写我们的管理文件或者说启动文件managepy
- 编写数据库模型在app中的modelspy中
- 编写表单蓝本下的formspy中
- 创建蓝本app下auth包中initpy文件
- 编写视图viewspy
1. Flask常见项目结构
|-Flasky
|app 程序的应用模块,有且只有一个
|-templates/ 网页模板 base.html bootstrap/wtf.html | moment函数
|-static/ 静态文件 js/css
|-auth/
|-__init__.py 实例化验证蓝本auth
|-errors.py
|-forms.py ValidationError/validate_email,validate_username
|-views.py @login_required login_user(user,form.remember.data) logout_user() current_user
|-main/ main蓝本
|-__init__.py 设置main为蓝本 Blueprint flask_blueprint|导入views和errors
|-errors.py 错误处理 main.app_errorhander(404)
|-forms.py 表单 flask_wtf/wtforms/wtforms.validators FlaskForm, StingFied,DataReqirede
|-views.py 视图函数 main.route flask render_template, url_for,request,redirect,flash
|-__init__.py 初始化app,并建立工厂函数,建立扩展 Bootstrap/Moment/Mail/SQLAlchemy/LoginManager
|-email.py 邮箱发送函数 Message(subject,sender,to), msg.body, msg.html/mail.send(msg)
|-models.py 数据库 werkzeug.security/generate_password_hash,check_password_hash @login_manager.user_loader
|-migrations/ 数据迁移仓库 init-migrate-upgrade
|-tests/ 测试模块
|-__init__.py
|-test*.py
|-venv/ 虚拟环境 virtualenv venv|. venv/bin/activate|pip freeze >requirements.txt|pip install -r requirements.txt
|-requirements.txt 依赖包
|-config.py 程序配置信息 SECRET_KEY/SQLALCHEMY_COMMIT_ON_TEARDOWN/BASE_DIR/SQLALCHEMY_DATABASE_URI
|-manage.py 程序管理文件,实例化app. Migrate/Manager add_command('db', MigrateCommand) add_command('shell', Shell(make_context=make_shell_context))
2. flask程序编写流程
1. 编写配置文件config.py
import pymysql
import os
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
class Config():
SQLALCHEMY_TRACK_MODIFICATIONS = True
SECRET_KEY = 'A232K3LJBFSLJ23O0VS024J3KSFKP'
REDIS_URL = "redis://127.0.0.1:6379/0"
UP_DIR = os.path.join(BASE_DIR, "static/uploads/")
FC_DIR = os.path.join(BASE_DIR, "static/uploads/users/")
MAIL_SERVER = 'smtp.126.com'
MAIL_PORT = 25
MAIL_USE_TLS = True
MAIL_USERNAME = '邮箱账号'
MAIL_PASSWORD = '邮箱密码'
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:[email protected]:3306/movie_dev'
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:[email protected]:3306/movie_test'
class ProductConfig(Config):
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:[email protected]:3306/movie_pro'
config = {
'development':DevelopmentConfig,
'testing':TestingConfig,
'product':ProductConfig,
'default': DevelopmentConfig
}
2. 编写app初始化工厂函数(在app中的init文件中)
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from config import config
from flask_login import LoginManager
bootstrap = Bootstrap()
moment = Moment()
mail = Mail()
db = SQLAlchemy()
redis = FlaskRdis()
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
def creat_app(config_name='default'):
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)
redis.init_app(app)
login_manager.init_app(app)
from .main import main
app.register_blueprint(main)
from .auth import auth
app.register_blueprint(auth, url_prefix='/auth')
return app
3. 编写我们的管理文件或者说启动文件(manage.py)
from app import create_app,db
from flask_script import Manager,Shell
from flask_migrate import MigrateCommand,Migrate
from app.models import User
app = create_app('default')
manager= Manager(app)
migrate = Migrate(app,db)
def make_shell_context():
return dict(app=app,db=db,User=User)
manager.add_command('db', MigrateCommand)
manager.add_command('shell', Shell(make_context=make_shell_context))
if __name__ == '__main__':
manager.run()
python manage.py db init 对数据库进行初始化,生成迁移目录
python manage.py db migrate 应用数据库迁移文件, 生成数据表
python manage.py db upgrade 当数据库模型中有改动时,使用此命令进行同步
4. 编写数据库模型(在app中的models.py中)
from . import db
from werkzeug.security import generate_password_hash,check_password_hash
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from flask import current_app
from flask_login import UserMixin
from . import login_manager
class User(UserMixin,db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.String(64))
email = db.Column(db.String(64))
password_hash = db.Column(db.String(128))
confirmed = db.Column(db.Boolean, default=False)
comments = db.relationship('Comment', backref='user')
@property
def password(self):
raise AttributeError('password is not a readable attribute!')
@password.setter
def password(self,pw):
self.password_hash = generate_password_hash(pw)
def verify_password(self,pw):
return check_password_hash(self.password_hash, pw)
def __repr__(self):
return 'User: %r' % self.username
def generate_confirmation_token(self, expiration=3600):
s = Serializer(current_app.config['SECRET_KEY'], expires_in=expiration)
return s.dumps({'confirm':self.id})
def confirm(self,token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except:
return False
if data.get('confirm') != self.id:
return False
self.confirmed = True
db.session.add(self)
return True
def generate_reset_token(self, expiration=3600):
s = Serializer(current_app.config['SECRET_KEY'], expires_in=expiration)
return s.dumps({'reset': self.id})
def reset_pssword(self, token, newpassword):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except:
return False
if data.get('reset') != self.id:
return False
self.password = newpassword
db.session.add(self)
return True
def generate_change_token(self,newemail,expiration=3600):
s = Serializer(current_app.config['SECRET_KEY'], expires_in=expiration)
return s.dumps({'change_email':self.id, 'new_email':newemail})
def change_email(self,token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except:
return False
if data.get('change_email') != self.id:
return False
newemail = data.get("new_email")
if newemail is None:
return False
self.email = newemail
db.session.add(self)
return True
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class Comment(db.Model):
__tablename__ = "comment"
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text)
movie_id = db.Column(db.Integer, db.ForeignKey('movie.id'))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
addtime = db.Column(db.DateTime, index=True, default=datetime.now())
def __repr__(self):
return "" % self.id
class Movie(db.Model):
__tablename__ = "movie"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255), unique=True)
url = db.Column(db.String(255), unique=True)
info = db.Column(db.Text)
logo = db.Column(db.String(255), unique=True)
star = db.Column(db.SmallInteger)
playnum = db.Column(db.BigInteger)
commentnum = db.Column(db.BigInteger)
area = db.Column(db.String(255))
release_time = db.Column(db.Date)
length = db.Column(db.String(100))
addtime = db.Column(db.DateTime, index=True, default=datetime.now())
comments = db.relationship("Comment", backref='movie')
def __repr__(self):
return "" % self.title
from flask_wtf import FlaskForm
from flask_sqlalchemy import SQLAlchemy
from wtforms import (
StringField,
PasswordField,
SubmitField,
FileField,
TextField,
TextAreaField,
SelectField,
SelectMultipleField
)
from wtforms.validators import (
DataRequired,
ValidationError,
EqualTo,
)
class LoginForm(FlaskForm):
"""
管理员登录表单
"""
account = StringField(
label="账号",
validators=[
DataRequired("请输入账号")
],
description="账号",
render_kw={
"class":"form-contorl",
"placeholder":"请输入账号!",
}
)
pwd = PasswordField(
label="密码",
validators=[
DataRequired("请输入密码!")
],
description="密码",
render_kw={
"class":"form-control",
"placeholder":"请输入密码!",
}
)
submit = SubmitField(
"登录",
render_kw={
"class": "btn btn-primary btn-block btn-flat",
}
)
def validate_account(self, field):
account = field.data
admin = User.query.filter_by(name=account).count()
if admin==0:
raise ValidationError("账号不存在!")
url = FileField(
label="文件",
validators=[
DataRequired("请上传文件!")
],
description="文件",
)
info = TextAreaField(
label="简介",
validators=[
DataRequired("请输入简介!")
],
description="简介",
render_kw={
"class":"form-control",
"rows":10
}
)
star = SelectField(
label="星级",
validators=[
DataRequired("请选择星级!")
],
coerce=int,
choices=[(1, '1星'),(2, '2星'),(3, '3星'),(4, '4星'),(5, '5星'),],
description="星级",
render_kw={
"class":"form-control",
}
)
6. 创建蓝本(app下auth包中init.py文件)
from flask import Blueprint
admin = Blueprint("admin", __name__)
from . import views, errors
7. 编写视图views.py
from functools import wraps
def admin_login_req(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if "admin" not in session:
return redirect(url_for("admin.login", next=request.url))
return f(*args, **kwargs)
return decorated_function
@admin.route("/logout/")
@admin_login_req
def logout():
session.pop("admin", None)
session.pop("admin_id", None)
return redirect(url_for("admin.login"))
from flask_login import login_required,logout_user,current_user,login_user
@auth.route('/login/', methods=['POST', 'GET'])
def login():
form = MyLogin()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is not None and user.verify_password(form.password.data):
login_user(user,form.remember.data)
return redirect(request.args.get('next') or url_for('main.index'))
flash('用户名或密码错误!')
form.username.data = ''
return render_template('auth/login.html', form=form)
@auth.route('/log_out/')
@login_required
def logout():
logout_user()
return redirect(url_for('main.index'))
@auth.route('/register/', methods=['GET','POST'])
def register():
form = MyRegister()
if form.validate_on_submit():
user = User(username=form.name.data,
email=form.email.data,
password=form.password.data)
db.session.add(user)
db.session.commit()
token = user.generate_confirmation_token()
sendMail(form.email.data, 'Confirm Your Acount', 'mail/confirm',user= current_user,token=token)
flash('你可以登陆了!')
return redirect(url_for('auth.login'))
return render_template('auth/register.html', form=form)
@auth.before_app_request
def before_request():
if current_user.is_authenticated \
and not current_user.confirmed\
and request.endpoint\
and request.endpoint[:5] != 'auth.':
print request.endpoint
return redirect(url_for('auth.unconfirmed'))
@auth.route('/resetpassword/', methods=['POST','GET'])
def reset_password(token):
form = ResetPassword()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user is None:
flash('邮箱不存在,请重新输入!')
return redirect(url_for('auth.reset_password_request'))
if user.reset_pssword(token,form.newpassword.data):
flash("你的密码已经重设成功!")
return redirect(url_for('auth.login'))
else:
return redirect(url_for('main.index'))
return render_template('auth/resetpassword.html', form=form)
from werkzeug.utils import secure_filename
import uuid
import datetime
def change_filename(filename):
fileinfo = os.path.splitext(filename)
filename = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + \
str(uuid.uuid4().hex) + fileinfo[-1]
return filename
@admin.route("/movie/add/", methods=["GET", "POST"])
@admin_login_req
def movie_add():
form = MovieForm()
if form.validate_on_submit():
data = form.data
file_url = secure_filename(form.url.data.filename)
file_logo = secure_filename(form.logo.data.filename)
if not os.path.exists(app.config["UP_DIR"]):
os.makedirs(app.config["UP_DIR"])
url = change_filename(file_url)
logo = change_filename(file_logo)
form.url.data.save(app.config["UP_DIR"+url])
form.logo.data.save(app.config["UP_DIR" + logo])
movie = Movie(
title = data["title"],
url=url,
info =data["info"],
logo =logo,
star = int(data["star"]),
playnum = 0,
commentnum = 0,
tag_id = int(data["tag_id"]),
area = data["area"],
release_time = data["release_time"],
length = data["length"]
)
db.session.add(movie)
db.session.commit()
flash("添加电影成功!", "ok")
return redirect(url_for("admin.movieadd"))
return render_template("admin/movie_add.html", form=form)
@admin.route("/movie/list//", methods=["GET"])
@admin_login_req
def movie_list(page=None):
if page is None:
page = 1
page_data = Movie.query.join(Tag).filter(
Tag.id == Movie.tag_id
).order_by(
Movie.addtime.desc()
).paginate(page=page, per_page=10)
return render_template("admin/movie_list.html", page_data=page_data)