今日内容详细
- 表查询数据准备及测试环境搭建
- 表查询关键字
- 外键创建
- 外键数据增删改查
- 多表查询(子查询、连表操作)
表查询数据准备及测试环境搭建
1.django自带一个sqlite3小型数据库
说明:该数据库功能非常有限,并且针对日期类型的数据兼容性很差。
2. django切换MySQL数据
- django1.X版本
导入模块pymysql: import pymysql pymysql.install_as_MySQLdb()
- django2.X 3.X 4.X
下载mysqlclient模块: pip install mysqlclient
3.定义模型类
class User(models.Model): uid = models.AutoField(primary_key=True,verbose_name='编号') name = models.CharField(max_length=32,verbose_name='姓名') age = models.IntegerField(verbose_name='年龄') join_time = models.DateField(auto_now_add=True) """ auto_now:每次操作数据并保存都会自动更新当前时间; auto_now_add:只在创建数据的那一刻自动获取当前时间,之后如果不认为更改则不变。 """
4.执行数据库迁移命令(模型类>>>表)
makemigrations
migrate
5.模型层测试环境准备
- 方式1:在任意空的py文件中准备环境(拷贝manage.py文件内一部分代码)
import os def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings') # 必须要等到上述环境准备好才可以导下面django和models模块!!! import django django.setup() from app01 import models print(models.User.objects.filter()) if __name__ == '__main__': main()
- 方式2:pycharm提供测试环境
python console命令行测试环境
ORM常见查询关键字
1/扩展知识点
- 当需要查询数据主键字段值的时候,可以使用pk忽略掉数据字段真正的名字;
- 在模型类中可以定义一个__str__方法,便于后续数据对象被打印展示的是查看方便;
- Queryset中如果是列表套对象那么直接for循环和索引取值但是索引不支持负数;
- 虽然queryset支持索引但是当queryset没有数据的时候索引会报错 推荐使用first。
2.常见查询关键字
(1)批量操作数据。
- create()
创建数据方式一,返回值就是当前创建的数据对象;import os def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings') import django django.setup() from app01 import models res = models.User.objects.create(name='kevin',age=28) print(res.name) # kevin print(res.age) # 28 print(res.join_time) # 2022-09-13 print(res.pk) # 3 if __name__ == '__main__': main()创建对象方式二:还可以利用类实例化对象然后调用save方法创建
user_obj = models.User(name='oscar',age=28) user_obj.save()
- update
更新数据models.User.objects.filter(pk=5).update(name='jasonnb')
- delete()
删除数据models.User.objects.filter(pk=2).delete()
(2)13种必会操作。
- filter()
筛选数据,返回值是一个QuerySet(可以看成是列表套数据对象)
(1)括号内不写查询条件 默认就是查询所有res = models.User.objects.filter() print(res) # <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>, <User: User object (4)>, <User: User object (5)>]>(2)括号内可以填写条件 并且支持多个 逗号隔开 默认是and关系
res = models.User.objects.filter(name='oscar',age=28) print(res) # <QuerySet [<User: 对象:oscar>]>
- all()
查询所有数据 返回值是一个QuerySet(可以看成是列表套数据对象),即可以for循环取值以及索引取值。res = models.User.objects.all() print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
- first()
获取Queryset中第一个数据对象 如果为空则返回Noneres = models.User.objects.all() print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]> print(res.first()) # 对象:jason
- last()
获取Queryset中最后一个数据对象 如果为空则返回Noneres = models.User.objects.last() print(res) # 对象:oscar
- get()
直接根据条件查询具体的数据对象 但是条件不存在直接报错 不推荐使用res = models.User.objects.get(pk=3) print(res) # 对象:kevin
- values()
指定查询字段 结果是Queryset(可以看成是列表套字典数据)res = models.User.objects.values('name','age') print(res) # <QuerySet [{'name': 'jason', 'age': 18}, {'name': 'jason', 'age': 18}, {'name': 'kevin', 'age': 28}, {'name': 'kevin', 'age': 28}, {'name': 'oscar', 'age': 28}]>
- values_list()
指定查询字段 结果是Queryset(可以看成是列表套元组数据)res = models.User.objects.values_list('name','join_time') print(res) # <QuerySet [('jason', datetime.date(2022, 9, 13)), ('jason', datetime.date(2022, 9, 13)), ('kevin', datetime.date(2022, 9, 13)), ('kevin', datetime.date(2022, 9, 13)), ('oscar', datetime.date(2022, 9, 13))]>
- order_by()
指定字段排序 默认是升序 在字段前加负号则为降序 并且支持多个字段排序res = models.User.objects.order_by('age') # 升序 print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]> res = models.User.objects.order_by('-age') # 降序 print(res) # <QuerySet [<User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>, <User: 对象:jason>, <User: 对象:jason>]>
- count()
统计orm查询之后结果集中的数据格式res = models.User.objects.all().count() print(res) # 5
- distinct()
针对重复的数据集进行去重 一定要注意数据对象中的主键(带主键后不能去重)res = models.User.objects.values('name','age').distinct() print(res) # <QuerySet [{'name': 'jason', 'age': 18}, {'name': 'kevin', 'age': 28}, {'name': 'oscar', 'age': 28}]>
- exclude()
针对括号内的条件取反进行数据查询 QuerySet(可以看成是列表套数据对象)res = models.User.objects.exclude(pk = 1) print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
- reverse()
针对已经排了序的结果集做颠倒res = models.User.objects.all().order_by('age') print(res.reverse()) # <QuerySet [<User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>, <User: 对象:jason>, <User: 对象:jason>]>
- exists()
判断查询结果集是否有数据 返回布尔值 但是几乎不用因为所有数据自带布尔值res = models.User.objects.filter(pk=100).exists() print(res) # False
- raw()
执行SQL语句;
res = models.User.objects.raw('select * from app01_user') print(res) # 原生sql语句:<RawQuerySet: select * from app01_user>还可以借助于模块。
from django.db import connection cursor = connection.cursor() cursor.execute("insert into hello_author(name) VALUES ('郭敬明')") cursor.execute("update hello_author set name='韩寒' WHERE name='郭敬明'") cursor.execute("delete from hello_author where name='韩寒'") cursor.execute("select * from hello_author") cursor.fetchone() cursor.fetchall()
神奇的双下划线的查询
1.比较运算符
- 字段__gt(大于)
res = models.User.objects.filter(age__gt=20) print(res) # <QuerySet [<User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
- 字段__lt(小于)
res = models.User.objects.filter(age__lt=20) print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>]>
- 字段__gte(大于等于)
res = models.User.objects.filter(age__gte=18) print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
- 字段__lte (小于等于)
res = models.User.objects.filter(age__lte=18) print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>]>
2.成员运算符
- 字段__in
res =models.User.objects.filter(name__in=['jason','kevin']) print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>]>3.范围查询(数字)
字段__rangeres = models.User.objects.filter(age__range=(20,30)) print(res) # <QuerySet [<User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>4.模糊查询
- 字段__contains(不忽略大小写)
res = models.User.objects.filter(name__icontains='j') print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>]>
- 字段__icontains(忽略大小写)
res = models.User.objects.filter(name__contains='j') print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>]>
5.日期处理
- 字段__year
res = models.User.objects.filter(join_time__year=2022) print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
- 字段__month
res = models.User.objects.filter(join_time__month=9) print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
- 字段__day
res = models.User.objects.filter(join_time__day=13) print(res) # <QuerySet [<User: 对象:jason>, <User: 对象:jason>, <User: 对象:kevin>, <User: 对象:kevin>, <User: 对象:oscar>]>
查看ORM底层SQL语句
方式1(局限性比较强):
如果是Queryset对象,那么可以直接点query查看SQL语句。举个栗子: res = models.User.objects.filter(join_time__month=9) print(res.query) # SELECT `app01_user`.`uid`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`join_time` FROM `app01_user` WHERE EXTRACT(MONTH FROM `app01_user`.`join_time`) = 9方式2(通用性强):
配置文件配置,打印所有的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外键字段创建
1.外键字段关系之一对多
models.ForeignKey()ORM中外键字段建在多的一方 ;
会自动添加_id后缀。
2.外键字段关系之多对多
models.ManyToManyField()
ORM中有三种创建多对多字段的方式:
- 方式1:直接在查询频率较高的表中填写字段即可 自动创建第三张关系表
- 方式2:自己创建第三张关系表
- 方式3:自己创建第三张关系表 但是还是要orm多对多字段做关联
3.外键字段关系之一对一
models.OneToOneField()ORM中外键字段建在查询频率较高的表中 ;
会自动添加_id后缀。
4.特别补充:
- django1.X 针对 models.ForeignKey() models.OneToOneField()不需要on_delete参数的;
- django2.X 3.X 则需要添加on_delete参数
5.举个栗子之图书管理系统外键字段之间的关系
class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) # 总共八位,小数点后占两位 publish_time = models.DateTimeField(auto_now=True) publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE) authors = models.ManyToManyField(to='Author') class Publish(models.Model): name = models.CharField(max_length=32) email = models.EmailField() class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() author_detail = >models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE) class AuthorDetail(models.Model): phone =models.BigIntegerField() addr = models.CharField(max_length=255)
外键字段数据操作
1.针对models.ForeignKey(to=‘Publish’, on_delete=models.CASCADE)外键添加方式。
方式1直接给实际字段添加关联数据值-------publish_id = 1 models.Book.objects.create(title='python从入门到放弃',price='29800.88',publish_id=1)方式2间接使用外键虚拟字段添加数据对象 --------publish=publish_obj publish_obj = models.Publish.objects.filter(pk=2).first() models.Book.objects.create(title='django从入门到入土',price=19888.80,publish=publish_obj)
2.针对models.OneToOneField(to=‘AuthorDetail’, on_delete=models.CASCADE)外键添加方式。
方式1直接给实际字段添加关联数据值--------author_detail_id = 1方式2间接使用外键虚拟字段添加数据对象--------author_detail=authorDetail_obj
3.针对多对多models.ManyToManyField(to=‘Author’)添加外键方式。
add() 添加数据 括号内即可以填写数字值也可以填写数据对象 支持多个 remove() 删除数据 括号内即可以填写数字值也可以填写数据对象 支持多个 set() 修改数据 括号内必须是可迭代对象 clear() 清空指定数据 括号内不需要任何参数
正反向概念
1.正反向的概念核心就在于外键字段在谁手上
- 正向查询
通过书查询出版社 外键字段在书表中- 反向查询
通过出版社查询书 外键字段不在出版社表中
ORM跨表查询口诀>>>:正向查询按外键字段 反向查询按表名小写
2.基于对象的跨表查询(子查询)
基于对象的正向跨表查询
- 查询主键为1的书籍对应的出版社(书>>>出版社)
(1)先根据条件查询数据对象(先查书籍对象)book_obj = models.Book.objects.filter(pk=1).first()(2)以对象为基准 思考正反向概念(书查出版社 外键字段在书表中 所以是正向查询)
print(book_obj.publish)
- 查询主键为3的书籍对应的作者(书>>>作者)
(1)先根据条件查询数据对象(先查书籍对象)book_obj = models.Book.objects.filter(pk=3).first()(2)以对象为基准 思考正反向概念(书查作者 外键字段在书表中 所以是正向查询)
print(book_obj.authors) # app01.Author.None print(book_obj.authors.all())
- 查询jason的作者详情
(1)先根据条件查询数据对象author_obj = models.Author.objects.filter(name='jason').first()(2)以对象为基准 思考正反向概念
print(author_obj.author_detail)
基于对象的反向跨表查询
- 查询南方出版社出版的书籍
(1)先拿出版社对象publish_obj = models.Publish.objects.filter(name='南方出版社').first()(2)思考正反向
# print(publish_obj.book) # print(publish_obj.book_set) # app01.Book.None print(publish_obj.book_set.all())
- 查询jason写过的书
(1)先拿作者对象author_obj = models.Author.objects.filter(name='jason').first()(2)思考正反向
# print(author_obj.book) # print(author_obj.book_set) # app01.Book.None print(author_obj.book_set.all())
- 查询电话是110的作者
(1)先拿作者详情对象author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()(2)思考正反向
print(author_detail_obj.author)
2.基于双下划线的跨表查询(连表操作)
基于双下划线的正向跨表查询
- 查询主键为1的书籍对应的出版社名称及书名
res = models.Book.objects.filter(pk=1).values('publish__name','title') print(res)
- 查询主键为3的书籍对应的作者姓名及书名
res = models.Book.objects.filter(pk=3).values('authors__name', 'title') print(res)
- 查询jason的作者的电话号码和地址
res = models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr') print(res)
基于双下划线的反向跨表查询
- 查询南方出版社出版的书籍名称和价格
res = models.Publish.objects.filter(name='南方出版社').values('book__title','book__price') # print(res)
- 查询jason写过的书的名称和日期
res = models.Author.objects.filter(name='jason').values('book__title','book__publish_time') # print(res)
- 查询电话是110的作者姓名和年龄
res = models.AuthorDetail.objects.filter(phone=110).values('author__name','author__age') print(res)
研究ORM跨表本质(需要用到三张表)
- 查询主键为1的书籍对应的作者电话号码
res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone') print(res)
本文深入探讨了Django ORM如何与数据库进行交互,包括模型定义、数据迁移、测试环境搭建、外键操作、数据增删改查、多表查询等。讲解了各种查询关键字如filter、all、first、last、get、values、values_list等,并展示了如何进行子查询和连表操作。此外,还介绍了日期处理、正反向查询、跨表查询的本质,以及外键和多对多关系的建立与操作。

1万+

被折叠的 条评论
为什么被折叠?



