django 模型层
ORM 关键字
数据库迁移命令
正向迁移(将类操作映射到表中)
python3 manage.py makemigrations
python3 manage.py migrate
反向迁移(将表映射成类操作)
python3 manage.py inspectdb
搭建测试环境
"""
django自带的sqlite3数据库 功能很少 并且针对日期类型不精确
1.数据库正向迁移命令(将类操作映射到表中)
python3 manage.py makemigrations
python3 manage.py migrate
2.数据库反向迁移命令(将表映射成类)
python3 manage.py inspectdb
"""
需求
我们只想操作orm 不想使用网络请求
需要有专门的测试环境
1.自己搭建
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day54.settings")
import django
django.setup()
2.pycharm提供
python console
数据准备
ORM 查询关键字
res1 = models.User.objects.all() # 获取所有对象 结果是列表套对象
res2 = models.User.objects.filter() # 在不加条件的时候等同于all
res3 = models.User.objects.filter(pk=1) # 获取符合条件的对象
res4 = models.User.objects.filter(pk=1)[0] # 获取索引值为0的对象
res5 = models.User.objects.filter(pk=1).first() # 获取第一个对象
res6 = models.User.objects.filter(pk=1, name='kevin') # 获取符合条件的数据,结果是字典
res7 = models.User.objects.filter().filter().filter() # 可重复使用
res8 = models.User.objects.filter().last() # 获取最后一个
res9 = models.User.objects.all().values() # 获取所有的值,结果为列表套字典
res10 = models.User.objects.all().values('name', 'age') # 获取指定字段的数据
res101 = models.User.objects.values('name', 'age') # 同上
res11 = models.User.objects.filter(pk=1).values('name') # 获取符合条件数据的某个字段的数据
res12 = models.User.objects.all().values_list('name', 'age') # 获取指定字段的数据,列表套元组
res13 = models.User.objects.all().distinct() # 去重
res14 = models.User.objects.order_by('age') # 排序(默认升序)
res15 = models.User.objects.order_by('-age') # 降序
res16 = models.User.objects.order_by('age', 'pk') # 多字段排序
res17 = models.User.objects.exclude(name='oliver') # 取反
res18 = models.User.objects.values('name').distinct()
res19 = models.User.objects.reverse() # 不起作用
res20 = models.User.objects.order_by('age').reverse() # 只有排序后才有作用
res21 = models.User.objects.order_by('age')
res22 = models.User.objects.count() # 统计结果集数
res23 = models.User.objects.exists()
res24 = models.User.objects.filter(name='kevin').exists() # 判断结果是否存在
res25 = models.User.objects.get(pk=1)
# res26 = models.User.objects.get(pk=100) # 获取不到直接报错
res27 = models.User.objects.filter(pk=100) # 找不到返回None
双下划线查询
# 1. 查询年龄大于20的用户
'''
__gt 大于
__lt 小于
__gte 大于等于
__lte 小于等于
__in 成员运算
__range 范围查询
__contains 模糊查询 区分大小写
__icontains 模糊查询 忽略大小写
__startwith 起始
__endwith 结束
__regex 正则
'''
res = models.User.objects.filter(age>20) # 不可用
res = models.User.objects.filter(age__gt=20). # 大于
res = models.User.objects.filter(age__lt=20). # 小于
# 2. 查询年龄是18、22、25的用户
res = models.User.objects.filter(age__in=[8, 38]) # 成员运算
res = models.User.objects.filter(age__range=[28, 38]) # 范围查找,包含头尾
res = models.User.objects.filter(name__contains='o') # 模糊查询,不忽略大小写
res = models.User.objects.filter(name__icontains='o') # 模糊查询,忽略大小写
# 3. 日期查询
"""
op_time__year 按照年份
op_time__month 按照月份
op_time__day 按照日
"""
res = models.User.objects.filter(op_time__month=5) # 按照月份筛选
print(res)
res = models.User.objects.filter(pk=1).values_list()
print(res)
ORM外键字段的创建
复习知识(MySQL创建外键字段)
表关系的种类
一对多关系
多对多关系
一对一关系
表关系的判断
换位思考
外键字段的位置
一对多关系 外键字段建在多的一方
多对多关系 外键字段建在第三张关系表中
一对一关系 外键字段建在任意一方都可以 但是推荐建在查询频率较高的表中
django ORM 创建表关系
创建的表(书、作者、出版社、作者信息)
步骤:
- 搭建基础的表字段:只创建一些基础的字段
- 判断表间关系:判断表间关系,然后选择搭建外键的表
类似MySQL中外键搭建的规律:
- 一对多关系:外键放在多的一方
- 多对多关系:外键放在第三张表里,不过第三张表是不需要自己创建的
- 一对一关系:两方皆可,推荐放在查询频率较高的一方
- 创建外键:根据不同的表间关系,选择不同类型的关键字
- 一对多关系:
ForeignKey
- 多对多关系:
ManyToManyField
- 一对一关系:
OneToOneField
- 外键补充
ManyToManyField
不会在表中创建实际的字段 而是告诉django orm自动创建第三张关系表ForeignKey
、OneToOneField
会在字段的后面自动添加_id
后缀 如果你在定义模型类的时候自己添加了该后缀那么迁移的时候还会再次添加_id_id 所以不要自己加_id后缀
- 外键参数补充
to="……"
:指定外键关联的表,默认关联该表主键to_field\to_fields
:指定外键关联的表的字段
class Book(models.Model):
"""图书表"""
title = models.CharField(max_length=32, verbose_name='书名')
price = models.DecimalField(max_digits=8, decimal_places=2)
pub_time = models.DateField(auto_now=True)
# 书与出版社外键
publish = models.ForeignKey(to='Publish') # 默认关联主键字段
# 书与作者外键
authors = models.ManyToManyField(to='Author')
def __str__(self): # 自行打印操作时自动触发
return '书对象:%s' % self.title
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
def __str__(self):
return '出版社对象:%s' % self.name
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
# 作者与详情(一对一关系)
detail = models.OneToOneField(to='AuthorDetail')
class AuthorDetail(models.Model):
phone = models.BigIntegerField()
addr = models.CharField(max_length=64)
def __str__(self):
return '作者详细信息:电话%s, 地址%s' % (self.phone, self.addr)
外键字段操作
# 一对多、一对一关系的ORM操作
# 增
# models.Book.objects.create(title='你好', price=234.56, publish_id=1) # 直接填写关联数据的主键值
# 1. 先获取主键值为1的出版社对象
# pub_obj = models.Publish.objects.filter(pk=1).first()
# 2. 在参数里选择关联的表名关键字参数,将获取的出版社对象传给该参数
# models.Book.objects.create(title='哈哈哈', price='654.32', publish=pub_obj)
# 改
# models.Book.objects.filter(pk=1).update(publish_id=3) # 直接填写要修改的参数
# pub_obj = models.Publish.objects.filter(pk=1).first()
# models.Book.objects.filter(pk=1).update(publish=pub_obj)
# 多对多 ORM字段操作
# 增
# book_obj = models.Book.objects.filter(pk=6).first() # 获取主键值为2的书对象
# book_obj.authors.add(3) # 将获取到的书对象在第三张表里与主键为3的作者关联起来
# book_obj.authors.add(1, 2, 3) # 将获取到的书对象在第三张表里与id为1,2,3的作者关联起来
# author_obj1 = models.Author.objects.filter(pk=1).first()
# author_obj2 = models.Author.objects.filter(pk=2).first()
# book_obj.authors.add(author_obj1, author_obj2) # 也可以先获取书对象和作者对象,然后将两者匹配
# 改
# book_obj = models.Book.objects.filter(pk=6).first()
# book_obj.authors.set([3, ]) # 更改主键值为6的书对象对应的作者 参数必须是可迭代对象
# book_obj.authors.set([2, 1])
# author_obj1 = models.Author.objects.filter(pk=1).first()
# author_obj2 = models.Author.objects.filter(pk=3).first()
# book_obj.authors.set([author_obj1, author_obj2]) # 也可以先获取作者对象
# 移除关系
# book_obj = models.Book.objects.filter(pk=6).first()
# book_obj.authors.remove(3, 1) # 直接写主键值移除
# author_obj1 = models.Author.objects.filter(pk=1).first()
# author_obj2 = models.Author.objects.filter(pk=2).first()
# book_obj.authors.remove(author_obj1, author_obj2) # 先获取作者对象再移除
# 清空关系
# book_obj = models.Book.objects.filter(pk=2).first()
# book_obj.authors.clear() # 移除主键值为2的书对象与其他表的关系 ,无需传值
多表查询
知识回顾(MySQL多表查询思路)
子查询
通过将SQL语句加括号当做条件使用,不断叠加SQL语句
连表操作
通过关键字inner join\left join\right join\union
将表按照某一字段进行拼接
django ORM多表查询
其实django ORM也是采用以上的两种思路
ORM 子查询:基于对象的跨表查询
ORM 连表操作:基于双下划线的跨表查询
正反向概念
正反向的判断标准是看当前的数据对象是否含有外键字段,含有外键是正向,不含有是反向
示例
- 正向
- 由书籍查询出版社 外键字段在书籍表中 那么书查出版社就是'正向'
- 由书籍查询作者 外键字段在书籍表中 那么书查作者就是'正向'
- 由作者查询作者详情 外键字段在作者表中 那么也是'正向'
- 反向
- 由出版社查询书籍 外键字段不在出版社表 那么出版社查书就是'反向'
正反向查询口诀
正向查询按外键字段名 反向查询按表名小写
基于对象的跨表查询(类似MySQL子查询)
步骤:
- 先获取目前要使用的数据对象
形式:obj = models.类名.objects.filter(条件).first()
- 再使用跨表查询
正向查询 (正向按外键字段名)
查询结果单一情况
obj.外键字段名
查询结果不单一
obj.外键字段名.all()
# 1. 查询主键为6的书所对应的出版社
# 先获取现在要使用的对象
# book_obj = models.Book.objects.filter(pk=6).first()
# 再进行跨表查询
# res = book_obj.publish
# print(res)
# 2. 查询主键值为6的书的作者
# book_obj = models.Book.objects.filter(pk=6).first()
# res = book_obj.authors # app01.Author.None
# res = book_obj.authors.all() # <QuerySet [<Author: Author object>, <Author: Author object>, <Author: Author object>]>
# res = book_obj.authors.all().values() # <QuerySet [{'id': 1, 'name': 'oliver', 'age': 18, 'detail_id': 1}, {'id': 2, 'name': 'kevin', 'age': 21, 'detail_id': 2}, {'id': 3, 'name': 'oscar', 'age': 26, 'detail_id': 3}]>
# res = book_obj.authors.all().values('name') # <QuerySet [{'name': 'oliver'}, {'name': 'kevin'}, {'name': 'oscar'}]>
# print(res)
# 3. 查询作者'oliver'的详细信息
# author_obj = models.Author.objects.filter(name='oliver').first()
# res = author_obj.detail
# print(res)
反向查询 (反向按表名小写)
obj.表名小写
如果获得的结果是报错'……' object has no attribute '……'
,则将obj.表名小写
修改为obj.表名小写_set
,然后如果结果为应用名.类名.None
,则将obj.表名小写_set
修改为obj.表名小写_set.all()
# 4. 查询某一出版社出版的书籍
# pub_obj = models.Publish.objects.filter(name='东').first()
# res = pub_obj.book_set # app01.Book.None
# res = pub_obj.book_set.all() # <QuerySet [<Book: 书对象:玄武>, <Book: 书对象:貔貅>]>
# res = pub_obj.book_set.all().values() # [{'id': 4, 'title': '玄武', 'price': Decimal('444.44'), 'pub_time': datetime.date(2013, 10, 17), 'publish_id': 1}, {'id': 7, 'title': '貔貅', 'price': Decimal('777.77'), 'pub_time': datetime.date(2022, 7, 12), 'publish_id': 1}]>
# res = pub_obj.book_set.all().values('title') # <QuerySet [{'title': '玄武'}, {'title': '貔貅'}]>
# print(res)
# 5. 查询某作者写的书
# author_obj = models.Author.objects.filter(name='oliver').first()
# res = author_obj.book_set.all().values('title')
# print(res)
# 6. 查询电话是110 的作者
# author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
# res = author_detail_obj.author # Author object
# res = author_detail_obj.author # 作者对象: oliver
# print(res)
基于下划线的跨表查询(类似MySQL的连表操作)
步骤
1. 手上有什么条件就先拿models
点该条件对应的表名
形式:models.类名.objects
2. 然后添加条件选定数据models.类名.objects.filter(条件)
3. 然后通过点values('目标表名__目标字段')
的方式获取目标信息
# 1. 查询某书籍对应的出版社名称
# res = models.Book.objects.filter(title='饕餮').values('publish__name') # <QuerySet [{'publish__name': '西'}]>
# 2. 查询某书对应的作者姓名年龄
# res = models.Book.objects.filter(title='朱雀').values('authors__name', 'authors__age') # <QuerySet [{'authors__name': 'oliver', 'authors__age': 18}, {'authors__name': 'kevin', 'authors__age': 21}]>
# 3. 查询某作者的手机号和地址
# res = models.Author.objects.filter(name='oliver').values('detail__addr', 'detail__phone') # <QuerySet [{'detail__addr': '杭州', 'detail__phone': 110}]>
# 4.查询某出版社出版的书籍名称和价格
# res = models.Publish.objects.filter(name='西').values('book__title', 'book__price') # <QuerySet [{'book__title': '白虎', 'book__price': Decimal('222.22')}, {'book__title': '饕餮', 'book__price': Decimal('888.88')}]>
# 5. 查询某作者编写的书籍名称和日期
# res = models.Author.objects.filter(name='oliver').values('book__title', 'book__pub_time') # <QuerySet [{'book__title': '朱雀', 'book__pub_time': datetime.date(2011, 5, 18)}, {'book__title': '金龙', 'book__pub_time': datetime.date(2010, 9, 9)}, {'book__title': '白虎', 'book__pub_time': datetime.date(2026, 6, 16)}, {'book__title': '穷奇', 'book__pub_time': datetime.date(2018, 11, 14)}]>
# 6. 查询某电话的主人的姓名和年龄
# res = models.AuthorDetail.objects.filter(phone=110).values('author__name', 'author__age') # <QuerySet [{'author__name': 'oliver', 'author__age': 18}]>
# print(res)
基于双下划线查询的扩展
- 基于双下划线查询的结果也可以是完整的数据对象
- 手上有条件所在的表可以不被models点 直接点最终的目标数据对应的表
先通过条件筛选到指定的作者(完整的对象),然后点属性的方式获得信息
# 1. 查询某书对应的出版社
# res = models.Publish.objects.filter(book__title='青龙') # <QuerySet [<Publish: 出版社对象:南>]>
# 2. 查询某书的作者的姓名和年龄
# res = models.Author.objects.filter(book__title='金龙').values('name', 'age') # <QuerySet [{'name': 'oscar', 'age': 26}]>
"""先通过条件筛选到指定的作者(完整的对象),然后点属性的方式获得信息"""
# 3. 查询某作者的手机号和地址
# res = models.AuthorDetail.objects.filter(author__name='oliver') # QuerySet [<AuthorDetail: 作者详细信息:电话110, 地址杭州>]>
# 4. 查询某出版社出版的书籍信息
# res = models.Book.objects.filter(publish__name='北').values('title', 'price') # <QuerySet [{'title': '金龙', 'price': Decimal('666.66')}, {'title': '穷奇', 'price': Decimal('999.99')}]>
# 5. 查询某作者编写的书籍信息
# res = models.Book.objects.filter(authors__name='kevin').values('title', 'pub_time') # <QuerySet [{'title': '朱雀', 'pub_time': datetime.date(2011, 5, 18)}, {'title': '金龙', 'pub_time': datetime.date(2010, 9, 9)}, {'title': '凤凰', 'pub_time': datetime.date(2019, 1, 15)}, {'title': '饕餮', 'pub_time': datetime.date(2023, 5, 19)}]>
# 6. 查询某电话的作者的姓名和年龄
# res = models.Author.objects.filter(detail__phone=110).values('name', 'age') # <QuerySet [{'name': 'oliver', 'age': 18}]>
# print(res)
连续跨表操作
其实连续跨表操作也是通过双下划线的方式在不同的表里进行跨越
# 查询某书的作者的手机号
# res = models.Book.objects.filter(title='金龙').values('authors__detail__phone') # <QuerySet [{'authors__detail__phone': 110}, {'authors__detail__phone': 123}, {'authors__detail__phone': 151}]>
# res = models.Book.objects.filter(title='金龙').values('authors__detail__phone')
# res = models.AuthorDetail.objects.filter(author__book__title='朱雀').values('phone')
# 获取多个表里的数据
# res = models.Book.objects.filter(title='青龙').values('publish__name', 'authors__detail__phone', 'title') # <QuerySet [{'publish__name': '南', 'authors__detail__phone': 151, 'title': '青龙'}]>
# print(res)
如何查看SQL语句
方式1:如果结果集对象是queryset
那么可以直接点query
查看
方式2:配置文件固定配置
适用面更广 只要执行了orm操作 都会打印内部SQL语句
有些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',
},
}
}