内容概览
- 表查询数据准备及测试环境搭建
- ORM常见查询关键字
- 双下划线的查询
- 查看ORM底层SQL语句
- ORM外键字段创建
- 外键字段数据操作
- 正反向概念
- 跨表查询
表查询数据准备及测试环境搭建
-
django切换为MySQL数据库
-
定义模型类
class User(models.Model): uid = models.AutoField(primary_key=True, verbose_name='编号') name = models.CharField(max_lenght=32, verbose_name='姓名') age = models.IntegerField(verbose_name='年龄') join_time = models.DateField(auto_now_add=True) """ auto_now:每次操作数据并保存都会自动更新为当前时间 auto_now_add:在创建数据的时候保存当前时间,之后不会自动更改 """
-
执行数据库迁移命令
python manage.py makemigrations
python manage.py migrate -
模型层测试环境准备
方式1:在任意空的py文件中或自带的测试文件中准备环境import os def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings') import django django.setup() """在函数内编写测试代码""" if __name__ == '__main__': main()
方式2:pycharm提供测试环境
使用python console命令行测试环境
ORM常见查询关键字
"""
先添加三条数据
models.User.objects.create(name='jason', age=18)
models.User.objects.create(name='kevin', age=24)
models.User.objects.create(name='oscar', age=36)
"""
1. fliter():筛选数据,返回值是Queryset
print(models.User.objects.filter()) # <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>]>
print(models.User.objects.filter(name='jason')) # <QuerySet [<User: User object (1)>]>
"""
括号内不写查询条件则返回所有查询到的数据
括号内可以填写多个条件,使用逗号隔开,默认是and关系
"""
2. all():查询所有数据,返回值是Queryset
print(models.User.objects.all()) # <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>]>
3. first():获取Queryset中第一个数据对象,如果为空则返回None
print(models.User.objects.all().first()) # User object (1)
4. last():获取Queryset中最后一个数据对象,如果为空则返回None
print(models.User.objects.all().last()) # User object (3)
5. get():根据条件获取具体的数据对象,如果对象不存在则报错,返回多个条件也会报错
print(models.User.objects.get(pk=1)) # User object (1)
print(models.User.objects.get(pk=10)) # User matching query does not exist.
print(models.User.objects.get()) # get() returned more than one User -- it returned 3!
6. values():查询指定的字段,结果是Queryset
print(models.User.objects.all().values('name')) # <QuerySet [{'name': 'jason'}, {'name': 'kevin'}, {'name': 'oscar'}]>
print(models.User.objects.values('name') # 也可以直接写values,结果与上边一样
7. values_list():查询指定的字段,结果是Queryset
print(models.User.objects.all().values_list()) # <QuerySet [(1, 'jason', 18, datetime.date(2022, 9, 5)), (2, 'kevin', 24, datetime.date(2022, 9, 5)), (3, 'oscar', 36, datetime.date(2022, 9, 5))]>
8. order_by():指定字段排序,默认为升序,可以在字段前加负号改为降序,并且支持多个字段排序
"""
可在models.py的类中重写__str__方法,能够更清晰知道当前返回的对象
def __str__(self):
return f'{self.name}'
"""
print(models.User.objects.order_by('name')) # <QuerySet [<User: jason>, <User: kevin>, <User: oscar>]>
print(models.User.objects.order_by('-name')) # <QuerySet [<User: oscar>, <User: kevin>, <User: jason>]>
9. count():统计orm查询后结果的个数
print(models.User.objects.all().count()) # 3
10. distinct:针对重复的数据集进行去重,查询的结果必须是完全相同
print(models.User.objects.all().distinct().count()) # 3
print(models.User.objects.all().values('join_date').distinct().count()) # 1
11. exclude:筛选出不符合括号中条件的数据,结果是Queryset
print(models.User.objects.exclude(name='oscar')) # <QuerySet [<User: jason>, <User: kevin>]>
12. reverse():对排过序的结果集做翻转
print(models.User.objects.all()) # <QuerySet [<User: jason>, <User: kevin>, <User: oscar>]>
print(models.User.objects.all().reverse()) # <QuerySet [<User: jason>, <User: kevin>, <User: oscar>]>
print(models.User.objects.all().order_by('name')) # <QuerySet [<User: jason>, <User: kevin>, <User: oscar>]>
print(models.User.objects.all().order_by('name').reverse()) # <QuerySet [<User: oscar>, <User: kevin>, <User: jason>]>
13. exists():判断查询结果集是否有数据,返回布尔值
print(models.User.objects.filter(name='jason').exists()) # True
print(models.User.objects.filter(name='xxx').exists()) # False
14. raw():自己编写SQL语句执行
print(list(models.User.objects.raw('select * from app01_user'))) # [<User: jason>, <User: kevin>, <User: oscar>]
"""也可以使用模块"""
from django.db import connection
cursor = connection.cursor()
cursor.execute("insert into app01_user(name,age, join_date) VALUES ('jerry',12,'2022-9-20')")
cursor.execute("update app01_user set name='tom' WHERE name='kevin'")
cursor.execute("delete from app01_user where name='oscar'")
cursor.execute("select * from app01_user")
print(cursor.fetchone())
print(cursor.fetchall())
双下划线的查询
1. 比较运算符
1. 大于:字段大于指定数据
print(models.User.objects.filter(age__gt=25)) # <QuerySet [<User: oscar>]>
2. 小于:字段小于指定数据
print(models.User.objects.filter(age__lt=25)) # <QuerySet [<User: jason>, <User: kevin>]>
3. 大于等于:字段__gte
4. 小于等于:字段__lte
2. 成员运算符
字段__in:字段中是否有指定数据,指定数据值必须为可迭代对象
print(models.User.objects.filter(age__in=(18,))) # <QuerySet [<User: jason>]>
3. 范围查询
字段__range:获取在某个范围之间的数据
print(models.User.objects.filter(age__range=(20, 30))) # <QuerySet [<User: kevin>]>
4. 模糊查询
字段__contains:查询数据中包含指定值的,不忽略大小写
print(models.User.objects.filter(name__contains='J')) # <QuerySet []>
字段__icontains:查询数据中包含指定值的,忽略大小写
print(models.User.objects.filter(name__icontains='J')) # <QuerySet [<User: jason>]>
5. 日期处理
字段__year:获取日期数据中包含指定年份的数据
print(models.User.objects.filter(join_date__year=2022)) # <QuerySet [<User: jason>, <User: kevin>, <User: oscar>]>
字段__month:获取日期数据中包含指定月份的数据
print(models.User.objects.filter(join_date__month=9)) # <QuerySet [<User: jason>, <User: kevin>, <User: oscar>]>
字段__day:获取日期数据中包含指定天数的数据
print(models.User.objects.filter(join_date__day=5)) # <QuerySet [<User: jason>, <User: kevin>, <User: oscar>]>
查看ORM底层SQL语句
- 如果是却如Queryset对象,那么可以直接使用
.query
查看SQL语句 - 配置文件添加配置,打印所有的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外键字段创建
一对多
外键字段建在多的一方
会自动添加_id后缀
代码:models.ForeignKey()
多对多
外键字段创建多对多关系有三种方式
1. 直接在查询频率较高的表中填写字段即可,自动创建第三张关系表
2. 自己创建第三张关系表
3. 自己创建第三张关系表,但还是使用orm多对多字段做关联
代码:models.ManyToManyField()
一对一
外键字段建在查询频率较高的表中
会自动添加_id后缀
代码:OneToOneField()
ps:django1版本不需要添加on_delete参数
提前准备:
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.IntegerField(verbose_name='单价')
publish_time = models.DateField(auto_now_add=True)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
def __str__(self):
return f'书本:{self.title}'
class Publish(models.Model):
name = models.CharField(max_length=32)
email = models.EmailField()
def __str__(self):
return f'出版社:{self.name}'
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
def __str__(self):
return f'作者:{self.name}'
class AuthorDetail(models.Model):
phone = models.BigIntegerField()
address = models.CharField(max_length=255)
def __str__(self):
return f'作者详情:{self.phone}'
外键字段数据操作
一对多与一对一:
方式1:直接使用实际字段名添加关联数据值
models.Book.objects.create(title='Python', price='998', publish_id=1)
方式2:先获取需要关联的对象,再用虚拟字段名添加关联数据值
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='java', price=888, publish=publish_obj)
多对多:
需要先获取一个对象
models.book_obj = models.Book.objects.filter(pk=1).first()
add():添加数据,可以填写数据值也可以填写数据对象,支持填写多个,使用逗号隔开
book_obj.authors.add(1)
book_obj.authors.add(2,3)
author_obj = models.Author.objects.filter(pk=2).first()
book_obj.authors.add(author_obj)
remove():删除数据,可以填写数据值也可以填写数据对象,支持填写多个,使用逗号隔开
book_obj.authors.remove(1)
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.remove(author_obj1, author_obj2)
set():修改数据,括号内必须填写可迭代对象(实际上是先删除后添加)
book_obj.authors.set([2, 3])
clear():清空指定数据,括号内不用填写参数
book_obj.authors.clear()
正反向概念
正反向的关键就在于外键字段在哪里
正向查询:通过书籍查询出版社,外键字段在书籍表中
反向查询:通过出版社查询书籍,外键字段不在出版社表中
跨表查询
ORM跨表查询口诀:正向查询按外键字段,反向查询按表名小写
基于对象(子查询)
"""正向跨表查询"""
# 1.查询主键为1的书籍对应的出版社(一对多)
# 1.1.先根据条件查询数据对象(先查书籍对象)
book_obj = models.Book.objects.filter(pk=1).first()
# 1.2.以对象为基准 思考正反向概念(书查出版社 外键字段在书表中 所以是正向查询)
print(book_obj.publish)
# 2.查询主键为3的书籍对应的作者(多对多)
book_obj = models.Book.objects.filter(pk=1).first()
print(book_obj.authors) # app01.Author.None
print(book_obj.authors.all()) # 在查询结果有多条时需要使用all
# 3.查询jason的作者详情(一对一)
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.author_detail)
"""反向跨表查询"""
# 4.查询南方出版社出版的书籍
publish_obj = models.Publish.objects.filter(name='南方出版社').first()
print(publish_obj.book) # 报错,还需要添加_set后缀
print(publish_obj.book_set) # app01.Book.None
print(publish_obj.book_set.all()) # 有多个结果还是需要使用all
# 5.查询jason写过的书
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set.all())
# 6.查询电话是110的作者
uthor_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(author_detail_obj.author) # 针对只有单个数据不需要加_set后缀与all
基于双下划线(连表操作)
"""正向跨表查询"""
# 1.查询主键为1的书籍对应的出版社名称及书名
res = models.Book.objects.filter(pk=1).values('publish__name', 'title')
print(res) # 正向依然是用外键字段,查哪个字段就双下划线加字段名即可
# 2.查询主键为3的书籍对应的作者姓名及书名
res = models.Book.objects.filter(pk=3).values('authors__name', 'title')
print(res)
# 3.查询jason的作者的电话号码和地址
res = models.Author.objects.filter(name='jason').values('author_detail__phone', 'author_detail__address')
print(res)
"""反向跨表查询"""
# 4.查询南方出版社出版的书籍名称和价格
res = models.Publish.objects.filter(name='南方出版社').values('book__title', 'book_price')
print(res) # 反向也依然使用表名小写
# 5.查询jason写过的书的名称和日期
res = models.Author.objects.filter(name='jason').values('book__title', 'book__publish_time')
print(res)
# 6.查询电话是110的作者姓名和年龄
res = models.AuthorDetail.objects.filter(phone=111).values('author__name', 'author__age')
print(res)
# 7.查询主键为1的书籍对应的作者电话号码(三张表)
res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
print(res)
"""先从第一张表通过外键字段名进入第二张表,再直接从第二张表的外键字段名查询到第三张表的数据"""