1 ORM常用操作
1.1 概念
对象关系映射(英语:Object Relational Mapping,简称ORM),用于实现面向对象编程语言里不同类型系统的数据之间的转换。换句话说,就是用面向对象的方式去操作数据库的创建表,增加、修改、删除、查询等操作。
1.2 查看ORM生成的sql语句。
1.2.1 使用QuerySet中的query属性
user_list = User.objects.all()
print(user_list.query)
1.2.2 使用Logging日志查看
前面有提及,此处略
1.3 ORM优缺点
1.3.1 优点
- ORM使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
- 可以避免一些新手程序猿写sql语句带来的性能效率和安全问题:select * from table效率低,避免sql注入风险
1.3.2 缺点
- 性能有所牺牲,不过现在的各种ORM框架都在尝试使用各种方法来减轻这个问题(LazyLoad,Cache),效果还是很显著的。
- 对于个别复杂查询,ORM仍然力不从心。为解决这个问题,ORM框架一般也提供了直接写原生sql的方式。
1.4 ORM 机制下面的增删查改操作
本次所有的实验测试都在manage.py的shell模式下面进行操作,建议安装IPython提高交互
D:\virtualenv\env_py35_django\Scripts>activate
(env_py35_django) D:\virtualenv\env_py35_django\Scripts>cd D:\MaiZi_Edu\Dropbox\Maizi\Django\learn11_22\hello_django
(env_py35_django) D:\MaiZi_Edu\Dropbox\Maizi\Django\learn11_22\hello_django>python manage.py shell
Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
安装IPython的编辑器,提供更为强大的编辑和交互功能。
(env_py35_django) D:\MaiZi_Edu\Dropbox\Maizi\Django\learn11_22\hello_django>pip install ipython
Successfully installed appdirs-1.4.0 colorama-0.3.7 decorator-4.0.11 ipython-5.2.2 ipython-genutils-0.1.0 packaging-16.8 pickleshare-0.7.4 prompt-toolkit-1.0.13 pygments-2.2.0 pyparsing-2.1.10 setuptools-34.1.1 simplegeneric-0.8.1 traitlets-4.3.1 wcwidth-0.1.7 win-unicode-console-0.5
(env_py35_django) D:\MaiZi_Edu\Dropbox\Maizi\Django\learn11_22\hello_django>python manage.py shell
Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)]
Type "copyright", "credits" or "license" for more information.
IPython 5.2.2 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]:
注意:每次退出shell模式都要重新导入 from hello.models import *
1.4.1 增
- 插入简单数据:
In [1]: from hello.models import *
In [2]: Author.objects.create(name='蓝盾')
(0.000) BEGIN; args=None
(0.033) INSERT INTO "hello_author" ("name") VALUES ('蓝盾'); args=['蓝盾']
Out[2]:
- 插入一对多关系数据:使用create方法,外键的字段输入的是对应外键的id值
In [4]: AuthorDetail.objects.create(sex=False,email='[email protected]',address='www.landun.com',birthday='2017-1-19',author_id=4)
(0.000) BEGIN; args=None
(0.001) INSERT INTO "hello_authordetail" ("sex", "email", "address", "birthday", "author_id") VALUES (0, '[email protected]', 'www.landun.com', '2017-01-19', 4); args=[False, '[email protected]', 'www.landun.com', '2017-01-19', 4]
Out[4]:
- 通过对象的方法插入数据:实例化对象,给对象加入属性,通过save方法保存
In [5]: pub = Publisher()
...: pub.name = "电子工业出版社"
...: pub.address = "广东广州"
...: pub.city = "广州"
...: pub.state_province = "广州"
...: pub.city = "广东"
...: pub.country = "中国"
...: pub.website = "http://www.baidu.com"
...: pub.save()
...:
(0.000) BEGIN; args=None
(0.001) INSERT INTO "hello_publisher" ("name", "address", "city", "state_province", "country", "website") VALUES ('电子工业出版社', '广东广州', '广东', '广州', '中国', 'http://www.baidu.com'); args=['电子工业出版社', '广东广州', '广东', '广州', '中国', 'http://www.baidu.com']
- 通过对象的方法插入多对多的数据:通过get方法获取两个对象的实例,然后再用add方法来关联他们之间的关系
In [6]: Book.objects.create(title='Pytho实战',publisher=pub,publication_date='2015-5-6')
(0.000) BEGIN; args=None
(0.001) INSERT INTO "hello_book" ("title", "publisher_id", "publication_date", "price") VALUES ('Pytho实战', 8, '2015-05-06', '10.00'); args=['Pytho实战', 8, '2015-05-06', '10.00']
Out[6]:
In [7]: book = Book.objects.get(id=1)
(0.001) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" WHERE "hello_book"."id" = 1; args=(1,)
In [8]: author = Author.objects.get(id=1)
(0.000) SELECT "hello_author"."id", "hello_author"."name" FROM "hello_author" WHERE "hello_author"."id" = 1; args=(1,)
In [9]: book.authors.add(author)
(0.000) BEGIN; args=None
(0.000) SELECT "hello_book_authors"."author_id" FROM "hello_book_authors" WHERE ("hello_book_authors"."book_id" = 1 AND "hello_book_authors"."author_id" IN (1)); args=(1, 1)
1.4.2 删【级联删除】
使用filter方法过滤在用delete删除,注意所有的删除都是级联删除
In [10]: Book.objects.filter(id=13).delete()
(0.000) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" WHERE "hello_book"."id" = 13; args=(13,)
(0.000) BEGIN; args=None
(0.001) DELETE FROM "hello_book_authors" WHERE "hello_book_authors"."book_id" IN (13); args=(13,)
(0.048) DELETE FROM "hello_book" WHERE "hello_book"."id" IN (13); args=(13,)
Out[10]: (1, {'hello.Book': 1, 'hello.Book_authors': 0})
1.4.3 查【惰性查询】
- 惰性查询:直到使用的时候才会对数据库进行查询
- shell下面是非惰性的,py文件下面都是惰性查询
- 查询使用filter得到的是QuerySet对象,使用get芳芳得到的是一个models对象
In [11]: Book.objects.filter(id=1)
Out[11]: (0.001) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" WHERE "hello_book"."id" = 1 LIMIT 21; args=(1,)
[]
In [13]: type(Book.objects.filter(id=1))
Out[13]: django.db.models.query.QuerySet
In [12]: Book.objects.get(id=1)
(0.000) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" WHERE "hello_book"."id" = 1; args=(1,)
Out[12]:
In [14]: type(Book.objects.get(id=1))
(0.001) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" WHERE "hello_book"."id" = 1; args=(1,)
Out[14]: hello.models.Book
1.4.4 改
- save的方法简单修改表数据(get和save结合)
In [14]: author = Author.objects.get(id=4)
(0.001) SELECT "hello_author"."id", "hello_author"."name" FROM "hello_author" WHERE "hello_author"."id" = 4; args=(4,)
In [15]: print(author.name)
蓝盾
In [16]: author.name = "蓝视"
In [17]: author.save()
(0.000) BEGIN; args=None
(0.126) UPDATE "hello_author" SET "name" = '蓝视' WHERE "hello_author"."id" = 4; args=('蓝视', 4)
In [18]: print(author.name)
蓝视
- save的方法修改关联表数据(get和save结合)
In [19]: authordetail =AuthorDetail.objects.get(author=author)
(0.001) SELECT "hello_authordetail"."id", "hello_authordetail"."sex", "hello_authordetail"."email", "hello_authordetail"."address", "hello_authordetail"."birthday", "hello_authordetail"."author_id" FROM "hello_authordetail" WHERE "hello_authordetail"."author_id" = 4; args=(4,)
In [20]: print(authordetail.sex)
False
In [21]: authordetail.sex = True
In [22]: print(authordetail.sex)
True
In [23]: authordetail.save()
(0.000) BEGIN; args=None
(0.001) UPDATE "hello_authordetail" SET "sex" = 1, "email" = '[email protected]', "address" = 'www.landun.com', "birthday" = '2017-01-19', "author_id" = 4 WHERE "hello_authordetail"."id" = 5; args=(True, '[email protected]', 'www.landun.com', '2017-01-19', 4, 5)
>>>
- upadte方法更新数据:先使用filter过滤,再用update更新:
In [26]: Publisher.objects.filter(id=4)
Out[26]: (0.001) SELECT "hello_publisher"."id", "hello_publisher"."name", "hello_publisher"."address", "hello_publisher"."city", "hello_publisher"."state_province", "hello_publisher"."country", "hello_publisher"."website" FROM "hello_publisher" WHERE "hello_publisher"."id" = 4 LIMIT 21; args=(4,)
[]
In [27]: Publisher.objects.filter(id=4).update(name="麻省理工出版社")
(0.000) BEGIN; args=None
(0.001) UPDATE "hello_publisher" SET "name" = '麻省理工出版社' WHERE "hello_publisher"."id" = 4; args=('麻省理工出版社', 4)
Out[27]: 1
In [28]: print(Publisher.objects.filter(id=4))
(0.000) SELECT "hello_publisher"."id", "hello_publisher"."name", "hello_publisher"."address", "hello_publisher"."city", "hello_publisher"."state_province", "hello_publisher"."country", "hello_publisher"."website" FROM "hello_publisher" WHERE "hello_publisher"."id" = 4 LIMIT 21; args=(4,)
[]
2 QuerySet 对象的API
方法 | 说明 |
---|---|
get | 返回值是一个modules,仅返回一条数据;如果为空值,会抛出DoesNotExist异常 |
filter | 返回值是一个QuerySet类型的对象 |
all | |
order_by | |
distinct | |
values | 返回的是一个字典 |
values_list | 返回的是一个元组 |
count |
3 关联查询
- 多对多关联查询(外联结查询)【双下划线】
In [31]: Book.objects.filter(title="第一本《Python web 开发》").values('authors')
Out[31]: (0.000) SELECT "hello_book_authors"."author_id" FROM "hello_book" LEFT OUTER JOIN "hello_book_authors" ON ("hello_book"."id" = "hello_book_authors"."book_id") WHERE "hello_book"."title" = '第一本《Python web 开发》' LIMIT 21; args=('第一本《Python web 开发》',)
[{'authors': 1}]
In [32]: Book.objects.filter(title="第一本《Python web 开发》").values('authors__name')
Out[32]: (0.000) SELECT "hello_author"."name" FROM "hello_book" LEFT OUTER JOIN "hello_book_authors" ON ("hello_book"."id" = "hello_book_authors"."book_id") LEFT OUTER JOIN "hello_author" ON ("hello_book_authors"."author_id" = "hello_author"."id") WHERE "hello_book"."title" = '第一本《Python web 开发》' LIMIT 21; args=('第一本《Python web 开发》',)
[{'authors__name': '蓝天'}]
- 多对多关联查询(内链接的查询) 【双下划线】
In [33]: Book.objects.filter(authors__name="蓝天")
Out[33]: (0.118) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" INNER JOIN "hello_book_authors" ON ("hello_book"."id" = "hello_book_authors"."book_id") INNER JOIN "hello_author" ON ("hello_book_authors"."author_id" = "hello_author"."id") WHERE "hello_author"."name" = '蓝天' LIMIT 21; args=('蓝天',)
[, , ]
>>>
- 外键关联查询【双下划线】
>>> AuthorDetail.objects.values('sex','email','address','birthday','author')
(0.001) SELECT `hello_authordetail`.`sex`, `hello_authordetail`.`email`, `hello_authordetail`.`address`, `hello_authordetail`.`birthday`, `hello_authordetail`.`author_id` FROM `hello_authordetail` LIMIT 21; args=()
[{'address': 'www.baidu.com', 'author': 1, 'sex': True, 'birthday': datetime.date(2017, 1, 19), 'email': '[email protected]'}]
>>> AuthorDetail.objects.values('sex','email','address','birthday','author__name')
(0.002) SELECT `hello_authordetail`.`sex`, `hello_authordetail`.`email`, `hello_authordetail`.`address`, `hello_authordetail`.`birthday`, `hello_author`.`name` FROM `hello_authordetail` INNER JOIN `hello_author` ON (`hello_authordetail`.`author_id` = `hello_author`.`id`) LIMIT 21; args=()
[{'address': 'www.baidu.com', 'author__name': '张三2', 'sex': True, 'birthday': datetime.date(2017, 1, 19), 'email': '[email protected]'}]
>>>
- 主键类访问外键类【_set】,使用的是get的方法
主键类访问外键类
In [39]: pub = Publisher.objects.get(name="广东人民出版社")
(0.000) SELECT "hello_publisher"."id", "hello_publisher"."name", "hello_publisher"."address", "hello_publisher"."city", "hello_publisher"."state_province", "hello_publisher"."country", "hello_publisher"."website" FROM "hello_publisher" WHERE "hello_publisher"."name" = '广东人民出版社'; args=('广东人民出版社',)
In [40]: pub.book_set.all()
Out[40]: (0.000) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" WHERE "hello_book"."publisher_id" = 2 LIMIT 21; args=(2,)
[, , ]
外键类访问主键类
In [43]: book = Book.objects.get(title="第十二本《廖雪峰 Python教程》")
(0.000) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" WHERE "hello_book"."title" = '第十二本《廖雪峰 Python教程》'; args=('第十二本《廖雪峰 Python教程》',)
In [44]: book.publisher_set.all()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
----> 1 book.publisher_set.all()
AttributeError: 'Book' object has no attribute 'publisher_set'
4 聚集查询和分组查询
4.0需要导入的库文件
In [1]: from hello.models import *
In [2]: from django.db.models import *
4.1 Count
# 统计名为“广东人民出版社”的出版商的数目
In [3]: Publisher.objects.filter(name="广东人民出版社").aggregate(mycount = Count('name'))
(0.002) SELECT COUNT("hello_publisher"."name") AS "mycount" FROM "hello_publisher" WHERE "hello_publisher"."name" = '广东人民出版社'; args=('广东人民出版社',)
Out[3]: {'mycount': 1}
![Count]](http://upload-images.jianshu.io/upload_images/3982944-c828758817ff50ec.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
4.2 Sum
# 统计姓名为“蓝天”的作者出书的总价
In [6]: Book.objects.filter(authors__name="蓝天").aggregate(Sum('price'))
(0.001) SELECT CAST(SUM("hello_book"."price") AS NUMERIC) AS "price__sum" FROM "hello_book" INNER JOIN "hello_book_authors" ON ("hello_book"."id" = "hello_book_authors"."book_id") INNER JOIN "hello_author" ON ("hello_book_authors"."author_id" = "hello_author"."id") WHERE "hello_author"."name" = '蓝天'; args=('蓝天',)
Out[6]: {'price__sum': Decimal('148.00')}
4.3 values
# 统计每个作者出书的总价
In [7]: Book.objects.values('authors__name').annotate(Sum('price'))
Out[7]: (0.054) SELECT "hello_author"."name", CAST(SUM("hello_book"."price") AS NUMERIC) AS "price__sum" FROM "hello_book" LEFT OUTER JOIN "hello_book_authors" ON ("hello_book"."id" = "hello_book_authors"."book_id") LEFT OUTER JOIN "hello_author" ON ("hello_book_authors"."author_id" = "hello_author"."id") GROUP BY "hello_author"."name" LIMIT 21; args=()
[{'price__sum': Decimal('392.00'), 'authors__name': None}, {'price__sum': Decimal('10.00'), 'authors__name': '蓝云'}, {'price__sum': Decimal('148.00'), 'authors__name': '蓝天'}, {'price__sum': Decimal('153.00'), 'authors__name': '蓝海'}]
4.4 Min
# 找出每个出版社里面价格最低的书
In [8]: Book.objects.values('publisher__name').annotate(Min('price'))
Out[8]: (0.000) SELECT "hello_publisher"."name", CAST(MIN("hello_book"."price") AS NUMERIC) AS "price__min" FROM "hello_book" INNER JOIN "hello_publisher" ON ("hello_book"."publisher_id" = "hello_publisher"."id") GROUP BY "hello_publisher"."name" LIMIT 21; args=()
[{'price__min': Decimal('10.00'), 'publisher__name': '\t广东高等教育出版社'}, {'price__min': Decimal('85.00'), 'publisher__name': '人民教育出版社'}, {'price__min': Decimal('31.00'), 'publisher__name': '广东人民出版社'}, {'price__min': Decimal('28.00'), 'publisher__name': '机械工业出版社'}, {'price__min': Decimal('92.00'), 'publisher__name': '清华大学出版社'}, {'price__min': Decimal('24.00'), 'publisher__name': '电子工业出版社'}, {'price__min': Decimal('87.00'), 'publisher__name': '麻省理工出版社'}]
5 使用原生的SQL查询
5.1 extra【QuerySet】对象
结果集修改器,一种提供额外查询参数的机制,结果是一个【QuerySet】对象
In [7]: type(Book.objects.extra(select={'count':'select count(*) from hello_book'}))
Out[7]: django.db.models.query.QuerySet
# 查询广东人民出版社价格大于50的书
In [2]: Book.objects.filter(publisher__name="广东人民出版社").extra(where=["price > 50"])
Out[2]: (0.001) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" INNER JOIN "hello_publisher" ON ("hello_book"."publisher_id" = "hello_publisher"."id") WHERE ("hello_publisher"."name" = '广东人民出版社' AND (price > 50)) LIMIT 21; args=('广东人民出版社',)
[, ]
# 查询广东人民出版社价格大于50的书
In [3]: Book.objects.filter(publisher__name='广东人民出版社', price__gt=50)
Out[3]: (0.001) SELECT "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" INNER JOIN "hello_publisher" ON ("hello_book"."publisher_id" = "hello_publisher"."id") WHERE ("hello_publisher"."name" = '广东人民出版社' AND "hello_book"."price" > '50') LIMIT 21; args=('广东人民出版社', Decimal('50'))
[, ]
# 汇总书籍表中的书
In [4]: Book.objects.extra(select={'count':'select count(*) from hello_book'})
Out[4]: (0.000) SELECT (select count(*) from hello_book) AS "count", "hello_book"."id", "hello_book"."title", "hello_book"."publisher_id", "hello_book"."publication_date", "hello_book"."price" FROM "hello_book" LIMIT 21; args=()
[, , , , , , , , , , , ]
5.2 raw【RawQuerySet】对象
执行原始sql并返回模型实例,最适合用于查询,结果是一个【QuerySet】对象
注意:RawQuerySet也是惰性的,只有在使用的时候才会被执行,特别是insert的时候要注意。
In [9]: type(Book.objects.raw('select * from hello_book'))
Out[9]: django.db.models.query.RawQuerySet
In [14]: for item in Book.objects.raw('select * from hello_book'):
...: print(item)
...:
(0.000) select * from hello_book; args=()
第一本《Python web 开发》
第二本《Mysql 高级教程》
第三本《HTML与CSS 高级编程》
第四本《BootStrap 框架》
第五本《Django 框架》
第六本《简明Python教程》
第七本《Python web 开发》
第八本《Mysql 底层原理》
第九本《HTML与CSS 基础教程》
第十本《Flask 框架》
第十一本《Tronado 框架》
第十二本《廖雪峰 Python教程》
第七本《Python web 开发》
5.3 直接执行自定义SQL
(这种方式完全不依赖model,前面两种方式还是要依赖于model),适合增删改
In [39]: from django.db import connection
In [40]: cursor = connection.cursor() #获得一个游标(cursor)对象
In [41]: cursor.execute("insert into hello_author(name) values('蓝盾')")
(0.272) insert into hello_author(name) values('蓝盾'); args=None
Out[41]:
In [42]: cursor.execute("update hello_author set name = '蓝网' where name='蓝盾'")
(0.270) update hello_author set name = '蓝网' where name='蓝盾'; args=None
Out[42]:
In [43]: cursor.execute("delete from hello_author where name='蓝网'")
(0.247) delete from hello_author where name='蓝网'; args=None
Out[43]:
In [44]: cursor.execute('select * from hello_author')
(0.000) select * from hello_author; args=None
Out[44]:
In [45]: print(cursor.fetchone()) #执行结果第一条输出
(1, '蓝天')
In [46]: print(cursor.fetchall()) # 所有执行结果输出,由于第一条已经输出,所以游标从第二条开始
[(2, '蓝海'), (3, '蓝云'), (4, '蓝视')