Python中django的ORM和SQLalchemy简单对比(一)

发布时间 2023-04-30 12:53:10作者: 小家电维修

1.ORM

  对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。

  一般的ORM包括以下四部分:

一个对持久类对象进行CRUD操作的API;

一个语言或API用来规定与类和类属性相关的查询;

一个规定MAPPING METADATA的工具;

一种技术可以让ORM的实现同事务对象一起进行DIRTYCHECKING, LAZY ASSOCIATION FETCHING以及其他的优化操作。

 

2.django的ORM和SQLalchemy

  Django 的 Model 驱动对数据库层面上的实现细节关注的非常少,开发者定义模型的过程非常接近声明式而非过程式,对于新项目来说,可能是这个原因让 Django Model 比 SQLAlchemy 讨人喜欢。

  传统的 SQLAlchemy 的使用方法是不入侵模型,在单独的地方定义表结构、映射规则,然后用 SQLAlchemy 驱动注入到模型类里去,这种方法可以完全避免模型与数据库的耦合,但是定义繁琐,要求开发者完全明白 engine、metadata、table、column、mapper 等概念,如果没有读过《企业应用架构模式》一类书籍会被弄得很乱。

  现在 SQLAlchemy 提供了 declarative 的方式,和 Django Model 很像,但是和声明式模型还是有一定的距离,好在对于灵活性几乎没损失。但是我对比了一下 Django Model 和 SQLAlchemy declarative,发现 Django Model 还是更简洁一些。例如对于类关联,Django 只要直接声明外键,就自动产生关联对象,而 SQLAlcyhemy 要定义外键、relationship 对象,如果有多个外键还要自己指定 join 规则…… 总之灵活性是好东西,但是不是什么情况下都讨人喜欢的。

  我本来想说这个是 ActiveRecord style 和 Data Mapper style 区别导致的,但是细想了一下,Django Model 并不是简单的 ActiveRecord,其对于复杂关联甚至继承的映射都有很好的适应性,应该和 SQLAlchemy 的 declarative 是同类型的,是对 Data Mapper 的 Active Record style 包装。

  因为在Django世界中它的ORM就是事实标准。Django最重要的特色是什么?从ORM快速生成后台管理界面!另外还有ModelForm、数据迁移(migration)等等从Django ORM延伸出去的概念……如果你选用了SQLAlchemy,那么这一切都没有了,你必须自行搭建,或者选用第三方库。话说,没有了内置ORM、没有了内置后台管理界面、没有了内置ModelForm、没有了数据迁移的Django,还是Django吗?不如直接用其它更轻量或更松散的框架好了!

  以我的观点,ORM是在SQL之上的封装,而这种封装引入了太厚的封装,使得程序员对底层的控制力明显减弱,又加入了太多新的设计。所以我是不赞同使用ORM的,还是干干净净的SQL好用的多。

 

3.实际对比

  这里使用一位大佬的博客来看看两者区别,具体的过程我就不做演示了,直接搬过来。

  表和表的之间关系,往往通过外键建立关系,那简单介绍下外键。

 

  一:DB角度上的上外键含义:

  主键:主键是唯一标识一条记录,不能重复,有唯一性约束,不可以为空。用来保证数据的完整性,我们常用自增id来创建主键。

  外键:是另一张表的主键或者带有唯一性约束的列,外键可以重复,可以为空,主要作用:用他来和其他的表建立逻辑关系。所以当我们谈到外键的时候最少涉及到2张表。

  比如:

  部门表dept 和员工表emp。emp表中的Dept_id就是外键,和dept表单的主键id关联。

  因为一个部门多个员工,所以员工需要在知道自己归属的部门,所以emp是子表而dept是父表。

  外键一定是从表中建立的,从而和主表建立关系,从表维护二者的关系。

  使用外键的条件:

1、两张表必须都是InnodeDB表,并且没有临时表。

注:InnodDB是数据库引擎。mysql常见的数据库引擎:innodeDBhe 和MySIAM,而MySIM不支持外键约束!!!

2、建立外键关系的对应列必须有具有相似的InnoDB内部数据类型。

3、建立外键关系的对应列必须建立了索引,

 

二:从python角度理解:

  在python中通过关系映射(orm),调用底层dbapi来实现数据库的操作。通过定义类和对象,(类是表,类的对象是数据库的一行数据。)来操作数据库,通过底层的转换,最终形成sql,在相应的数据库中执行。

  notice:

  1. 无论是python通过orm(sqlalchemy和django的orm)来创建表,通过定义字段使表与表建立关系,比如外键。python层面定义的外键作用到数据库中,也是形成相应的外键。
  2. 所以无论哪种的orm建立的表结构,外键都是在子表中,可以理解成外键在多对一中“多”的字段中。
  3. 在数据库中,外键是建立在子表中。建立外键的列是另一张的表的主键。所以python设置外键的表是固定的。对应的“一对多”的“多”表中建立外键。

 

  A:django的orm:

  一对多:

  比如说:作者和书籍的关系,一个作者有多本书,构成一对多的关系。

  modles结构:

1 class Author(models.Model):
2     username=models.CharField(max_length=32)
3 class Book(models.Model):
4     name=models.CharField(max_length=32)
5     b_author=models.ForeignKey('Author')

  插入数据:通过访问相应的url插入数据

1 def insert(request):
2     models.Author.objects.create(username="tom")
3     return HttpResponse('ok')

1 def insert(request):
2     models.Book.objects.create(name='唐吉坷德',b_author_id=1)
3     return HttpResponse('ok')

  数据库本质的插入。

  查询:

  正向查询:

  通过双下划线来操作。

  比如:百年孤独的作者的名字是谁?

1 def insert(request):
2     ret=models.Book.objects.filter(name="百年孤独").values("b_author__username")
3     print(ret)
4     return HttpResponse('ok')
5 <QuerySet [{'b_author__username': 'tom'}]>

  反向查询:一对多也支持反向查询。方法有2个。一个是通过双下划线,一个通过tablename_set字段哎查询,查询的方式不一样,方法也不一样!!!

  • 通过tablename_set来反向查询,但是在这种查询,方法和manytomany 操作第三章表类似。

  需求:查看tom都出 那些书,书的名字是什么?

1 def insert(request):
2     obj=models.Author.objects.filter(username='tom').first()
3     ret=obj.book_set.all()
4     for i in ret:
5         print(i.name)
6     return HttpResponse('ok')
1 百年孤独
2 唐吉坷德
  • 通过双下滑线反向查询:
1 def insert(request):
2     booklist=models.Author.objects.filter(username='tom').values('book__name')
3     print(booklist)
4     return HttpResponse('ok')
5 <QuerySet [{'book__name': '百年孤独'}, {'book__name': '唐吉坷德'}]>

  notice:

  • 无论正向查询还是反向查询,都可以用双下划线。
  • 反向查询的时候,因为对应的是“一”,所以查询的结果需要单个对象,然后查询对应“多”的表的对象集合。而使用set的字段的时候,需要注意使用的方法都是all、filter等。

  对于多对多情况,正向查询的时候,使用的的是双下划线,反向查询是用含有manytomany的字段的“表”名的隐藏的列的进行反向查询。也是利用双下划线。

  表结构:一个服务器有多个用户,一个用户在存在于多个服务器中。

class Host(models.Model):
    ip=models.CharField(max_length=32)
    hostname=models.CharField(max_length=32)

class User(models.Model):
    username=models.CharField(max_length=32)
    password=models.CharField(max_length=32)
    user_host=models.ManyToManyField('Host')#注意引号的作用,如果没有引号的话,关联的表必须在当前表的前面,否则报错。

  插入数据:

1 def insert(request):
2     for i in range(4):
3         models.Host.objects.create(ip='172.17.33.'+str(i),hostname=str(i)+'.com')
4     return HttpResponse('ok')

1 def insert(request):
2     for i in range(4):
3         models.User.objects.create(username='evil'+str(i),password=str(i)+'123')
4     return HttpResponse('ok')

  • 需要注意:当我们在给含有ManyToMany的表进行插值的时候,对于字段:user_host=models.ManyToManyField('Host'),不需要插值。该列只是关联第三张关系表:mod_user_user_host。实质该列在数据库并不存在该列。

  第三张表(mod_user_user_host)进行插值:第三张表存储在django层面来看是对象,实际在数据库中存储的是这2张表(host、user)的主键值。所以插入值的方法也有2种。

  正向插入值:

1 def insert(request):
2     res=models.User.objects.filter(id=3).first()
3     ret=models.Host.objects.filter(id__gt=2)
4     res.user_host.add(*ret)
5     return HttpResponse('ok')

  反向插入值,反向插入值,用另一张表名_set.add()方法来操作。

1 def insert(request):
2     res=models.Host.objects.filter(id=1).first()
3     ret=models.User.objects.filter(id__lt=4)
4     res.user_set.add(*ret)
5     return HttpResponse('ok')

  当然相应的查询方法,也可以 用_set字段或者表名进行查询,和一对多是一样的。唯一区别是:更新第三张表的时候,最好用原生sql进行更新。

 

  B:sqlalchemy的orm

  • 对于一对多或者多对多的情况下,在sqlalchemy中使用的是relationship()来创建一个数据库中并不存在的列来构建2个表之间的关系。
  • 需要注意的:在django中建立外键,直接类的名字既可,在sqlalchemy中需要写:表的名字.主键列的名字。

  表结构:比如主机和主机用户来构建多对多表结构

class Host(Base):
    __tablename__='host'
    nid=Column(Integer,autoincrement=True,primary_key=True)
    hostname=Column(String(32))
    ip=Column(String(32))

class system_User(Base):
    __tablename__='system_user'
    nid=Column(Integer,autoincrement=True,primary_key=True)
    username=Column(String(32))
    password=Column(String(32))

class Host_to_system_User(Base):
    __tablename__='host_to_system_user'
    nid=Column(Integer,primary_key=True,autoincrement=True)
    host_nid=Column(Integer,ForeignKey('host.nid'))
    system_user_nid=Column(Integer,ForeignKey('system_user.nid'))#需要注意这个和django不一样,django直接写类名字既可。
    host=relationship('Host',backref='ho')
    system_user=relationship('system_User',backref='sys_u')
Base.metadata.create_all(engine)##执行该函数,他就会执行所有Base所有的子类。调用我们定义类并创建相应的表结构。
Session=sessionmaker(engine)
session=Session()#创建数据库连接。可以理解为django的db模块中connection。

  插入数据:2张表正常插入值和django一样,不需要关注第三张表,当需要建立关系的时候,直接给第三张表插入相应的id值既可。

Session=sessionmaker(engine)
session=Session()#创建数据库连接。可以理解为django的db模块中connection。
host_obj=Host(hostname='c1.com',ip='172.17.22.12')
host_obj_1=Host(hostname='c2.com',ip='172.17.22.13')
host_obj_2=Host(hostname='c3.com',ip='172.17.22.14')
session.add_all(
        [
            host_obj,
            host_obj_1,
            host_obj_2
        ]
)
system_user_obj=system_User(username='evil',password='123')
system_user_obj_1=system_User(username='tom',password='123')
system_user_obj_2=system_User(username='jack',password='123')
session.add_all(
        (
            system_user_obj,
            system_user_obj_1,
            system_user_obj_2
        )
)
#
host_to_system_user_obj=Host_to_system_User(host_nid=1,system_user_nid=1)
host_to_system_user_obj_1=Host_to_system_User(host_nid=1,system_user_nid=2)
host_to_system_user_obj_2=Host_to_system_User(host_nid=1,system_user_nid=3)
host_to_system_user_obj_3=Host_to_system_User(host_nid=2,system_user_nid=1)
host_to_system_user_obj_7=Host_to_system_User(host_nid=2,system_user_nid=2)
host_to_system_user_obj_4=Host_to_system_User(host_nid=2,system_user_nid=3)
host_to_system_user_obj_5=Host_to_system_User(host_nid=3,system_user_nid=1)
host_to_system_user_obj_6=Host_to_system_User(host_nid=3,system_user_nid=2)
session.add_all(
    [
        host_to_system_user_obj,
        host_to_system_user_obj_1,
        host_to_system_user_obj_2,
        host_to_system_user_obj_3,
        host_to_system_user_obj_4,
        host_to_system_user_obj_5,
        host_to_system_user_obj_6,
        host_to_system_user_obj_7
    ]
)
session.commit()
mysql> select * from  host;
+-----+----------+--------------+
| nid | hostname | ip           |
+-----+----------+--------------+
|   1 | c1.com   | 172.17.22.12 |
|   2 | c2.com   | 172.17.22.13 |
|   3 | c3.com   | 172.17.22.14 |
+-----+----------+--------------+
3 rows in set (0.00 sec)

mysql> select * from  system_user;
+-----+----------+----------+
| nid | username | password |
+-----+----------+----------+
|   1 | evil     | 123      |
|   2 | tom      | 123      |
|   3 | jack     | 123      |
+-----+----------+----------+
3 rows in set (0.00 sec)

mysql> select * from host_to_system_user;
+-----+----------+-----------------+
| nid | host_nid | system_user_nid |
+-----+----------+-----------------+
|   1 |        1 |               1 |
|   2 |        1 |               2 |
|   3 |        1 |               3 |
|   4 |        2 |               1 |
|   5 |        2 |               3 |
|   6 |        3 |               1 |
|   7 |        3 |               2 |
|   8 |        2 |               2 |
+-----+----------+-----------------+
8 rows in set (0.00 sec)

  查询:

  需求:用户evil 都在哪些服务器上?

  分析:首先通过system_user查到evil的对象,通过该对象获取sys_u的列,这个列是第三张表的对象的集合,通过循环该列获取每列的我们建立的host关系列来获取对应的host表的属性值。也就是说先反向查询在正向查询。

1 ret=session.query(system_User).filter(system_User.username=='evil').first()
2 for i in ret.sys_u:
3     print(i.host.ip)


结果
1 172.17.22.12
2 172.17.22.13
3 172.17.22.14

 

  django orm和sqlalchemy的orm的对比:

  • 在django中orm中插入和查询的时候,使用是类中定义数据库的列的名字,而在sqlalchemy中使用的调用数据库的字段是对象的属性字段。

  django:

  modle:

1 class Person(models.Model):
2     username=models.CharField(max_length=32)
3 
4 class Fav(models.Model):
5     name=models.CharField(max_length=32)
6     per_fav=models.ForeignKey('Person')

  查询语句:

  比如查询名字为evil的爱好都有哪些:

1 def insert(request):
2     ret=models.Fav.objects.filter(per_fav__username='evil').values('name')
3     print(ret)
4     return HttpResponse('ok')

  其中查询的字段并没有对象.字段属性。而sqlalchemy在调用表的字段的字段的时候,需要使用类.字段(其中字段属于类的静态字段。)

  sqlalchemy:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index#导入列数据类型字段。
from sqlalchemy.orm import sessionmaker, relationship#导入会话函数、
from sqlalchemy import create_engine
engine = create_engine("mysql+pymysql://root:@192.168.31.222:3306/django", max_overflow=15)#创建数据库引擎,通过连接池获取数据库的连接。数据库连接池数量为:15,默认值是5.
#创建sqlorm基类。(为声明的类定义基类。)
Base = declarative_base()
class User(Base):
    __tablename__='user'
    nid=Column(Integer,primary_key=True,autoincrement=True)
    username=Column(String(32))

    def __repr__(self):
        temp='%s-%s'%(self.nid,self.username)
        return temp
Base.metadata.create_all(engine)##执行该函数,他就会执行所有Base所有的子类。调用我们定义类并创建相应的表结构。

  插入数据:

Session=sessionmaker(engine)
session=Session()#创建数据库连接。可以理解为django的db模块中connection。
user_obj=User(username='evil')#创建类的对象。
user_obj1=User(username='tom')
user_obj2=User(username='jack')
session.add_all(
        (
            user_obj,
            user_obj1,
            user_obj2
        )
)
session.commit()#提交数据库事务。
mysql> select *from user;
+-----+----------+
| nid | username |
+-----+----------+
|   1 | evil     |
|   2 | tom      |
|   3 | jack     |
+-----+----------+
3 rows in set (0.00 sec)

  查询数据库:

1 Session=sessionmaker(engine)
2 session=Session()#创建数据库连接。可以理解为django的db模块中connection。
3 ret=session.query(User).filter(User.nid>1).all()
4 print(ret)
5 [2-tom, 3-jack]

  对比:

  • 数据库连接:django在创建项目的时候在setting配置文件中需要配置数据库的连接串,默认使用数据库是sqlite。
1 DATABASES = {
2     'default': {
3         'ENGINE': 'django.db.backends.sqlite3',
4         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
5     }
6 }
  • sqlalchemy 需要导入相应的模块,然后建立相应的连接引擎和数据库的连接池。每次执行数据库操作的时候都需要创建连接对象。而django默认帮我们做数据库连接。
1 from sqlalchemy import create_engine
2 engine = create_engine("mysql+pymysql://root:@192.168.31.222:3306/django", max_overflow=15)#创建数据库引擎,通过连接池获取数据库的连接。
  • django在创建数据库的时候,创建表的类的时候需要继承(models.Model)而sqlalchemy需要继承一个基类:Base = declarative_base()
  • 在django如果不指定主键的时候,django默认会创建一个名为:id的自增列。而sqlalchemy需要手动指定自增列和主键。
  • 默认在django中表明是类名的小写,而在sqlalchemy中必须要指定__tablename__字段,指定数据库的表的名字否则报如下错误信息。
1 sqlalchemy.exc.InvalidRequestError: Class <class '__main__.User'> does not have a __table__ or __tablename__ specified and does not inherit from an existing table-mapped class.

  django:

1 from django.db import models
2 
3 # Create your models here.
4 class Person(models.Model):
5     username=models.CharField(max_length=32)

  sqlalchemy:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index  # 导入列数据类型字段。
from sqlalchemy.orm import sessionmaker, relationship  # 导入会话函数、
from sqlalchemy import create_engine

engine = create_engine("mysql+pymysql://root:@192.168.147.129:3306/sqlalchemy",
                       max_overflow=15)  # 创建数据库引擎,通过连接池获取数据库的连接。数据库连接池数量为:15,默认值是5.
# 创建sqlorm基类。(为声明的类定义基类。)
Base = declarative_base()

class   User(Base):
    __tablename__='user'
    nid=Column(Integer,primary_key=True,autoincrement=True)
    name=Column(String(32))
    def __repr__(self):
        return self.name
  • django 默认情况不需要指定表名,而sqlalchemy需要指定表名否则报错。
  • 2者在查询返回的结果都是对象的列表(django是Queryset对象,而sqlalchemy返回的是相应的类对象),django可以用__unicode__方法 输出对象的时候我们可以自定义输出的我们自定义的字段(django版本Django 1.10中是__str__方法),而sqlalchemy是__repr__方法来输出对象的自定义字段。

  sqlalchemy:

class User(Base):
    __tablename__='user'
    nid=Column(Integer,primary_key=True,autoincrement=True)
    username=Column(String(32))
Session=sessionmaker(engine)
session=Session()#创建数据库连接。可以理解为django的db模块中connection。
ret=session.query(User).filter(User.nid>1).all()
print(ret)
[<__main__.User object at 0x03708210>, <__main__.User object at 0x03708250>]

  django:

from django.db import models

# Create your models here.
class Person(models.Model):
    username=models.CharField(max_length=32)

class Fav(models.Model):
    name=models.CharField(max_length=32)
    per_fav=models.ForeignKey('Person')
1 def insert(request):
2     ret=models.Fav.objects.filter(per_fav__username='evil').all()
3     print(ret)
4     return HttpResponse('ok')
1 <QuerySet [<Fav: Fav object>, <Fav: Fav object>]>

  sqlalchemy:

class User(Base):
    __tablename__='user'
    nid=Column(Integer,primary_key=True,autoincrement=True)
    username=Column(String(32))

    def __repr__(self):
        temp='%s-%s'%(self.nid,self.username)
        return temp
 [2-tom, 3-jack]

  django:

from django.db import models

# Create your models here.
class Person(models.Model):
    username=models.CharField(max_length=32)
    def __str__(self):
        temp='%s-%s'%(self.id,self.username)
        return temp

class Fav(models.Model):
    name=models.CharField(max_length=32)
    per_fav=models.ForeignKey('Person')
    def __str__(self):
        temp='%s-%s'%(self.id,self.name)
        return temp

  输出函数:

from django.shortcuts import render,HttpResponse
from mod  import models
# Create your views here.
def insert(request):
    ret=models.Fav.objects.filter(per_fav__username='evil').all()
    for i in ret:
        print(i)
    return HttpResponse('ok')
1-bak
2-foot
  • 在sqlalchemy中对数据库进行增删改的时候,最后需要提交事务(session.commit()),数据库才能保存我们的数据修改,在低版本的django中也需要save()进行提交事务在版本:Django 1.10中不需要提交事务。
User_obj=User(name='evil')
User_obj_1=User(name='tom')
User_obj_2=User(name='jack')

Session=sessionmaker(engine)
session=Session()
session.add_all(
    [
        User_obj,
        User_obj_1,
        User_obj_2
    ]
)
session.commit()
mysql> select * from  user;
+-----+------+
| nid | name |
+-----+------+
|   1 | evil |
|   2 | tom  |
|   3 | jack |
+-----+------+
3 rows in set (0.00 sec)
  • 插入数据:在一对多的情况下,django可以有两种方式进行数据的插入,包括:django层面,对外键列插入关联表的对象,在数据库层面:实际数据库中存储的是:另一个列的主键id值。可以直接插入数字。

  表结构:

1 class Author(models.Model):
2     username=models.CharField(max_length=32)
3 class Book(models.Model):
4     name=models.CharField(max_length=32)
5     b_author=models.ForeignKey('Author')

  给b_author插入值,默认情况下,book表(注意是小写)中的b_author列在数据库中的列为:b_author_id.

  所以我们在插入b_author列插入author表的对象,也可以直接插入author主键列里的id值。插入对象:

from django.shortcuts import render,HttpResponse
from mod import models
# Create your views here.
def insert(request):
    author_obj=models.Author.objects.filter(id=1).first()
    models.Book.objects.create(b_author=author_obj,name="罗兵逊漂流记")
    return HttpResponse('ok')

  插入id值:

1 from django.shortcuts import render,HttpResponse
2 from mod import models
3 # Create your views here.
4 def insert(request):
5     models.Book.objects.create(b_author_id=2,name="陈二狗的妖孽人生")
6     return HttpResponse('ok')

  需要注意的是:需要使用实际数据库存在的列(b_author_id)而不是django定义字段的列名字:b_author。

  • 在sqlalchemy中,插入的时候可以直接入相应类的对象(add()),或者可迭代对象集合(addall(元组、或者列表)),存在foreign key 列需要特别注意: 插入该列的值不是对象,而是关联表的主键nid值,不是对象!!!区别于django 插入外键的时候可以是对象或者id值。而且sqlalchemy定义的列名字就是数据库存储的名字。区别于django中在数据库层次的实际存储的列名字是django定义列的名字加上_id.

  表结构:一个作者有多本书。book表示子表,相对author表为父表。

class Author(Base):
    __tablename__='author'
    username=Column(String(32))
    nid=Column(Integer,primary_key=True,autoincrement=True)
    def __repr__(self):
        temp='%s--%s'%(self.nid,self.username)
        return temp

class Book(Base):
    __tablename__ = 'book'
    nid = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(32))
    b_a=Column(Integer,ForeignKey('author.nid'))
    book=relationship('Author',backref='auth')

    def __repr__(self):
        temp = '%s-%s' % (self.nid, self.name)
        return temp
Base.metadata.create_all(engine)##执行该函数,他就会执行所有Base所有的子类。调用我们定义类并创建相应的表结构。

  插入值:

author1=Author(username='evil')
author2=Author(username='tom')
author3=Author(username='jack')

Session=sessionmaker(bind=engine)
session=Session()
session.add_all(
    [
        author1,
        author2,
        author3
    ]
)
session.commit()
mysql> select * from author;
+----------+-----+
| username | nid |
+----------+-----+
| evil     |   1 |
| tom      |   2 |
| jack     |   3 |
+----------+-----+
3 rows in set (0.00 sec)

  插入语句:注意外键的列插入的不是对象,而是相应的关联表的主键的值。即主表的主键nid值。

book1=Book(name='百年孤独',b_a=3)
book2=Book(name='陈二狗的妖孽人生',b_a=3)
book3=Book(name='海贼王',b_a=3)

session.add_all(
    (
        book1,
        book2,
        book3
    )
)
session.commit()
mysql> select * from book;
+-----+--------------------------+------+
| nid | name                     | b_a  |
+-----+--------------------------+------+
|   2 | 百年孤独                 |    2 |
|   3 | 陈二狗的妖孽人生         |    2 |
|   4 | 海贼王                   |    2 |
|   5 | 百年孤独                 |    1 |
|   6 | 陈二狗的妖孽人生         |    1 |
|   7 | 海贼王                   |    1 |
|   8 | 百年孤独                 |    3 |
|   9 | 陈二狗的妖孽人生         |    3 |
|  10 | 海贼王                   |    3 |
+-----+--------------------------+------+
9 rows in set (0.00 sec)
  • 出现的问题: 编码问题,如果我们在sqlalchemy中不指定编码的时候,底层dbapi采用的编码:latin-1进行编码,如果你插入的数据含有中文的话,由于latin-1在解码的时候,不支持中文会报错:

  UnicodeEncodeError: 'latin-1' codec can't encode characters in position 38-41: ordinal not in range(256)

  解决方法:在数据连接引擎处指定编码,让sql语句中的中文部分按指定的编码进行解码。使用dbname?charset=utf8来指定相应的编码。

  http://firefish.blog.51cto.com/298258/112794/

1 engine = create_engine("mysql+pymysql://root:@192.168.147.129:3306/sqlalchemy?charset=utf8",
2                        max_overflow=15)  # 创建数据库引擎,通过连接池获取数据库的连接。数据库连接池数量为:15,默认值是5.

  所以以后再写引擎的时候,需要注意:直接写上面的连接串。

  在mysql中修改编码的方法:在/etc/my.cnf 中添加入下语句,如果不添加client字段的话,在数据库中中文字段显示是?问号。但是程序查询的时候,还会正常显示中文。

1 [mysqld]
2 default-storage-engine=INNODB
3 default-character-set=utf8
4 [client]
5 default-character-set=utf8

  因为有的版本mysql不支持参数:default-character-set=utf8 需要使用:character_set_server=utf8。

    • 建立外键的区别:django 中在一对多的情况下,使用的foreign key 。 在多对多,不创建第三张表的时候,使用ManyToMany字段。如果手动创建第三张表的时候使用foreign key来构建关系。sqlalchemy中,一对多、多对多的情况下,都是用foreign key来构建关系。而且在多对多的情况下,需要手动构建表结构关系,创建第三张表,
    • 对外键列的插入方法的不同:在django中对于外键列插入值,可以是插入关联表的对象或者关联表的对应的主键列即id列2种方法,但是在sqlalchemy对于外键列只能插入父表中的定义的主键列的值比如:nid值,而不是对象。
    • 对于查询建立关系的区别:在django中,默认给咱们创建相应的查询的虚拟列比如一对多,反向查询:tablename_set  和tablename2种虚拟关系列。前者不能使用双下划线,后者可以使用双下划线。在sqlalchemy中需要我们手动创建一个不存在数据库的虚拟机列,使用(本表和关联表的虚拟关系列)=relationship('classname',backref='另一张表的虚拟关系列')进行查询。正向查询用:用等号的左边的列,反向查询的时候,用等号的右边的backref定义的虚拟关系列。

  sqlalchemy:

  表结构:

class Author(Base):
    __tablename__='author'
    username=Column(String(32))
    nid=Column(Integer,primary_key=True,autoincrement=True)
    def __repr__(self):
        temp='%s--%s'%(self.nid,self.username)
        return temp

class Book(Base):
    __tablename__ = 'book'
    nid = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(100))
    b_a=Column(Integer,ForeignKey('author.nid'))
    book=relationship('Author',backref='auth')
    def __repr__(self):
        temp = '%s-%s' % (self.nid, self.name)
        return temp

  反向查询:使用子表中定义的列:book=relationship('Author',backref='auth') 建立的虚拟关系列:auth需要注意的是auth是另一个表的对象的集合。

  需求场景:作者为:evil,都出那些书籍?

Session=sessionmaker(bind=engine)
session=Session()
res=session.query(Author).filter(Author.username=='evil').first()
for i in res.auth:
    print(i.name)
百年孤独
陈二狗的妖孽人生
海贼王

  正向查询:

  需求场景:book表中nid=2的书作者是谁?

1 Session=sessionmaker(bind=engine)
2 session=Session()
3 ret=session.query(Book).filter(Book.nid==2).first()
4 print(ret.book.username)
5 
6 tom
  • 在django中对于大于、小于、等于、包含、不包含等使用的双下滑线:id__lt=2...    在sqlalchemy中直接使用  > < ==等。
  • 在django和sqlalchemy中如果查询条件为并的时候,都是用逗号。
Session=sessionmaker(bind=engine)
session=Session()
ret=session.query(Book).filter(Book.nid.in_((2,3,7)) ).all()
for i in ret:
    print(i.name)
百年孤独
陈二狗的妖孽人生
海贼王

 

 

参考: https://blog.csdn.net/GeekLeee/article/details/52806385/

https://www.cnblogs.com/evilliu/articles/5981122.html