form表单及数据库表操作

1、form表单验证  wtforms

需要下载包 pip3 install wtforms

简单使用:

winform 程序 链接mysql form连接数据库_User

winform 程序 链接mysql form连接数据库_html_02

from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms import validators
from wtforms.fields import simple
from wtforms import widgets

app = Flask(__name__, template_folder='templates')
# app.debug = True

class LoginForm(Form):
    name = simple.StringField(
        # 标签名
        label='用户名',
        validators=[
            # 必须要有,错误信息
            validators.DataRequired(message='用户名不能为空!'),
            # 校验规则
            validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
        ],
        widget=widgets.TextInput(),  # 页面上显示的类型text或者password
        render_kw={'class': 'outter'}  # 给标签设置类属性
    )
    pwd = simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码不能为空!'),
            validators.Length(min=8, message='密码长度必须大于%(min)d'),
            # 可以使用正则
            validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
                              message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
            ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'password'}
    )

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'GET':
        form = LoginForm()
        return render_template('login.html', form=form)
    else:
        form = LoginForm(formdata=request.form)
        if form.validate():
            print('用户提交的是:', form.data)
        else:
            print(form.errors)
        return render_template('login.html', form=form)
if __name__ == '__main__':
    app.run()

login.py

winform 程序 链接mysql form连接数据库_User

winform 程序 链接mysql form连接数据库_html_02

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<form method="post">
    <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>

    <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
    <input type="submit" value="提交">
</form>
</body>
</html>

login.html

winform 程序 链接mysql form连接数据库_winform 程序 链接mysql_05

常规使用:

winform 程序 链接mysql form连接数据库_User

winform 程序 链接mysql form连接数据库_html_02

from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets

app = Flask(__name__, template_folder='templates')
app.debug = True


# 需要用什么字段导什么字段来定义
class RegisterForm(Form):
    # 自定义钩子校验,也可以用validators.EqualTo直接校验
    def validate_pwd_confirm (self, field):
        """
        自定义pwd_confirm字段规则,例:与pwd字段是否一致
        :param field:
        :return:
        """
        # 最开始初始化时,self.data中已经有所有的值

        if field.data != self.data['pwd']:
            raise validators.ValidationError("密码不一致") # 继续后续验证
            #raise validators.StopValidation("密码不一致123123123")  # 不再继续后续验证

    name = simple.StringField(
        label='用户名',
        validators=[
            validators.DataRequired()
        ],
        widget=widgets.TextInput(),
        render_kw={'class': 'form-control'},
        default='zack'
    )

    pwd = simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码不能为空.')
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    pwd_confirm = simple.PasswordField(
        label='重复密码',
        validators=[
            #validators.DataRequired(message='重复密码不能为空.'),
            validate_pwd_confirm,
            # 校验等于pwd
            validators.EqualTo('pwd', message="两次密码输入不一致")
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    email = html5.EmailField(
        label='邮箱',
        validators=[
            validators.DataRequired(message='邮箱不能为空.'),
            validators.Email(message='邮箱格式错误')
        ],
        widget=widgets.TextInput(input_type='email'),
        render_kw={'class': 'form-control'}
    )

    gender = core.RadioField(
        label='性别',
        choices=(
            (1, '男'),
            (2, '女'),
        ),
        coerce=int # “1” “2”
     )
    city = core.SelectField(
        label='城市',
        choices=(
            ('bj', '北京'),
            ('sh', '上海'),
        )
    )

    hobby = core.SelectMultipleField(
        label='爱好',
        choices=(
            (1, '篮球'),
            (2, '足球'),
        ),
        coerce=int
    )

    favor = core.SelectMultipleField(
        label='喜好',
        choices=(
            (1, '篮球'),
            (2, '足球'),
        ),
        widget=widgets.ListWidget(prefix_label=False),
        option_widget=widgets.CheckboxInput(),
        coerce=int,
        default=[1, 2]
    )

    # def __init__(self, *args, **kwargs):
    #     super(RegisterForm, self).__init__(*args, **kwargs)
    #     self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))




@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'GET':
        form = RegisterForm()  # initial
        return render_template('register.html', form=form)
    else:
        form = RegisterForm(formdata=request.form)
        if form.validate():
            print('用户提交数据通过格式验证,提交的值为:', form.data)
        else:
            print(form.errors)
        return render_template('register.html', form=form)



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

register.py

winform 程序 链接mysql form连接数据库_User

winform 程序 链接mysql form连接数据库_html_02

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户注册</h1>
<form method="post" novalidate style="padding:0  50px">
    {% for field in form %}
    <p>{{field.label}}: {{field}} {{field.errors[0] }}</p>
    {% endfor %}
    <input type="submit" value="提交">
</form>
</body>
</html>

register.html

winform 程序 链接mysql form连接数据库_winform 程序 链接mysql_10

 

 2、flask的ORM  sqlalchemy

pip3 install sqlalchemy

SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系对象映射进行数据库操作,将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果

1、ORM简单使用

1、创建和删除表

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String

Base = declarative_base()


class User(Base):
    __tablename__ = 'user'  # 数据库表名
    id = Column(Integer, primary_key=True)  # 主键
    name = Column(String(32), index=True, nullable=False)  # 索引,不能为空
    age = Column(Integer)

    def __repr__(self):
        return self.name


# 根据类创建数据库表
def init_db():
    engine = create_engine(
        # 数据库名+链接数据库://root:密码@ip:port/数据库名?charset=字符集
        'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8',
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.create_all(engine)


# 根据类删除数据库表
def drop_db():
    engine = create_engine(
        # 数据库名+链接数据库://root:密码@ip:port/数据库名?charset=字符集
        'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8',
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.drop_all(engine)


if __name__ == '__main__':
    # drop_db()  # 删除表
    init_db()  # 创建表

2、给表添加数据

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from models import User

# 1 连接引擎
engine = create_engine(
    'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8',
)
Connection = sessionmaker(bind=engine)

# 2 每次执行数据库操作时都要连接,后面一般用session=Connection()
conn = Connection()

# 1 单增
# 创建数据
obj = User(name='Immy', age=18)
# 将数据添加到表中
conn.add(obj)

# 2 群增
conn.add_all([
    User(name='zack', age=18),
    User(name='vicky', age=20),
    # 或者其他表也行
    # Order(name='aaa', price=100)
])

# 3 删除(整体删)
conn.query(User).delete()

# 4 改(传字典的形式)
# 方式1:群改
conn.query(User).update({'name':'tank', 'age': 17})
# 方式2:群改,类似Django的f查询,加字符串的时候必需要synchronize_session=False
conn.query(User).update({User.name:User.name+' is sb', 'age': 12},synchronize_session=False)
# 方式3:加数字的时候可以直接加,也可以设置synchronize_session=False
conn.query(User).update({User.age:User.age+10})

# 5 查(查不需要commit,也能拿到结果)
# 打印SQL语句
res = conn.query(User)
print(res)

# 查所有,得到列表
res = conn.query(User).all()
print(res)

# 查单条记录
res = conn.query(User).first()
print(res.age)

# 查询哪些字段,.label并将字段取别名隐藏本身名字
res = conn.query(User.age, User.name.label('yhm')).first()
print(res.age, res.yhm)

# 6 过滤用filter(传表达式)或者用filter_by(传参数)
res = conn.query(User).filter(User.name == 'zack').first()
res = conn.query(User).filter_by(name='zack').first()
print(res)

# 利用filter过滤修改
conn.query(User).filter(User.name == 'vicky').update({User.name:'wxm', 'age': 1})
res = conn.query(User).filter(User.name == 'wxm').first()
print(res.age, res.name)

# 3 必须提交才能生效
conn.commit()
# 4 关闭连接,将连接放回连接池
conn.close()

3、单表查询

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from models import User

engine = create_engine(
    'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8'
)
Connection = sessionmaker(bind=engine)
# 一般都用session
session = Connection()

# 1 表达式的and条件用 , 连接
ret = session.query(User).filter(User.name=='zack', User.age==18).first()
print(ret)

# 2 表达式的between条件
ret = session.query(User).filter(User.age.between(18, 20)).all()
print(ret)

# 3 sql查询的in_操作,相当于Django中的__in
ret = session.query(User).filter(User.id.in_([7,8,9])).all()
print(ret)

# 4 查询取反 ~
ret = session.query(User).filter(~User.id.in_([7,8])).all()
print(ret)

# 5 or查询and查询,or_,and_需要导入
from sqlalchemy import or_, and_

ret = session.query(User).filter(or_(User.id == 7, User.name == 'wxm')).all()
res = session.query(User).filter(and_(User.id == 7, User.name == 'Immy')).all()
print(ret, res)
# or_与and_联合使用
ret =session.query(User).filter(or_(User.id == 7,
                                    and_(User.age==18, User.name=='zack'))).all()
print(ret)

# 6 like查询
# 必须以I开头
ret = session.query(User).filter(User.name.like("I%")).all()
print(ret)
# 第二个字母是m,_m
ret = session.query(User).filter(User.name.like("_m%")).all()
print(ret)
# 不以I开头的,~
ret = session.query(User).filter(~User.name.like("I%")).all()
print(ret)

# 7 排序,order_by
# 降序desc
ret = session.query(User).filter(User.id>1).order_by(User.id.desc()).all()
print(ret)
# 升序,asc
ret = session.query(User).filter(User.id>1).order_by(User.id.asc()).all()
print(ret)
# 先升序再降序,用 , 隔开
# 先按年龄升序如果有相同年龄的再按id降序
ret =session.query(User).filter(User.id>=1).order_by(User.age.asc(), User.id.desc()).all()
print(ret)

# 8 分组查询
# 按照年龄分组
ret = session.query(User).group_by(User.name).all()
print(ret)
# 分组后要聚合操作需要用func
from sqlalchemy.sql import func
# 选出年龄最小大于等于18的组
ret = session.query(User).group_by(User.name).having(func.min(User.age) >= 18).all()
print(ret)
# 选出组内最小年纪大于等于18的组,查询组内的最小年龄与最大年龄的和与名字
ret = session.query(User.name,
                    func.min(User.age),
                    func.max(User.age),
                    func.sum(User.age)
                    ).group_by(User.name).having(func.min(User.age) >= 18).all()
print(ret)

2、ORM表关系

1、一对多

winform 程序 链接mysql form连接数据库_User

winform 程序 链接mysql form连接数据库_html_02

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship

Base = declarative_base()


class Hobby(Base):
    __tablename__ = 'hobby'
    id = Column(Integer, primary_key=True)
    catption = Column(String(32), default='洗脚')

class Person(Base):
    __tablename__ = 'person'
    nid = Column(Integer, primary_key=True)
    name = Column(String(32))
    # 外键hobby值tablename而不是Hobby类名,
    hobby_id = Column(Integer, ForeignKey('hobby.id'))
    # 更新数据库没有关系,不会增加新字段,只能用于快速连表查询
    # relationship的第一个参数,是类名,第二个参数backref,用于反向查询
    hobby = relationship("Hobby", backref="pres")

    def __repr__(self):
        return self.name

# 根据类创建数据库表
def init_db():
    engine = create_engine(
        # 数据库名+链接数据库://root:密码@ip:port/数据库名?charset=字符集
        'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8',
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.create_all(engine)


# 根据类删除数据库表
def drop_db():
    engine = create_engine(
        # 数据库名+链接数据库://root:密码@ip:port/数据库名?charset=字符集
        'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8',
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.drop_all(engine)


if __name__ == '__main__':
    # drop_db()  # 删除表
    init_db()  # 创建表

models.py

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from models import Hobby, Person

engine = create_engine(
    'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8'
)
Connection = sessionmaker(bind=engine)
# 一般都用session
session = Connection()

# 1 单独给两张表添加数据,不使用关联关系
session.add_all([
    Hobby(catption='吃鸡'),
    Hobby(catption='学习'),
    Person(name='vicky', hobby_id=1),
    Person(name='zack', hobby_id=2)
])

# 2 用关联关系 添加
person = Person(name='Mr沈', hobby=Hobby(catption='学习'))
session.add(person)

hobby = Hobby(catption='旅游')
hobby.pres = [Person(name='Immy'), Person(name='zack')]
session.add(hobby)

# 3 正向查询
pr = session.query(Person).filter(Person.name=='vicky').first()
print(pr)
print(pr.hobby.catption)

# 4 反向查询
ver = session.query(Hobby).filter(Hobby.catption=='旅游').first()
print(ver.catption)
print(ver.pres)

# 5 如果不用relationship连表,我们自己连表查询,isouter=True表示是left join,不填默认为inner join
person_list = session.query(Hobby).join(Person, Person.hobby_id == Hobby.id, isouter=True).all()
print(person_list)

session.commit()
session.close()

2、多对多

winform 程序 链接mysql form连接数据库_User

winform 程序 链接mysql form连接数据库_html_02

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship

Base = declarative_base()

# 一个男孩可以喜欢多个女孩,一个女孩也可以喜欢多个男孩
class Boy2Girl(Base):
    __tablename__ = "boy2girl"
    id = Column(Integer, primary_key=True)
    girl_id = Column(Integer, ForeignKey("girl.id"))
    boy_id = Column(Integer, ForeignKey("boy.id"))


class Girl(Base):
    __tablename__ = "girl"
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable=False)

    def __repr__(self):
        return self.name


class Boy(Base):
    __tablename__ = "boy"
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable=False)
    #secondary=boy2girl 中间表的表名
    girl = relationship("Girl",secondary="boy2girl",backref = "boys")

    def __repr__(self):
        return self.name


# 根据类创建数据库表
def init_db():
    engine = create_engine(
        # 数据库名+链接数据库://root:密码@ip:port/数据库名?charset=字符集
        'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8',
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.create_all(engine)


# 根据类删除数据库表
def drop_db():
    engine = create_engine(
        # 数据库名+链接数据库://root:密码@ip:port/数据库名?charset=字符集
        'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8',
        max_overflow=0,  # 超过连接池大小外最多创建的连接
        pool_size=5,  # 连接池大小
        pool_timeout=30,  # 池中没有线程最多等待的时间,否则报错
        pool_recycle=-1  # 多久之后对线程池中的线程进行一次连接的回收(重置)
    )
    Base.metadata.drop_all(engine)


if __name__ == '__main__':
    # drop_db()  # 删除表
    init_db()  # 创建表

models.py

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from models import Boy, Girl, Boy2Girl

engine = create_engine(
    'mysql+pymysql://root:2694@127.0.0.1:3306/py13?charset=utf8'
)
Connection = sessionmaker(bind=engine)
# 一般都用session
session = Connection()

# 利用关联添加数据
boy = Boy(name='Zack')
boy.girl=[Girl(name='vicky'), Girl(name='wxm')]
session.add(boy)
session.commit()

girl = Girl(name='vicky')
girl.boys=[Boy(name='Mr沈'), Boy(name='zack')]
session.add(girl)
session.commit()

# 使用relationship的关系,正向查
b = session.query(Boy).filter(Boy.name == 'zack').first()
print(b.name)
print(b.girl)

# 使用relationship的关系,反向查
g = session.query(Girl).filter(Girl.name=='vicky').first()
print(g.name)
print(g.boys)

3、Flask-SQLAlchemy与 flask_migrate

 flask_sqlalchemy是flask和SQLAchemy的管理者,通过他把他们做连接的ORM

要用就必须先安装。
所有的到导入都找 下面的db
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

flask_migrate数据库迁移命令

命令:manager.add_command('db1', MigrateCommand)
# 1 当项目第一次执行迁移的时候。
python3 manage.py db1 init  # 只需要初始化一次

python3 manage.py db1 migrate # 等同于django的makemigrations

python3 manage.py db1 upgrade # 等同于django的migrate