08SQLAlchemy表关系和简单登录注册

01一对一表关系

在mysql中讲到了表关系,那么在sqlalchemy中怎么表示呢?

Module

需要先创建对应的 Module ,这里采用之前建立好的 User  和 UserDetails

relationship

from sqlalchemy.orm import relationship

在 UserDetails 中添加如下代码:
userdetail = relationship('User',backref='details',uselist=False,cascade='all')

使用

rows = session.query(User).get(1)
print(rows)
print(rows.details) # 查询 与user的第一条数据 有一对一关系的users的数据
<User(id=1,username=wu,password=123,creatime=2018-07-13 16:07:54)>
[<UserDetails(id=1,id_card=1234,last_login=2018-07-09 16:09:13,login_num=1,user_id=1)>]

自动添加属性

在刚才这里, User 里面本来是没有 details 这个属性的,但是在 UserDetails 里面添加 relationship 之后, User 实例会自动加上 details 属性

relationship

表关系是逻辑上的关系,但是 mysql 中并没有直接说明表关系的东西,外键约束是一个表现形式,外键是一种表之间的约束,可以用来表示这种关系

在SQLAlchemy里面,这个relationship代表了一对多的关系,当然我们可以通过参数改变关系,它默认是一对多的关系,而这个关系是SQLAlchemy里面的,和数据库没有关系,但是relationship是和外键一起使用的。

relationship参数分解

userdetails = relationship('User', backref='details', uselist=False, cascade='all')

在User类里加上detail属性

在UserDetails类里加上userdetails属性

User关联的 Module
backref在对应的 Module 中添加属性
uselist表示 一对多 关系, 如果是一对多关系,则不需要重新赋值,如果是一对一关系,则需要赋值为 False
cascade自动关系处理,就和 MySQL 中的 ON DELETE 类似

cascade

cascade 所有的可选字符串项是:

all所有操作都会自动处理到关联对象上.
save-update关联对象自动添加到会话.
delete关联对象自动从会话中删除.
delete-orphan属性中去掉关联对象, 则会话中会自动删除关联对象.
mergesession.merge() 时会处理关联对象.
refresh-expiresession.expire() 时会处理关联对象.
expungesession.expunge() 时会处理关联对象.

UserDetails上有 ForeignKey

正向查询: 通过 UserDetails 查询 User( UserDetails. userdetail)

反向查询: 通过 User 查询 UserDetails (User.details)

userdetails = relationship('User', backref='details', uselist='False')

查询

# 反向查询
row = session.query(User).first()
print(dir(row)) # 里面有details属性
print(row)
print(row.details)

# 正向查询
rows = session.query(UserDetails).get(1) # 取id=1的数据
print(rows)
print(rows.userdetails)
<User(id=1,username=wu,password=123,creatime=2018-07-13 16:07:54)>

[<UserDetails(id=1,id_card=1234,last_login=2018-07-09 16:09:13,login_num=1,user_id=1)>]

<UserDetails(id=1,id_card=1234,last_login=2018-07-09 16:09:13,login_num=1,user_id=1)>

[<User(id=1,username=wu,password=123,creatime=2018-07-13 16:07:54)>]

分辨正向查询和反向查询,主要看 ForeginKey 在哪个 Module

代码:

user_module.py

from datetime import datetime
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean
from connect import Base, session
from sqlalchemy.orm import relationship


# 实体类User,对应数据库中的user表
class User(Base):
   # 表的名字:
   __tablename__ = 'user'

   # 表的结构:
   id = Column(Integer, primary_key=True, autoincrement=True)  # 主键  自增
   username = Column(String(20), nullable=False)  # 不为空
   password = Column(String(50))
   creatime = Column(DateTime, default=datetime.now)
   _locked = Column(Boolean, default=False, nullable=False)

   @classmethod
   def by_name(cls, name):
      return session.query(cls).filter(cls.username == name).first()

   def __repr__(self):
      return "<User(id=%s,username=%s,password=%s,creatime=%s)>" % (
         self.id,
         self.username,
         self.password,
         self.creatime
      )


class UserDetails(Base):
   __tablename__ = 'user_details'

   id = Column(Integer, primary_key=True, autoincrement=True)
   id_card = Column(Integer, nullable=True, unique=True)
   lost_login = Column(DateTime)
   login_num = Column(Integer, default=0)
   user_id = Column(Integer, ForeignKey('user.id'))

   userdetails = relationship('User', backref='details', uselist='False')

   def __repr__(self):
      return '<UserDetails(id=%s,id_card=%s,last_login=%s,login_num=%s,user_id=%s)>' % (
         self.id,
         self.id_card,
         self.lost_login,
         self.login_num,
         self.user_id
      )


if __name__ == '__main__':
   # 找到BaseModel的所有子类,并在数据库中建立这些表
   Base.metadata.create_all()

relationship.py

from connect import session
from user_module import User, UserDetails

if __name__ == '__main__':
   row = session.query(User).first()
   print(row)
   print(row.details)
   # print(dir(row)

   rows = session.query(UserDetails).get(1)
   print(rows)
   print(rows.userdetails)

connect.py

# -*- coding:utf-8 -*-

from sqlalchemy import create_engine

HOSTNAME = '127.0.0.1'  # 在本机上运行
PORT = '3306'  # 端口
DATABASE = 'mydb'  # 数据库
USERNAME = 'admin'  # 用户名
PASSWORD = 'Root110qwe'  # 密码

# 连接数据库的路径
# “mysql+pymysql”指定了使用 pymysql 来连接
db_url = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(
   USERNAME,
   PASSWORD,
   HOSTNAME,
   PORT,
   DATABASE
)

# 连接数据库
# create_engine() 会返回一个数据库引擎
engine = create_engine(db_url)


from sqlalchemy.ext.declarative import declarative_base

# declarative_base() 创建了一个 BaseModel 类,
# 这个类的子类可以自动与一个表关联。
# 创建对象的基类
Base = declarative_base((engine))


from sqlalchemy.orm import sessionmaker

# sessionmaker() 会生成一个数据库会话类
Session = sessionmaker(engine)
# 创建session对象:
session = Session()


if __name__ == '__main__':
   connection = engine.connect()
   result = connection.execute('select 1')
   print(result.fetchone())

一对多表关系

使用一对多

relationship 默认是 一对多 关系

uselist

uselist=True

默认是 True ,因此可以省略不写

02多对多表关系

用户 和 文章之间,可以是一对多的关系,但是如果用户转载的话,就可以看成是 多对多 关系,那 多对多 关系在 SQLAlchemy 中怎么表示呢?

多对多关系也很常见,例如学生与选修课之间的关系,一个学生可以选择多门选修课,而每个选修课又可以被多名学生选择。 数据库中的多对多关联关系一般需采用中间表的方式处理,将多对多转化为两个一对多。

创建 Module

from sqlalchemy import Table


# 建立中间表:
user_article = Table('user_article', Base.metadata,
                    Column('user_id', Integer, ForeignKey('user.id'), primary_key=True),
                    Column('article_id', Integer, ForeignKey('article.id'), primary_key=True)
)
文章 Module


class Article(Base):
    __tablename__ = 'article'

    id = Column(Integer, primary_key=True, autoincrement=True)
    content = Column(String(500), nullable=True)
    create_time = Column(DateTime, default=datetime.now)

    article_user = relationship('User', backref='article', secondary=user_article)

    def __repr__(self):
        return 'Article(id=%s, content=%s, creat_time=%s)' % (
            self.id,
            self.content,
            self.create_time
        )

在User类里加入article属性

article_user = relationship('User', backref='article', secondary=user_article)

查询

rows = session.query(User).get(1)
# print(dir(rows)) # 里面有 article属性
print(rows)
print(rows.article)

rows = session.query(Article).get(1)
print(rows)
print(rows.article_user)
<User(id=1,username=wu,password=123,creatime=2018-07-13 16:07:54)>

[Article(id=1, content=ok, creat_time=2018-06-13 17:19:14), Article(id=2, content=hello, creat_time=2018-09-13 17:19:33)]

Article(id=1, content=ok, creat_time=2018-06-13 17:19:14)

[<User(id=1,username=wu,password=123,creatime=2018-07-13 16:07:54)>, <User(id=2,username=qq,password=111,creatime=2018-09-05 16:08:09)>]

代码:

user_module.py

from datetime import datetime
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean, Table
from connect import Base, session
from sqlalchemy.orm import relationship


# 实体类User,对应数据库中的user表
class User(Base):
   # 表的名字:
   __tablename__ = 'user'

   # 表的结构:
   id = Column(Integer, primary_key=True, autoincrement=True)  # 主键  自增
   username = Column(String(20), nullable=False)  # 不为空
   password = Column(String(50))
   creatime = Column(DateTime, default=datetime.now)
   _locked = Column(Boolean, default=False, nullable=False)

   @classmethod
   def by_name(cls, name):
      return session.query(cls).filter(cls.username == name).first()

   def __repr__(self):
      return "<User(id=%s,username=%s,password=%s,creatime=%s)>" % (
         self.id,
         self.username,
         self.password,
         self.creatime
      )


class UserDetails(Base):
   __tablename__ = 'user_details'

   id = Column(Integer, primary_key=True, autoincrement=True)
   id_card = Column(Integer, nullable=True, unique=True)
   lost_login = Column(DateTime)
   login_num = Column(Integer, default=0)
   user_id = Column(Integer, ForeignKey('user.id'))

   userdetails = relationship('User', backref='details', uselist='False')

   def __repr__(self):
      return '<UserDetails(id=%s,id_card=%s,last_login=%s,login_num=%s,user_id=%s)>' % (
         self.id,
         self.id_card,
         self.lost_login,
         self.login_num,
         self.user_id
      )


user_article = Table('user_article', Base.metadata,
                Column('user_id', Integer, ForeignKey('user.id'), primary_key=True),
                Column('article_id', Integer, ForeignKey('article.id'), primary_key=True),
                )


class Article(Base):
   __tablename__ = 'article'

   id = Column(Integer, primary_key=True, autoincrement=True)
   content = Column(String(500), nullable=True)
   create_time = Column(DateTime, default=datetime.now)

   article_user = relationship('User', backref='article', secondary=user_article)

   def __repr__(self):
      return 'Article(id=%s, content=%s, creat_time=%s)' % (
         self.id,
         self.content,
         self.create_time
      )


if __name__ == '__main__':
   # 找到BaseModel的所有子类,并在数据库中建立这些表
   Base.metadata.create_all()

relationship.py

from connect import session
from user_module import User, UserDetails,Article

if __name__ == '__main__':
   row = session.query(User).first()
   print(row)
   print(row.details)
   # print(dir(row)

   rows = session.query(UserDetails).get(1)

   print(rows)
   print(rows.userdetails)

   rows = session.query(User).get(1)
   # print(dir(rows))
   print(rows)
   print(rows.article)

   rows = session.query(Article).get(1)
   print(rows)
   print(rows.article_user)

03包管理

当把 Module 写好之后,该如何调用呢?

直接导入

在 模块中直接导入:
from data.user_modules import User

刚才看到,运行报错,这是为什么呢?

包概念:

把很多模块放到一个文件夹里面,就可以形成一个包

包管理:

当把很多模块放在文件中时,为了方便引用包中的模块,引入了包管理

__init__.py:

在包管理中,加入此模块,则包名可以直接通过属性访问的方式,访问此模块内的对象,此模块不加上可能不会报错,但是规范是要加上,文件内容可以为空

相对路径导入:

在包管理中,可以通过 . (一个点) 和 .. (两个点)分别来导入同层和上一层的模块

相对路径导入

引入作用:

在包中,如果包中模块要导入同一包中的其他模块,就必须使用此方法导入

使用方法:

from  .module(..module)  
import obj   (as  new_name)

引入之后的影响:

当一个模块中出现此导入方式,则该模块不能被直接运行,只能被导入

04简单的登录

结合前面的内容来看,如何做一个登录的验证呢?

Handler

class LoginHandler(tornado.web.RequestHandler):

    def get(self):
        self.render('01in_out.html')

    def post(self, *args, **kwargs):
        user = self.get_argument('name', '')
        username = User.by_name(user)
        passwd = self.get_argument('password', '')

        if username and passwd == username[0].password:
            self.render('07login.html',
                        username=username,
                        )
        else:
            self.write('用户名或密码错误')

User.by_name

在 Module -> User 中添加如下方法:

@classmethod
def by_name(cls,name):
    return session.query(cls).filter_by(username=name).all()

模板

{% if username%}
    欢迎用户 {{ username[0].username }} 登录
    <br>
    <img src="/static/images/c.jpg" width="250" height="250" >
    <br>
{% else %}
    您还没有登录
{% end %}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值