目录
一、测试环境搭配
- 切换数据库
自带的sqlite3数据库对时间字段不敏感 有时候会展示错乱 ,所以我们习惯切换成常见的数据库比如MySQL django orm并不会自动帮你创建库, 所以需要提前准备好!
- 单独搭配测试环境
单独测试django某个功能层,默认不允许单独测试某个py文件,如果想要测试某个py文件(主要models.py),主要有如下两种方法
测试环境1:pycharm提供的python console
测试环境2:自己搭建(自带的test或者自己创建)
| |
| import os |
| import sys |
| def main(): |
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoday05.settings') |
| |
| import django |
| jango.setup() |
- django orm底层还是SQL语句 我们是可以查看的
如果我们手上是一个QuerySet对象 那么可以直接点query查看SQL语句
如果想查看所有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', |
| }, |
| } |
| } |
| class User(models.Model): |
| name = models.CharField(max_length=32, verbose_name='用户名') |
| age = models.IntegerField(verbose_name='年龄') |
| register_time = models.DateTimeField(verbose_name='注册时间', auto_now_add=True) |
| ''' |
| 针对时间字段有两个重要的参数 |
| auto_now:每次操作数据都会自动更新当前时间 |
| auto_now_add:创建数据自动获取当前时间 后续修改不人为操作的情况下不会更新 |
| ''' |
| |
| def __str__(self): |
| return f'用户对象{self.name}' |
二、常见的十几种查询方法-ORM关键字
基本关键字
| |
| models.User.objects.create(username='kevin1', password='123', age=20) |
| models.User.objects.create(username='kevin2', password='123', age=20) |
| models.User.objects.create(username='kevin3', password='123', age=20) |
| models.User.objects.create(username='kevin4', password='123', age=20) |
| |
| res = models.User(username='tank', password=123, age=18) |
| res.save() |
- 查 filter() first() last()
| |
| |
| res = models.User.objects.filter() |
| res = models.User.objects.filter(name='jason') |
| res = models.User.objects.filter(name='jason', age=19) |
| |
| |
| res = models.User.objects.filter()[1] |
| res = models.User.objects.filter(pk=100)[0] |
| res = models.User.objects.filter(pk=100).first() |
| res = models.User.objects.filter().last() |
| |
| models.User.objects.filter().update() 批量更新 |
| models.User.objects.filter(id=1).update() 单个更新 |
| |
| user_obj = models.User.objects.filter(pk=1).first() |
| user_obj.username = 'jerry' |
| user_obj.password = '234' |
| user_obj.save() |
| |
| models.User.objects.filter().delete() 批量删除 |
| models.User.objects.filter(id=1).delete() 单个删除 |
| |
| user_obj = models.User.objects.filter(pk=1).first() |
| user_obj.delete() |
常用关键字
- all() 查询所有数据 结果是QuerySet [数据对象1,数据对象2]
| res = models.User.objects.all() |
- values() 根据指定字段获取数据 结果是QuerySet [{},{},{},{}]
| res = models.User.objects.all().values('name') |
| res = models.User.objects.filter().values() |
| res = models.User.objects.values() |
- values_list() 根据指定字段获取数据 结果是QuerySet [(),(),(),()]
| res = models.User.objects.all().values_list('name','age') |
- distinct() 去重 数据一定要一模一样才可以 如果有主键肯定不行
| res = models.User.objects.values('name','age').distinct() |
- order_by() 根据指定条件排序 默认是升序 字段前面加负号就是降序
| res = models.User.objects.all().order_by('age') |
| print(res) |
- get() 根据条件筛选数据并直接获取到数据对象 一旦条件不存在会直接报错 不建议使用
| res = models.User.objects.get(pk=1) |
| print(res) |
| res = models.User.objects.get(pk=100, name='jason') |
| print(res) |
- exclude() 取反操作
| res = models.User.objects.exclude(pk=1) |
| print(res) |
- reverse() 颠倒顺序(被操作的对象必须是已经排过序的才可以)
| res = models.User.objects.all() |
| res = models.User.objects.all().order_by('age') |
| res1 = models.User.objects.all().order_by('age').reverse() |
| print(res, res1) |
- count() 统计结果集中数据的个数
| res = models.User.objects.all().count() |
| print(res) |
- exists() 判断结果集中是否含有数据 如果有则返回True 没有则返回False
| res = models.User.objects.all().exists() |
| print(res) |
| res1 = models.User.objects.filter(pk=100).exists() |
| print(res1) |
13个必会操作总结
返回QuerySet对象的方法
- all()
- filter()
- exclude()
- order by()
- reverse()
- distinct()
特殊的QuerySet
- values()返回一个可选代的字典序列
- values list() 返回一个可迭代的元祖序列
返回具体对象的返回具体对象的
- get()
- first()
- last()
返回布尔值的方法有:
- exists()
返回数字的方法有
- count()
*****************************************test.py文件测试的代码
| from django.test import TestCase |
| |
| |
| |
| import os |
| |
| |
| def main(): |
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoday05.settings') |
| import django |
| django.setup() |
| from app01 import models |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| res = models.User.objects.all().exists() |
| print(res) |
| res1 = models.User.objects.filter(pk=100).exists() |
| print(res1) |
| main() |
三、ORM执行SQL语句
有时候ORM的操作效率可能偏低 我们是可以自己编写SQL的
| res = models.User.objects.raw('select * from app01_user;') |
| print(list(res)) |
| from django.db import connection |
| cursor = connection.cursor() |
| cursor.execute('select name from app01_user;') |
| print(cursor.fetchall()) |
四、双下划线查询
1._ _gt
查询年龄大于18的用户数据
| res = models.User.objects.filter(age__gt=18) |
| print(res) |
2._ _lt
查询年龄小于38的用户数据
| res = models.User.objects.filter(age__lt=38) |
| print(res) |
3._ _gte _ _lte
大于等于 小于等于
| res = models.User.objects.filter(age__gte=18) |
| res = models.User.objects.filter(age__lte=38) |
4._ _in
查询年龄是18或者28或者38的数据
| res = models.User.objects.filter(age__in=(18, 28, 38)) |
| print(res) |
5._ _range
查询年龄范围在18-38的用户数据
| res = models.User.objects.filter(age__range=(18, 38)) |
| print(res) |
6._ _contains
查询名字中含有字母j的数据
| select *from user where username like '%s%' 模糊查询 |
| res = models.User.objects.filter(name__contains='j') |
| print(res) |
| res = models.User.objects.filter(name__icontains='j') |
| print(res) |
7._ _year 、 _ _month
1.查询注册年份是2022的数据
2.查询出注册时间是 2023 5月
| res = models.User.objects.filter(register_time__year=2022) |
| print(res) |
| res= models.User.objects.filter(reg_time__month='5',reg_time__year='2023') |
PS:只要还是queryset对象就可以无限制的点queryset对象的方法
| queryset.filter().values().filter().values_list().filter() |
针对django框架的时区问题 是需要配置文件中修改的 后续bbs讲解
五、ORM外键字段的创建
复习MySQL外键关系
ps:关系的判断可以采用换位思考原则 熟练的之后可以瞬间判断
1.创建基础表(书籍表、出版社表、作者表、作者详情)
图书表
| class Book(models.Model): |
| """图书表""" |
| title = models.CharField(max_length=32, verbose_name='书名') |
| price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格') |
| publish_time = models.DateField(auto_now_add=True, verbose_name='出版日期') |
| |
| |
| publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) |
| '''django1.x版本外键字段默认都是级联更新删除 django2.x及以上需要自己申明 on_delete=models.XXX ''' |
| |
| authors = models.ManyToManyField(to='Author') |
| '''多对多字段为虚拟字段 用于告诉ORM自动创建第三张表''' |
| |
| def __str__(self): |
| return f'数据对象:{self.title}' |
出版社表
| class Publish(models.Model): |
| """出版社表""" |
| name = models.CharField(max_length=32, verbose_name='名称') |
| address = models.CharField(max_length=64, verbose_name='地址') |
| |
| def __str__(self): |
| return f'出版社对象:{self.name}' |
作者表
| class Author(models.Model): |
| """作者表""" |
| name = models.CharField(max_length=32, verbose_name='姓名') |
| age = models.IntegerField(verbose_name='年龄') |
| |
| author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE) |
| |
| def __str__(self): |
| return f'作者对象:{self.name}' |
作者详情表
| class AuthorDetail(models.Model): |
| """作者详情表""" |
| phone = models.BigIntegerField(verbose_name='手机号') |
| address = models.CharField(max_length=64, verbose_name='家庭住址') |
| |
| def __str__(self): |
| return f'详情对象:{self.address}' |
| |
2.确定外键关系(语句已经写在上面)
- 一对多 ORM与MySQL一致 外键字段建在多的一方
图书和出版社关系表
外键建在图书表
- 多对多 ORM比MySQL有更多变化
图书和作者关系表
1.外键字段可以直接建在某张表中(查询频率较高的)
内部会自动帮你创建第三张关系表
2.自己创建第三张关系表并创建外键字段
详情后续讲解
- 一对一 ORM与MySQL一致
外键字段建在查询较高的一方
3.ORM创建(细节,要注意)
针对一对多和同步到表中之后会自动加_id的后缀
| |
| publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE) |
| |
| author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE) |
针对多对多 不会在表中有展示 而是创建第三张表
| authors = models.ManyToManyField(to='Author') |
四、创建好的表

六、外键字段数据的增删改查
针对一对多,有两种方式,既可以传数字也可以传对象(第一种方便,第二种用的多)
| models.Book.objects.create(title='三国演义', price=888.88, publish_id=1) |
| models.Book.objects.create(title='人性的弱点', price=777.55, publish_id=1) |
| publish_obj = models.Publish.objects.filter(pk=1).first() |
| models.Book.objects.create(title='水浒传', price=555.66, publish=publish_obj) |
ps:一对一与一对多一致
针对多对多关系绑定
添加关系 add()
- 方式一:先通过外键所在图书表的id值找到所要绑定的图书对象,直接通过默认的作者id进行add关键字添加
| book_obj = models.Book.objects.filter(pk=1).first() |
| book_obj.authors.add(1) |
| book_obj.authors.add(2, 3) |
- 方式二:先通过外键所在图书表的id值找到所要绑定的图书对象,再通过作者表的id找到需要绑定的作者对象,然后进行add关键字添加
| book_obj = models.Book.objects.filter(pk=4).first() |
| author_obj1 = models.Author.objects.filter(pk=1).first() |
| author_obj2 = models.Author.objects.filter(pk=2).first() |
| book_obj.authors.add(author_obj1) |
| book_obj.authors.add(author_obj1, author_obj2) |
修改关系 set() 这里必须要写元组或者列表
| book_obj = models.Book.objects.filter(pk=1).first() |
| book_obj.authors.set((1, 3)) |
| book_obj.authors.set([2, ]) |
| author_obj1 = models.Author.objects.filter(pk=1).first() |
| author_obj2 = models.Author.objects.filter(pk=2).first() |
| book_obj.authors.set((author_obj1,)) |
| book_obj.authors.set((author_obj1, author_obj2)) |
删除关系 remove()
| book_obj.authors.remove(2) |
| book_obj.authors.remove(1, 3) |
| book_obj.authors.remove(author_obj1,) |
| book_obj.authors.remove(author_obj1,author_obj2) |
清除指定所有 clear()
小总结:
add()\remove() 多个位置参数(数字 对象)
set() 可迭代对象(元组 列表) 数字 对象
clear() 情况当前数据对象的关系
七、ORM跨表查询介绍(多表查询——子查询,连表查询)
复习MySQL跨表查询的思路
| 子查询 |
| 分步操作:将一条SQL语句用括号括起来当做另外一条SQL语句的条件 |
| 连表操作 |
| 先整合多张表之后基于单表查询即可 |
| inner join 内连接 |
| left join 左连接 |
| right join 右连接 |
正反向查询的概念(重要)
| 正向查询 |
| 由外键字段所在的表数据查询关联的表数据 正向 |
| 反向查询 |
| 没有外键字段的表数据查询关联的表数据 反向 |
| ps:正反向的核心就看外键字段在不在当前数据所在的表中 |
ORM跨表查询的口诀(重要)
八、基于对象的跨表查询(等价MySQL子查询 分步操作)
正向查询
| author_obj = models.Author.objects.filter(name='jason').first() |
| print(author_obj.author_detail.phone) |
反向查询
| author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first() |
| print(author_detail_obj.author) |
| print(author_detail_obj.author.name) |
九、基于上双线的跨表查询(等价MySQL连表查询)
正向查询
| res = models.Book.objects.filter(pk=1).values('publish__name','title') |
| print(res) |
| res = models.Book.objects.filter(pk=4).values('title', 'authors__name') |
| print(res) |
| res = models.Author.objects.filter(name='jason').values('author_detail__phone') |
| print(res) |
反向查询
| res = models.Author.objects.filter(name='jason').values('book__title', 'name') |
| print(res) |
| res = models.AuthorDetail.objects.filter(phone=110).values('phone', 'author__name') |
| print(res) |
进阶操作 (models后面的对象就是对应values)
| res = models.Publish.objects.filter(book__pk=1).values('name') |
| print(res) |
| res = models.Author.objects.filter(book__pk=4).values('name','book__title') |
| print(res) |
| res = models.AuthorDetail.objects.filter(author__name='jason').values('phone') |
| print(res) |
| res = models.Book.objects.filter(publish__name='北方出版社').values('title','price') |
| print(res) |
| res = models.Book.objects.filter(authors__name='jason').values('title') |
| print(res) |
| res = models.Author.objects.filter(author_detail__phone=110).values('name') |
| print(res) |
| |
| |
| |
| |
| res = models.Author.objects.filter(book__pk=4).values('author_detail__phone') |
| print(res) |
十、聚合查询
聚合函数:Max Min Sum Count Avg
在ORM中支持单独使用聚合函数 aggregate()关键字
| from django.db.models import Max, Min, Sum, Count, Avg |
| res = models.Book.objects.aggregate(Max('price'), Count('pk'), 最小价格=Min('price'), allPrice=Sum('price'),平均价格=Avg('price')) |
| print(res) |
十一、分组查询 annotate()关键字
"""
如果执行orm分组查询报错 并且有关键字sql_mode strict mode
移除sql_mode中的only_full_group_by
"""
上面都是按表分组,下面是按指定字段
"""
models.表名.objects.annotate() 按照表分组
models.表名.objects.values('字段名').annotate() 按照values括号内指定的字段分组
"""
| res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id', 'count_pk') |
| print(res) |
十二、F与Q查询
F
- 1.查询库存数大于卖出数的书籍
当查询条件不是明确的 也需要从数据库中获取 就需要使用F查询
| from django.db.models import F |
| |
| |
| from django.db.models.functions import Concat |
| from django.db.models import Value |
| models.Book.objects.update(title=Concat(F('title'), Value('新款'))) |
Q
| |
| from django.db.models import Q |
| |
| |
| res = models.Book.objects.filter(~Q(pk=1) | Q(price__gt=2000)) |
| print(res.query) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步