django5/关键字/双下划线的查询/orm外键/正反向概念/正反向查询/跨表查询
- 4.表查询数据准备及测试环境搭建
- ORM常见查询关键字
- 神奇的双下划线的查询
- 查看ORM底层SQL语句
- ORM外键字段创建
- 外键字段数据操作
- 正反向概念
- 基于对象的跨表查询(子查询)
- 基于爽下划线的跨表查询
表查询数据准备测试环境搭建
django 自带一个sqlite3小型数据库.sqlite3功能有限,对日期类型兼容很差,我们使用mysql
1.创建一个database库(mysql)
2.在pycharm中创建一个项目
3.django项目配置文件(settings.py)修改
4.在models.py创建准备数据
5.连接数据库查看是否连接成功
6.模型层环境准备创建一个test.py文件进行数据查询测试
第一步和第二步略
3.settings.py修改
#第一处
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'应用名', #pycharm创建会自动添加 #linux环境需要自己手动编辑添加你写的应用名
]
#第二处
# 'django.middleware.csrf.CsrfViewMiddleware', # 47行
#第三处mysql创建库,并修改配置django的settings文件中的database连接
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day59',
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD': '111',
'CHARSET': 'utf8'
}
}
#第四处关闭debug
DEBUG = True #26行,pycharm可不关,linux环境可以关闭。
django 切换MySQL数据
版本 | 使用 |
---|---|
django1.1 | import pymysql pymysql.install_as_MySQLdb() |
django2.X 3.X 4.X | pip install mysql mysqlclient |
#我们使用的是jango2.2.22版本 没有mysqlclient请安装
4.在models.py创建准备数据
定义模型类(models.py)
from django.db import models
import os
import sys
# Create your models here.
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)
------------------------------------------------------------------------
#然后执行数据库迁移命令模型>>>表
#执行以下步骤需要确认好settings的database配置无误
tools -> run manage.py Task
makemigrations
migrate
5:略
6.模型层测试环境准备
两种准备
1.pycharm提供测试环境
2.自定义一个py文件
方式1:python console命令行测试环境
方式2.自定义一个test.py用于测试数据查询 (#推荐这个)
python console
方式2.pycharm提供测试环境
import os
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings')
import django
django.setup()
from app01 import models
print(models.User.objects.filter())
if __name__ == '__main__':
main()
#创建模拟数据
import os
def main()
os.environ.setdefault('DJANGO_SETTINGS_MODULE','day59.settings')
import django
django.setup()
from app01 import models
modules.User.object.create(name ='jason',age='18'
返回值接收查看
import os
def main()
os.environ.setdefault('DJANGO_SETTINGS_MODULE','day59.settings')
import django
django.setup()
from app01 import models
res = modules.User.object.create(name ='jason',age='18'
print(res.name)
print(res.age)
print(res.join_time)
print(res.uid)
print(res.pk)
-------------------执行结果-------------------------------------
jason
18
2022-09-05
2
2
另一种创建数据的方式
#可以用类实例化对象,然后调用save方法创建 #比较冷门
增删改
1.create()
创建数据 返回值就是当前创建的数据对象
2.update()
更新数据
3.delete()
删除数据
注意!!! 上面例子中,创建数据时,返回值就是当前创建的数据对象。
res变量可接受返回值,以便用于查询操作。下面来了解下查询操作。
ORM常见查询关键字
在模型类(models.py)中可以定义一个双下str方法,便于后续数据对象被打印展示,自动触发
打印可以用于print也可以用作页面展示,数据查询。
#在modes.py;里,main函数内部
def __str__(self):
return f'对象:{self.name}' #该方法必须返回一个字符串类型的数据
Queryset中如果是列表套对象那么直接for循环和索引取值但是索引不支持负数
4.虽然queryset支持索引但是当queryset没有数据的时候会索引报错,推荐用first
#关键字查询例子
filter()
res = models.User.objects.filter()
print(res)
13个必会
1.filter() #【使用频率最高】
筛选数据 返回值是一个QuerySet(可以看成是列表套数据对象)
1.括号内不写查询条件 默认就是查询所有
2.括号内可以填写条件 并且支持多个 逗号隔开 默认是and关系
------------------------------------------------------------------------------------------
2.all()
查询所有数据 返回值是一个QuerySet(可以看成是列表套数据对象)
可以for循环取值。
------------------------------------------------------------------------------------------
3.first()
获取Queryset中第一个数据对象 如果为空则返回None
------------------------------------------------------------------------------------------
4.last()
获取Queryset中最后一个数据对象 如果为空则返回None
------------------------------------------------------------------------------------------
5.get()
直接根据条件查询具体的数据对象 但是条件不存在直接报错 【不推荐使用】
------------------------------------------------------------------------------------------
6.values()
指定查询字段 结果是Queryset(可以看成是列表套字典数据)
------------------------------------------------------------------------------------------
7.values_list()
指定查询字段 结果是Queryset(可以看成是列表套元组数据)
------------------------------------------------------------------------------------------
8.order_by()
指定字段排序 默认是升序 在字段前加负号则为降序 并且支持多个字段排序
------------------------------------------------------------------------------------------
9.count()
统计orm查询之后结果集中的数据格式
------------------------------------------------------------------------------------------
10.distinct()
针对重复的数据集进行去重 一定要注意数据对象中的主键
------------------------------------------------------------------------------------------
11.exclude()
针对括号内的条件取反进行数据查询 QuerySet(可以看成是列表套数据对象)
------------------------------------------------------------------------------------------
12.reverse()
针对已经排了序的结果集做颠倒
------------------------------------------------------------------------------------------
13.exists()
判断查询结果集是否有数据 返回布尔值 但是几乎不用因为所有数据自带布尔值
神奇的双下划线的查询
1.比较运算符
字段__gt 大于
字段__lt 小于
字段__gte 大于等于
字段__lte 小于等于
2.成员运算符
字段__in
3.范围查询(数字)
字段__range
4.模糊查询
字段__contains #不忽略大小写
字段__icontains #忽略大小写
5.日期处理
字段__year
字段__month
字段__day
查看ORM底层SQL语句
底层的SQL如何执行的?
如何查看?
方式1
如果是Queryset对象,那么可以直接点queryset查看sql语句
import os
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings')
import django #导入模块只能放在这个位置,放在函数外会报错。
django.setup()
from app01 import models
res = models.User.objects.filter()
print(res.query) #查看
if __name__ == '__main__':
main()
---------------执行结果-----------------------------
SELECT `app01_user`.`uid`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`join_time` FROM `app01_user`
方式二
配置文件配置 打印所有的ORM操作对应的SQL语句 直接拷贝使用即可
在settings配置文件加以下内容
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外键字段创建
一对多
ORM 外键字段建在多的一方 models.ForeignKey()
会自动添加id后缀
多对多
ORM 中有三种创建多对多的字段,models.ManyToManyField()
方式1:直接在查询频率较高的表中填写字段即可 自动创建第三张关系表
方式2:自己创建第三张关系表
方式3:自己创建第三张关系表 但是还是要orm多对多字段做关联
一对一
ORM中外键字段建在查询频率较高的表中 models.OneToOneField()
会自动添加_id后缀
django1.X 针对 models.ForeignKey() models.OneToOneField()不需要on_delete
django2.X 3.X 则需要添加on_delete参数
Django中的外键创建和mysql几乎一样,我们以创建图书表,出版社表,作者表和作者详情表来举例子。
两张表 | 关系 | 方法 | 外键位置 |
---|---|---|---|
书与出版社 | 一对多关系 | ForeignKey(to='出版社表') | 一对多关系也是建在多的一方,建在书的表里 |
书与作者 | 多对多关系 | ManyToManyField(to='作者表') | 多对多关系,可以不用自己创建第三张表 |
作者与作者详情 | 一对一关系 | OneToOneField=(to='作者详情表') | 一对一关系,建在查询频率较高的表中,建在作者表里 |
ps:三个关键字里面的参数,to用于指定跟哪张表有关系,自动关联主键。to_field\to_fields,也可以自己指定关联字段。
ManyToManyField不会在表中创建实际的字段,而是告诉django orm自动创建第三张关系表。ForeignKey、OneToOneField会在字段的后面自动添加_id后缀,如果你在定义模型类的时候自己添加了该后缀那么迁移的时候还会添加,所以不要自己加下划线id后缀。
#创建具体i代码
from django.db import models
# Create your models here.
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:只在创建数据的那一刻自动获取当前时间 之后如果不人为更改则不变
"""
def __str__(self):
'''对象被执行打印(print、页面展示、数据查询)操作的时候自动触发'''
return f'对象:{self.name}' # 该方法必须要返回一个字符串类型的数据
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')
def __str__(self):
return '书籍对象:%s' % self.title
class Publish(models.Model):
name = models.CharField(max_length=32)
email = models.EmailField()
def __str__(self):
return '出版社对象:%s' % 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 '作者对象:%s' % self.name
class AuthorDetail(models.Model):
phone = models.BigIntegerField()
addr = models.CharField(max_length=255)
def __str__(self):
return '作者详情对象:%s' % str(self.phone)
外键字段数据操作
一对多和一对一
models.ForeignKey(to='Publish', on_delete=models.CASCADE)
models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
方式1:直接给实际字段添加关联数据值
models.Book.objects.create(title='python从入门到放弃',price=29800.88,publish_id=1)
方式2:间接使用外键虚拟字段添加数据对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='django从入门到入土',price=19888.88,publish=publish_obj)
多对多
models.ManyToManyField(to='Author')
第三张关系表添加数据
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.add() # 括号内即可以填写数字值也可以填写数据对象 支持多个
第三张关系表修改数据
book_obj.authors.set() # 括号内必须是一个可迭代对象,元素同样支持主键值或者数据对象
第三张关系表删除数据
book_obj.authors.remove() # 括号内即可以填写数字值也可以填写数据对象 支持多个
第三张关系表清空指定数据
book_obj.authors.clear() # 括号内无需传值 直接清空当前表在第三张关系表中的绑定记录
正反向概念
正反向的概念核心就在于外键字段在谁手上
正向查询:通过书查询出版社,外键字段在书表中
反向查询:通过出版社查询书,外键字段不在出版社表中
ORM跨表查询口诀:正向查询按外键字段、反向查询按表名小写
基于对象的正向跨表查询
1.查询主键为1的书籍对应的出版社
1.先根据条件查询数据对象(先查书籍对象)
book_obj = models.Book.objects.filter(pk=1).first()
2.以对象为基准,思考正反向概念(书查出版社 外键字段在书表中 所以是正向查询)
print(book_obj.publish)
2.查询主键为3的书籍对应的作者
1.先根据条件查询数据对象(先查书籍对象)
book_obj = models.Book.objects.filter(pk=3).first()
2.以对象为基准 思考正反向概念(书查作者 外键字段在书表中 所以是正向查询)
print(book_obj.authors) # app01.Author.None 因为书和作者是多对多的关系,所以需要在加个all()
print(book_obj.authors.all())
3.查询jason的作者详情
1.先根据条件查询数据对象
author_obj = models.Author.objects.filter(name='jason').first()
2.以对象为基准 思考正反向概念
print(author_obj.author_detail)
基于对象的反向跨表查询
1.查询南方出版社出版的书籍
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())
2.查询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())
3.查询电话是110的作者
1.先拿作者详情对象
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
2.思考正反向
print(author_detail_obj.author)
基于双下划线的跨表查询(连表操作)
基于双下划线的正向跨表查询
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__addr')
print(res)
基于双下划线的反向跨表查询
1.查询南方出版社出版的书籍名称和价格
res = models.Publish.objects.filter(name='南方出版社').values('book__title','book__price')
print(res)
2.查询jason写过的书的名称和日期
res = models.Author.objects.filter(name='jason').values('book__title','book__publish_time')
print(res)
3.查询电话是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)