加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

python 之路,Day11 - sqlalchemy ORM

发布时间:2020-12-17 00:06:38 所属栏目:Python 来源:网络整理
导读:本节内容 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业 1.?ORM介绍 orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都

本节内容

  1. ORM介绍
  2. sqlalchemy安装
  3. sqlalchemy基本使用
  4. 多外键关联
  5. 多对多关系
  6. 表结构设计作业

1.?ORM介绍

orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言。

orm的优点:

  1. 隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
  2. ORM使我们构造固化数据结构变得简单易行。

缺点:

  1. 无可避免的,自动化意味着映射和关联管理,代价是牺牲性能(早期,这是所有不喜欢ORM人的共同点)。现在的各种ORM框架都在尝试使用各种方法来减轻这块(LazyLoad,Cache),效果还是很显著的。

2. sqlalchemy安装

在Python中,最有名的ORM框架是SQLAlchemy。用户包括openstack\Dropbox等知名公司或应用,主要用户列表http://www.sqlalchemy.org/organizations.html#openstack

Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:

:@[:]/

pymysql
mysql+pymysql://:@/[?]

MySQL-Connector
mysql+mysqlconnector://:@[:]/

cx_Oracle
oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]

更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html

  

安装sqlalchemy

3.sqlalchemy基本使用

下面就开始让你见证orm的nb之处,盘古开天劈地之前,我们创建一个表是这样的

这只是最简单的sql表,如果再加上外键关联什么的,一般程序员的脑容量是记不住那些sql语句的,于是有了orm,实现上面同样的功能,代码如下

engine = create_engine("mysql+pymysql://root:alex3714@localhost/testdb",encoding='utf-8',echo=True)

Base = declarative_base() #生成orm基类

class User(Base):
tablename = 'user' #表名
id = Column(Integer,primary_key=True)
name = Column(String(32))
password = Column(String(64))

Base.metadata.create_all(engine) #创建表结构

你说,娘那个腚的,并没有感觉代码量变少啊,呵呵, 孩子莫猴急,好戏在后面

Lazy ConnectingThe Engine,when first returned by create_engine(),has not actually tried to connect to the database yet; that happens only the first time it is asked to perform a task against the database.  

除上面的创建之外,还有一种创建表的方式,虽不常用,但还是看看吧

metadata = MetaData()

user = Table('user',metadata,Column('id',primary_key=True),Column('name',String(50)),Column('fullname',Column('password',String(12))
)

class User(object):
def init(self,name,fullname,password):
self.name = name
self.fullname = fullname
self.password = password

mapper(User,user) #the table metadata is created separately with the Table construct,then associated with the User class via the mapper() function

事实上,我们用第一种方式创建的表就是基于第2种方式的再封装。

  

最基本的表我们创建好了,那我们开始用orm创建一条数据试试

user_obj = User(name="alex",password="alex3714") #生成你要创建的数据对象
print(user_obj.name,user_obj.id) #此时还没创建对象呢,不信你打印一下id发现还是None

Session.add(user_obj) #把要创建的数据对象添加到这个session里, 一会统一创建
print(user_obj.name,user_obj.id) #此时也依然还没创建

Session.commit() #现此才统一提交,创建数据

我擦,写这么多代码才创建一条数据,你表示太tm的费劲了,正要转身离开,我拉住你的手不放开,高潮还没到。。

查询

此时你看到的输出是这样的应该

我擦,这是什么?这就是你要的数据呀, 只不过sqlalchemy帮你把返回的数据映射成一个对象啦,这样你调用每个字段就可以跟调用对象属性一样啦,like this..

输出
1 alex alex3714

不过刚才上面的显示的内存对象对址你是没办法分清返回的是什么数据的,除非打印具体字段看一下,如果想让它变的可读,只需在定义表的类下面加上这样的代码

" % ( self.name,self.password)

  

修改

my_user.name = "Alex Li"

Session.commit()

回滚

fake_user = User(name='Rain',password='12345')
Session.add(fake_user)

print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) #这时看session里有你刚添加和修改的数据

Session.rollback() #此时你rollback一下

print(Session.query(User).filter(User.name.in_(['Jack','rain'])).all() ) #再查就发现刚才添加的数据没有了。

Session

Session.commit()

  

获取所有数据

多条件查询

0).filter(User.id<7).all()

上面2个filter的关系相当于 user.id >1 AND user.id <7 的效果

统计和分组

分组

相当于原生sql为

输出为

[(1,'Jack'),(2,'Rain')]

外键关联

我们创建一个addresses表,跟user表关联

class Address(Base):
tablename = 'addresses'
id = Column(Integer,primary_key=True)
email_address = Column(String(32),nullable=False)
user_id = Column(Integer,ForeignKey('user.id'))

user = relationship("User",backref="addresses") #这个nb,允许你在user表里通过backref字段反向查出所有它在addresses表里的关联项

def __repr__(self):
    return "<Address(email_address='%s')>" % self.email_address

The??parameter is a newer version of a very common SQLAlchemy feature called. The??parameter hasn’t gone anywhere and will always remain available! The??is the same thing,except a little more verbose and easier to manipulate. For an overview of the entire topic,see the section?.  

  

表创建好后,我们可以这样反查试试

addr_obj = Session.query(Address).first()
print(addr_obj.user.name) #在addr_obj里直接查关联的user表

创建关联对象

obj.addresses = [Address(email_address="r1@126.com"),#添加关联对象
Address(email_address="r2@126.com")]

Session.commit()

常用查询语法

Common Filter Operators

Here’s a rundown of some of the most common operators used in filter():

  • equals:

         query.filter(User.name == 'ed')
    
  • not equals:

         query.filter(User.name != 'ed')
    
  • LIKE:

    query.filter(User.name.like('%ed%'))

  • IN:

  • NOT IN: query.filter(~User.name.in_(['ed','wendy','jack']))

                        </li>
  • IS NULL:

                        </li>
  • IS NOT NULL:

                        </li>
  • AND: 2.1. ObjectRelationalTutorial 17

                        </li>
    
    
    
    
    
                    </ul>
        </div>

<div class="section">
<div class="layoutArea">
<div class="column">

query.filter(User.name.in_(['ed','jack']))
# works with query objects too:

query.filter(User.name.in_( session.query(User.name).filter(User.name.like('%ed%'))

))

query.filter(User.name == None)
# alternatively,if pep8/linters are a concern
query.filter(User.name.is_(None))
query.filter(User.name != None)
# alternatively,if pep8/linters are a concern
query.filter(User.name.isnot(None))
SQLAlchemy Documentation,Release 1.1.0b1

# use and_()

from sqlalchemy import and_ query.filter(and_(User.name == 'ed',User.fullname == 'Ed Jones'))

# or send multiple expressions to .filter()
query.filter(User.name == 'ed',User.fullname == 'Ed Jones')
# or chain multiple filter()/filter_by() calls
query.filter(User.name == 'ed').filter(User.fullname == 'Ed Jones')
Note: Makesureyouuseand_()andnotthePythonandoperator! ? OR:

Note: MakesureyouuSEOr_()andnotthePythonoroperator! ? MATCH:

query.filter(User.name.match('wendy')) Note: match() uses a database-specific MATCH or CONTAINS f?

            </div>





        </div>





    </div>

  

4.多外键关联

One of the most common situations to deal with is when there are more than one foreign key path between two tables.

Consider a??class that contains two foreign keys to an??class:

下表中,Customer表有2个字段都关联了Address表?

Base = declarative_base()

class Customer(Base):
tablename = 'customer'
id = Column(Integer,primary_key=True)
name = Column(String)

billing_address_id = Column(Integer,ForeignKey("address.id")) 
shipping_address_id = Column(Integer,ForeignKey("address.id"))

billing_address = relationship("Address")  
shipping_address = relationship("Address")

class Address(Base):
tablename = 'address'
id = Column(Integer,primary_key=True)
street = Column(String)
city = Column(String)
state = Column(String)

创建表结构是没有问题的,但你Address表中插入数据时会报下面的错

解决办法如下

billing_address = relationship("Address",foreign_keys=[billing_address_id]) shipping_address = relationship("Address",foreign_keys=[shipping_address_id])

这样sqlachemy就能分清哪个外键是对应哪个字段了

  

5.多对多关系

现在来设计一个能描述“图书”与“作者”的关系的表结构,需求是

  1. 一本书可以有好几个作者一起出版
  2. 一个作者可以写好几本书

此时你会发现,用之前学的外键好像没办法实现上面的需求了,因为

当然你更不可以像下面这样干,因为这样就你就相当于有多条书的记录了,太low b了,改书名还得都改。。。

那怎么办呢? 此时,我们可以再搞出一张中间表,就可以了

这样就相当于通过book_m2m_author表完成了book表和author表之前的多对多关联

用orm如何表示呢?

<span style="color: #0000ff;">from sqlalchemy <span style="color: #0000ff;">import<span style="color: #000000;"> Table,DATE,ForeignKey
<span style="color: #0000ff;">from sqlalchemy.orm <span style="color: #0000ff;">import<span style="color: #000000;"> relationship
<span style="color: #0000ff;">from sqlalchemy.ext.declarative <span style="color: #0000ff;">import<span style="color: #000000;"> declarative_base
<span style="color: #0000ff;">from sqlalchemy <span style="color: #0000ff;">import<span style="color: #000000;"> create_engine
<span style="color: #0000ff;">from sqlalchemy.orm <span style="color: #0000ff;">import<span style="color: #000000;"> sessionmaker

Base =<span style="color: #000000;"> declarative_base()

book_m2m_author = Table(<span style="color: #800000;">'<span style="color: #800000;">book_m2m_author<span style="color: #800000;">'<span style="color: #000000;">,Base.metadata,Column(<span style="color: #800000;">'<span style="color: #800000;">book_id<span style="color: #800000;">',ForeignKey(<span style="color: #800000;">'<span style="color: #800000;">books.id<span style="color: #800000;">'<span style="color: #000000;">)),Column(<span style="color: #800000;">'<span style="color: #800000;">author_id<span style="color: #800000;">',ForeignKey(<span style="color: #800000;">'<span style="color: #800000;">authors.id<span style="color: #800000;">'<span style="color: #000000;">)),)

<span style="color: #0000ff;">class<span style="color: #000000;"> Book(Base):
<span style="color: #800080;">tablename = <span style="color: #800000;">'<span style="color: #800000;">books<span style="color: #800000;">'<span style="color: #000000;">
id = Column(Integer,primary_key=<span style="color: #000000;">True)
name = Column(String(64<span style="color: #000000;">))
pub_date =<span style="color: #000000;"> Column(DATE)
authors = relationship(<span style="color: #800000;">'<span style="color: #800000;">Author<span style="color: #800000;">',secondary=book_m2m_author,backref=<span style="color: #800000;">'<span style="color: #800000;">books<span style="color: #800000;">'<span style="color: #000000;">)

</span><span style="color: #0000ff;"&gt;def</span> <span style="color: #800080;"&gt;__repr__</span><span style="color: #000000;"&gt;(self):
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; self.name

<span style="color: #0000ff;">class<span style="color: #000000;"> Author(Base):
<span style="color: #800080;">tablename = <span style="color: #800000;">'<span style="color: #800000;">authors<span style="color: #800000;">'<span style="color: #000000;">
id = Column(Integer,primary_key=<span style="color: #000000;">True)
name = Column(String(32<span style="color: #000000;">))

</span><span style="color: #0000ff;"&gt;def</span> <span style="color: #800080;"&gt;__repr__</span><span style="color: #000000;"&gt;(self):
    </span><span style="color: #0000ff;"&gt;return</span> self.name</pre>

接下来创建几本书和作者

b1 = Book(name="跟Alex学Python")
b2 = Book(name="跟Alex学把妹")
b3 = Book(name="跟Alex学装逼")
b4 = Book(name="跟Alex学开车")

a1 = Author(name="Alex")
a2 = Author(name="Jack")
a3 = Author(name="Rain")

b1.authors = [a1,a2]
b2.authors = [a1,a2,a3]

s.add_all([b1,b2,b3,b4,a1,a3])

s.commit()

此时,手动连上mysql,分别查看这3张表,你会发现,book_m2m_author中自动创建了多条纪录用来连接book和author表

select * from books; +----+------------------+----------+ | id | name | pub_date | +----+------------------+----------+ | 1 | 跟Alex学Python | NULL | | 2 | 跟Alex学把妹 | NULL | | 3 | 跟Alex学装逼 | NULL | | 4 | 跟Alex学开车 | NULL | +----+------------------+----------+ 4 rows in set (0.00 sec)

mysql> select * from authors;
+----+------+
| id | name |
+----+------+
| 10 | Alex |
| 11 | Jack |
| 12 | Rain |
+----+------+
3 rows in set (0.00 sec)

mysql> select * from book_m2m_author;
+---------+-----------+
| book_id | author_id |
+---------+-----------+
| 2 | 10 |
| 2 | 11 |
| 2 | 12 |
| 1 | 10 |
| 1 | 11 |
+---------+-----------+
5 rows in set (0.00 sec)

此时,我们去用orm查一下数据

book_obj = s.query(Book).filter_by(name="跟Alex学Python").first()
print(book_obj.name,book_obj.authors)

print('--------通过作者表查关联的书---------')
author_obj =s.query(Author).filter_by(name="Alex").first()
print(author_obj.name,author_obj.books)
s.commit()

输出如下

牛逼了我的哥!!完善实现多对多  

  

多对多删除

删除数据时不用管boo_m2m_authors , sqlalchemy会自动帮你把对应的数据删除

通过书删除作者

book_obj = s.query(Book).filter_by(name="跟Alex学把妹").first()

book_obj.authors.remove(author_obj) #从一本书里删除一个作者
s.commit()

直接删除作者 

删除作者时,会把这个作者跟所有书的关联关系数据也自动删除

  

处理中文

sqlalchemy设置编码字符集一定要在数据库访问的URL上增加charset=utf8,否则数据库的连接就不是utf8的编码格式

eng = create_engine('mysql://root:root@localhost:3306/test2?charset=utf8',echo=True)

 

 

6.本节作业

主题:学员管理系统

需求:

  • 用户角色,讲师\学员, 用户登陆后根据角色不同,能做的事情不同,分别如下
  • 讲师视图
    1.   管理班级,可创建班级,根据学员qq号把学员加入班级
    2.   可创建指定班级的上课纪录,注意一节上课纪录对应多条学员的上课纪录, 即每节课都有整班学员上, 为了纪录每位学员的学习成绩,需在创建每节上课纪录是,同时 ? ? ? ? 为这个班的每位学员创建一条上课纪录
    3.   为学员批改成绩, 一条一条的手动修改成绩
  • 学员视图
  1. 提交作业
  2. 查看作业成绩
  3. 一个学员可以同时属于多个班级,就像报了Linux的同时也可以报名Python一样, 所以提交作业时需先选择班级,再选择具体上课的节数
  4. 附加:学员可以查看自己的班级成绩排名

  

  

  

  

  

 

  

  

?

  

  

  

  

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读