照着视频写代码!因为底子原因,进度太慢,对其中的部分代码和知识点进行记录

一、设计models:

​ 本着简单的原则设计,只包函user、post、comments 三个表格,user为用户信息,post为文章列表,comments为评论列表,代码如下:

from . import db, login_manager
from flask_login import UserMixin
# import datetime
from datetime import datetime
from markdown import markdown
from werkzeug.security import generate_password_hash, check_password_hash
import bleach


class Role(db.Model):
    __tablename__ = "roles"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=True)
    users = db.relationship("User", backref='roles')

    @staticmethod
    def seed():
        db.session.add_all(map(lambda r: Role(name=r), ["Guest", "Administrator"]))
        db.session.commit()


class User(db.Model, UserMixin):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=True)
    nickname = db.Column(db.String(64), nullable=True)
    email = db.Column(db.String(255))
    password_hash = db.Column(db.String(255), nullable=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

    posts = db.relationship("Post", backref='author')
    commnets = db.relationship("Comment", backref='comment')

    @staticmethod
    def on_created(target, value, oldvalue, initiator):
        target.role = Role.query.filter_by(name="Guest").first()
	#设置禁止读取password
    @property
    def password(self):
        raise AttributeError('Password is not a readable attribute')
	#hash密码
    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)
	#检查密码是否匹配,返回bool值
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)


@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

#监听创建用户事件
db.event.listen(User.name, "set", User.on_created)


class Post(db.Model):
    __tablename__ = "posts"
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(64), nullable=True)#MySQL需要字符串长度,sqlite可以不设
    body = db.Column(db.String(5000), nullable=True)
    body_html = db.Column(db.String(5000), nullable=True)
    created = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    comments = db.relationship("Comment", backref="posts")
    author_id = db.Column(db.Integer, db.ForeignKey("users.id"))

    @staticmethod
    def on_body_changed(target, value, oldvalue, initiator):
        allowed_tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul',
                        'h1', 'h2', 'h3', 'p']
        target.body_html = bleach.linkify(
            bleach.clean(markdown(value, output_format='html'), tags=allowed_tags, strip=True))

#监听修改文章事件
db.event.listen(Post.body, "set", Post.on_body_changed)


class Comment(db.Model):
    __tablename__ = "comments"
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.String(200), nullable=True)
    created = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    post_id = db.Column(db.Integer, db.ForeignKey("posts.id"))
    author_id = db.Column(db.Integer, db.ForeignKey("users.id"))

二、创建wtf表单:登陆、注册、修改密码、新增(修改)、评论

from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField
from wtforms.validators import DataRequired
from flask_pagedown.fields import PageDownField
#新增(修改)表单
class PostForm(FlaskForm):
    title = StringField('标题',validators=[DataRequired()])
    body=PageDownField("正文",validators=[DataRequired()])
    submit=SubmitField("发布")
#评论表单
class CommentForm(FlaskForm):
    body=PageDownField("评论",validators=[DataRequired()])
    submit=SubmitField("发表")
from flask_wtf import FlaskForm as Form
from wtforms import StringField, PasswordField, SubmitField, SelectField
from wtforms.validators import DataRequired, EqualTo, Email, Regexp, Length
from app.models import Role



class LoginForm(Form):
    username = StringField(validators=[DataRequired()], label="用户名")
    password = PasswordField(validators=[DataRequired()], label="密 码")
    submit = SubmitField(label="提交")


class RegistrationFrom(Form):
    email = StringField(u"邮箱地址", validators=[DataRequired(), Length(1, 64), Email()])
    nickname = StringField("昵称", validators=[DataRequired()])
    username = StringField(u"用户名", validators=[DataRequired(), Length(1, 64), Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0,
                                                                                     u"用户名必须由字母开头,字母、数字、下划线或 . 组成")])
    password = PasswordField(u"密码", validators=[DataRequired(), EqualTo('password2', message=u'两次密码不一致')])
    password2 = PasswordField(u"确认密码", validators=[DataRequired()])
    authed = SelectField(u'权限', coerce=int)
    submit = SubmitField(u"立即注册")

    # SelectFrom Data 数据需要注意,多个列表直接在init里面添加
    def __init__(self, *args, **kwargs):
        super(RegistrationFrom, self).__init__(*args, **kwargs)
        self.authed.choices = [(role.id, role.name) for role in Role.query.order_by(Role.id).all()]


class PasswdChangeFrom(Form):
    user = StringField(u"用户名", validators=None)
    nickname = StringField("昵称")
    # old_password = HiddenField(validators=[DataRequired()])
    password = PasswordField(u'新密码', validators=[DataRequired(), Length(6, 16, message="密码长度不合适,请输入6-16位密码"),
                                                 EqualTo("password2", message=u"两次密码不一致")])
    password2 = PasswordField(u"重复密码", validators=[DataRequired()])
    submit = SubmitField(u"提交")

注册和修改密码对安全性要求较高,做了一些限制:邮箱有效性、用户名规则、用户名是否存在、密码长度等

第二弹:对项目的结构进行分析!使用blueprint进行分组,减少单个文件的大小传送门

第三弹:对jinja2 网页模板进行分析,使用bootstrap+wtf快速布局(待续)

第四弹,整个项目的总结以及扩展,qrcode等一些小工具的介绍(...)

项目源码地址:github 教学视频地址:Flask入门