ORM(上)
内容概要
- 模型层之前期准备工作
- ORM常用关键字
- ORM执行SQL语句
- 神奇的双下划线查询
- ORM外键字段的创建
- 外键字段数据的增删改查
- 多表查询(基于对象的跨表查询、基于双下划线的跨表查询)
模型层之前期准备工作
-
自带的
sqlite3
数据库对时间字段不敏感 有时候会展示错乱 素以我们习惯切换成常见的数据库比如MySQL
django orm
并不会自动帮你创建库 所以需要自己提前准备好 -
单独测试
django
某个功能层默认不允许单独测试某个
py
文件 如果想要测试某个py
文件(能单独测试也就models.py
了)-
测试环境1: pycharm提供的
python console
-
测试环境2:自己搭建(自带的test或者自己创建)
-
拷贝
manage.py
前四行import os import sys # 用不到可以删掉 def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoday05.settings')
-
自己在加两行
import django django.setup()
-
django orm
底层还是SQL
语句 我们是可以查看的如果我们手上是一个
QuerySet
对象 那么可以直接点query
查看如果想查看所有
orm
底层的SQL
语句也可以在配置文件添加日志记录LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
-
-
ORM常用关键字
-
create
res = models.User.objects.create(name='jason', age=18) print(res) # 用户对象jason
-
filter()
里面不写入条件 则查所有 写了就可以当成(where
后面的条件 ,多个条件是and
关系)# res = models.User.objects.filter() # 如果什么都不写则查所有 # res 是一个 QuerySet 里面可以看成列表套字典 # print(res) # <QuerySet [<User: 用户对象jason>, <User: 用户对象jason>, <User: 用户对象kevin>, <User: 用户对象oscar>, <User: 用户对象lili>, <User: 用户对象tom>]> res = models.User.objects.filter(name='jason') print(res # <QuerySet [<User: 用户对象jason>]>
-
update
是QuerySet
的方法不是对象的方法models.User.objects.filter(name='jason').update(name='weiwei') # 如果不加筛选条件 则 修改全部 res = models.User.objects.filter() print(res) # <QuerySet [<User: 用户对象weiwei>, <User: 用户对象kevin>, <User: 用户对象oscar>, <User: 用户对象lili>, <User: 用户对象tom>]>
-
delete
也是QuerySet
的方法不是对象的方法models.User.objects.filter(name='tom').delete() # 如果不加筛选条件 则 删除全部 res = models.User.objects.filter() print(res)
-
first()
在filter()
的方法取它的第一个值res = models.User.objects.filter().first() res1 = models.User.objects.filter()[0] print(res) print(res1)
这样看虽然一样 ,但是取一个不存在的索引取值直接报错
而first()则返回
None
res = models.User.objects.filter(id=100).first() print(res)
res1 = models.User.objects.filter(id=100)[0] print(res1)
而我们 那安排需求没有实现,我们也不希望报错
-
last()
取filter()
的最后一个值res = models.User.objects.filter().last() print(res)
-
all()
跟filter()
一样但是语义不同all()
一看就知道是获取全部数据,filter()
是筛选条件res = models.User.objects.all().values('name') print(res)
-
values()
根据指定的字段获取数据res = models.User.objects.filter().values('name') # 如果不写限制条件则 返回每条数据的全部 # 如果写了限制条件则 返回每条数据的这个字段 print(res)
可以看到这个
QuerySet
里面是列表套字典 字典的键是限制的字段名
值是对应的值 -
values_list()
也是根据指定的字段获取数据
res = models.User.objects.filter().values_list('name')
print(res)
获取的是列表套元组
-
distinct()
去重 数据一定要一模一样,一定要不主键字段排除在外,有主键字段在根本去重不了res = models.User.objects.filter().values('name') print(res) res = models.User.objects.filter().values('name').distinct() print(res)
res = models.User.objects.filter() print(res) res = models.User.objects.filter().distinct() print(res)
-
order_by()
根据指定条件排序 默认是升序res = models.User.objects.filter().values('name', 'age') print(res) print('-------------------------------------') res = models.User.objects.filter().order_by('age').values('name', 'age') print(res)
如果想降序,则把条件前面加
符号
res = models.User.objects.filter().values('name', 'age') print(res) print('-------------------------------------') res = models.User.objects.filter().order_by('-age').values('name', 'age') print(res)
-
get()
根据条件筛选数据并直接获取到数据对象,一旦条件不存在会直接报错 不建议使用res = models.User.objects.get(pk=100) print(res)
```python
res = models.User.objects.filter(pk=100)
print(res)
```
![](https://img2023.cnblogs.com/blog/2490064/202212/2490064-20221214215242321-72923446.png)
而 `filter`不会
-
exclude()
取反操作res = models.User.objects.filter().values('name', 'age') print(res) res = models.User.objects.filter().values('name', 'age').exclude(age=18) print(res)
-
reverse()
颠倒顺序(被操作的对象必须是已经排过序的才可以)res = models.User.objects.filter().values('name', 'age') print(res) print('--------------------') res = models.User.objects.filter().values('name', 'age').reverse() print(res) print('--------------------') res = res = models.User.objects.filter().values('name','age').order_by('age') print(res) print('--------------------') res = models.User.objects.filter().values('name','age').order_by('age').reverse() print(res)
ORM执行SQL语句
有时候ORM的操作效率可能偏低 我们自己编写SQL的
方式1:
res = models.User.objects.raw('select * from app01_user;')
print(list(res))
只能执行查询,如果要执行增删改
还得需要别的执行sql
方法
方式2:
import pymysql
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='db6',
charset='utf8',autocommit=True)
cursor = conn.cursor()
cursor.execute('insert into app01_user(name, age) values("lili", 18);')
cursor.execute('select * from app01_user')
res = cursor.fetchone()
for i in res:
print(i)
方式3:
from django.db import connection
cursor = connection.cursor()
cursor.execute('insert into app01_user(name, age) values("kevin", 19)')
cursor.execute('select * from app01_user')
res = cursor.fetchall()
for i in res:
print(i)
from django.db import connection
是连接一个数据库的
from django.db import connections 可以连接多个数据库
神奇的双下划线查询
只要是QuerySet
对象都可以无限制的点QuerySet
对象的方法
-
__gt
大于res = models.User.objects.filter(age__gt=18).values('name', 'age') print(res) 大于等于 res=models.User.objects.filter(age__gte=18).values('name','age') print(res)
-
__lt
小于res = models.User.objects.filter(age__lt=38).values('name','age') print(res) 小于等于 res = models.User.objects.filter(age__lte=38).values('name', 'age') print(res)
-
查询年龄是18或者28,38的数据
res = models.User.objects.filter(age__in=(18,28,38)).values('name','age') print(res)
-
查询年龄在18到38范围之内的
res = models.User.objects.filter(age__range=(18,38)).values('name','age') print(res)
-
查询名字含有字母
j
的数据res = models.User.objects.filter(name__contains='j').values('name','age') print(res)
大小写模糊的
res = models.User.objects.filter(name__icontains='j').values('name','age') print(res)
-
查询注册年份是
2022
的数据res = models.User.objects.filter(register__year=2022).values('name','register') print(res)
ORM外键字段的创建
在MySQL中的表关系有
一对多
外键创建在多的一方
多对多
外键创建在第三张表
一对一
外键创建在热数据的一方 热数据就是访问频率高的一方
没有关系
-
创建基础表(书籍表,出版社表,作者表,作者详情表)
class Book(models.Model): """书的表 """ name = models.CharField(max_length=32, verbose_name='书名') price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格') # 书跟出版社 一本书的版权只能有一个出版社 出版社可以有多本书 # 外键创建在多的一方 这一对多必须要有 on_delete 要不然报错 publish = models.ForeignKey(to='publish', on_delete=True, verbose_name='外键 出版社') # 书跟作者 一本书可以有多个作者 一个作者可以写多本书 # 多对多 orm会在自动创建第三张表 你也可以自己创建第三张表 authors = models.ManyToManyField(to='Author') class Publish(models.Model): """ 出版社 """ name = models.CharField(max_length=32, verbose_name='出版社名称') addr = models.CharField(max_length=64, verbose_name='出版社地址') class Author(models.Model): """作者表""" name = models.CharField(max_length=32, verbose_name='名字') # 作者跟作者详情表 是一对一的关系 # 外键可以创建在 任意一个表中 author_detail = models.OneToOneField(to='AuthorDetail', on_delete=True, verbose_name='外键 作者详情') class AuthorDetail(models.Model): phon = models.CharField(max_length=32, verbose_name='手机号') age = models.IntegerField(verbose_name='年龄')
-
确认外键关系
一对多 ORM与MySQL一致 外键字段建立在多的一方
多对多 ORM与MySQL有更多的变化
-
外键字段可以直接建立在某张表中(查询频率较高的)
内部会自动创建第三张表关系
-
自己创建第三张表关系并创建外键字段
一对一 ORM与MySQL一致 外键字段建在查询较高的一方
-
-
ORM创建
针对一对多和一对一同步到表中会自动加
_id
后缀自动创建第三张表
外键字段相关操作
针对一对多 插入数据直接填写表中字段
models.Book.objects.create(name='sanguo',price=888.88,publish_id=2)
models.Book.objects.create(name='shitouji',price=888.88,publish_id=3)
models.Book.objects.create(name='xiyouji',price=888.88,publish_id=4)
models.Book.objects.create(name='shuixuzhuan',price=888.88,publish_id=2)
# 针对一对多 插入数据也可以填写表中的类中字段名
# publish_obj = models.Publish.objects.filter(pk=1).first()
# models.Book.objects.create(title='水浒传', price=555.66, publish=publish_obj)
"""针对多对多的关系绑定"""
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.add(2)
book_obj.authors.add(2,3)
"""如果关系已有则不管,"""
author_obj = models.Author.objects.filter(pk=1).first()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.add(author_obj)
可以放一个数字 也可以放一个作者对象
修改关系
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.set((1, 2)) # 里面必须是一个参数
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set((author_obj1, author_obj2)) # 也可以放一个对象
删除关系
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.remove(author_obj1, author_obj2)
清空关系
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.clear()
add 增加关系
对象.外键.add(可以是被关联对象也可以是int类型)
remove
对象.外键.remove(可以是被关联对象也可以是int类型)
set
对象.外键.set((可以是被关联对象,也可以是int类型))必须在容器里
clear()
对象.外键.clear()
ORM跨表查询
"""
复习MySQL跨表查询的思路
子查询
分布操作:将一条SQL语句用括号括起来当做另外一条SQL语句的条件
连表操作
先整合多张表之后基于单表查询即可
inner join 内连接
left join 左连接
ringht join 右连接
"""
正反向查询的概念(重要)
正向查询
由外键字段所在的表数据查询关联的表数据 正向
反向查询
没有外键字段的表数据查询关联的表数据 反向
ps:正反向的核心就看外键字段在不在当前数据所在的表中
ORM跨表查询的口诀(重要)
正向查询按外键字段
反向查询按表名小写
基于对象的跨表查询
1.查询主键为2的书籍对应的出版社名称
"""
先获取主键为2的书籍对象
书籍到 出版社 外键字段在 数据 正向 点外键字段
"""
book_obj = models.Book.objects.filter(pk=2).first()
# book_obj.publish 这样一点就到了 publish的这张表了
print(book_obj.publish.name)
2.查询主键为4的书籍对应的作者姓名
"""
先获取主键为4的数据对象
书籍找作者 外键在书籍 正向 点外键
"""
book_obj = models.Book.objects.filter(pk=4).first()
print(book_obj.authors)
# app01.Author.None 碰到这种不要怕这就离成功就差一步了
print(book_obj.authors.all()) # 获取里面的数据
print(book_obj.authors.all().first().name) # 那里面对象的name
# 3.查询jason的电话号码
"""
先获取jason对象 然后去 作者详情表拿电话号码
"""
# author_obj = models.Author.objects.filter(name='jason').first()
# print(author_obj.author_detail.phon)
# 4.查询BeiFang出版过的书籍
"""
先获取BeiFang的对象
然后从出版社 到 书记 外键 在数据 反向 反向表名小写
"""
# publish_obj = models.Publish.objects.filter(name='BeiFang').first()
# print(publish_obj.book_set) # app01.Book.None
# """看到 app01.Book.None"""
# print(publish_obj.book_set.all().values('name'))
# 5.查询jason写过的书籍
"""
先获取json对象
然后从 作者到书籍 主键在 书籍 反向
反向点书名小写
"""
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set)
print(author_obj.book_set.all().values('name'))
"""
app01.Book.None
<QuerySet [{'name': 'shitouji'}, {'name': 'xiyouji'}, {'name': 'shuixuzhuan'}]>
"""
基于双下划线的跨表查询
就是把上面的操作两行变为一行
# 1.查询主键为2的书籍对应的出版社名称
"""
先获取书籍对象
然后看正反向
书籍到出版社 外键在书籍 正向
"""
# res = models.Book.objects.filter(pk=2).values('外键__外键的字段')
res = models.Book.objects.filter(pk=2).values('publish__name', 'name')
print(res)
# 2.查询主键为4的书籍对应的作者姓名
"""
书籍到作者 正向
"""
res = models.Book.objects.filter(pk=4).values('authors__name')
print(res)
# 3.查询jason的电话号码
res = models.Author.objects.filter(name='jason').values('author_detail__phon')
print(res)
# 5.查询jason写过的书籍名称
"""
反向 表名小写
"""
res = models.Author.objects.filter(name='jason').values('book__name')
print(res)
# 6.查询电话号码是110的作者姓名
res = models.AuthorDetail.objects.filter(phon=110).values('author__name')
print(res)
ps:当关系是多的时候需要加_set
进阶操作
# 1.查询主键为2的书籍对应的出版社名称
"""
反着写 先写 搞出版社对象
出版社到书籍反向 反向表名小写
"""
res = models.Publish.objects.filter(book__pk=2).values('name')
# print(res)
# 2.查询主键为4的书籍对应的作者姓名
res = models.Author.objects.filter(book__pk=4).values('name')
# print(res)
# 3.查询jason的电话号码
res = models.AuthorDetail.objects.filter(author__name='jason').values('phon')
# print(res)
# 4.查询BeiFang出版社出版过的书籍名称和价格
# res = models.Book.objects.filter(publish__name='BeiFang').values('name', 'price')
# print(res)
# 5.查询jason写过的书籍名称
res = models.Book.objects.filter(authors__name='jason').values('name')
print(res)
# 6.查询电话号码是110的作者姓名
res = models.Author.objects.filter(author_detail__phon=110).values('name')
print(res)
main()