django入门菜鸟教程:ORM查询操作详解
表查询数据准备以及测试环境搭建
1.数据库介绍
Django自带一个小型数据库:sqlite3数据库。该数据库功能十分有限,并且针对日期类型的数据兼容性差,所以我们选择用MySQL数据库。
2.mysql数据库配置
Django1.x
import pymysql
pymysql.install_as_MySQLdb()
Django2.x 3.x 4.x
pip install mysqlclient
配置文件信息:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day59',
'USER': 'root',
'PASSWORD': '',
'HOST': '127.0.0.1',
'PORT': 3306,
'CHARSET': 'utf8'
}
}
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)
def __str__(self):
return f'数据对象:{self.name}'
# __str__方法:对象被执行打印操作的时候自动执行,该方法需返回一个字符串,可以作为我们测试时的提示信息
主要关键字介绍:
primary_key:指定主键 在django中 主键字段可以默认不写,如果不写,则会自动添加一个名为id的主键字段。如果写了则用我们自己定义的。
verbose_name:字段说明,相当于是备注,方便见名知意。
DateField:年月日
DateTimeField:年月日时分秒
auto_now:每次操作数据库并保存都会自动更新当前时间
auto_now_add:只在创建数据的时候自动获取当前时间,以后除非人为改动,否则永不改变。
4.执行数据库迁移命令
模型类>>>数据表
makemigrations
migrate
5.模型层测试环境准备
方式1:在任意的空py文件中准备环境
import os
# Create your tests here.
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings')
import django
django.setup()
from app01 import models
if __name__ == '__main__':
main()
方式2:pycharm提供测试环境
python console命令行测试环境
6.django删除表重建的实现方法
使用migrate同步数据到数据库上时遇到了一些问题,记录下来。比如设计表的时候,手贱直接删除了一张表,然后就一直无法生成表了。 或者已经删除了这张表,执行migrate时候,一直说"Table 'hello_xxx' already exists",都是血与泪的坑
手贱删除表 如果有一天你手痒了,删除了一张自己设计的表,你会发现,不管你怎么执行makemigrations和migrate都无法自动生成新的表了。 就算你删除app_name/migrations下面的0001_initial.py和其它文件,重新执行makemigrations和migrate依然无法查询生成表,提示" No migrations to apply." 就算你重新改过models.py的标名称,还是一样的,不会自动创建。
正确的方法如下:
1.先到数据库把表删掉:drop table
2.注释django中对应的Model
3.执行以下命令:
python manage.py makemigrations
python manage.py migrate --fake
4.去掉注释重新迁移
python manage.py makemigrations
python manage.py migrate
ORM表查询常见关键字
前排排坑:
1.当需要查询数据主键字段值时,可以直接使用pk(primary key)忽略真正的主键名字
2.在模型类中定义一个__str__方法,便于我们后续执行对象打印操作时查看方便
3.QuerySet中如果是列表套对象的形式,可以支持for循环和列表索引取值(不支持负数索引)
4.虽然QuerySet支持索引,但当其内部没有数据的时候,索引会报错,更推荐使用first()
5.create() 创建数据,可以有返回值,返回的就是当前创建的数据对象(相当于创建之后又查询一次)
5.update() 更细数据
6.delete() 删除数据
1.filter() # 核心
筛选数据,相当于是SQL里的where,返回值是一个QuerySet(可以看成是列表套数据对象)
括号内不写查询条件,默认就是查询所有数据
括号内可以填写条件,并且可以填写多个,逗号隔开,默认是and关系
eg:
res = models.User.objects.filter()
print(res) # <QuerySet [<User: User object (1)>, <User: User object (2)>]>
2.all()
查询所有数据,返回值是一个QuerySet(可以看成是列表套对象,与filter类似)
eg:
res = models.User.objects.all()
print(res) # <QuerySet [<User: 数据对象:jason>, <User: 数据对象:kevin>>
3.first()
查询QuerySet中的第一个数据对象,如果是QuerySet为空则返回None,常配合filter使用
eg:
res = models.User.objects.filter().first()
print(res) # 数据对象:jason
4.last()
查询QuerySet中的最后一个数据对象,如果是QuerySet为空则返回None,与first使用方法一致
eg:
res = models.User.objects.filter().last()
print(res) # 数据对象:kevin
5.get()
根据条件直接查询具体的数据对象(不用再被QuerySet包起来),但是如果对象不存在会报错,不推荐使用
eg:
res = models.User.objects.get(name='jason')
# 相当于是 models.User.objects.filter(name='jason').first()
print(res) # 数据对象:jason
6.values()
查询指定字段,结果是QuerySet(可以看成是列表套数据字段),里边可以跟多个条件,逗号隔开
eg:
res = models.User.objects.filter(name='jason').values('uid', 'join_time')
print(res) # <QuerySet [{'uid': 1, 'join_time': datetime.date(2022, 9, 5)}]>
7.values_list()
查询指定字段,结果是QuerySet(可以看成是列表套元组数据)
eg:
res = models.User.objects.filter(pk=1).values_list('pk', 'name')
print(res) # <QuerySet [(1, 'jason')]>
8.order_by()
指定字段排序,默认是升序,在字段前加负号是降序,并且支持多个字段排序
eg:
res = models.User.objects.order_by('pk') # 升序
print(res) # <QuerySet [<User: 数据对象:jason>, <User: 数据对象:kevin>]>
res = models.User.objects.order_by('-pk') # 降序
print(res) # <QuerySet [<User: 数据对象:kevin>, <User: 数据对象:jason>]>
9.count()
统计orm查询之后结果集数据格式的个数
eg:
res = models.User.objects.order_by('-pk')
print(res) # <QuerySet [<User: 数据对象:jason>, <User: 数据对象:kevin>]>
print(res.count()) # 2
10.distinct()
针对重复的数据进行去重 要求数据必须完全一致,一定要注意数据对象中的主键
11.exclude()
针对括号内的条件进行反向数据查询,结果是QuerySet(filter的反向操作)
eg:
res = models.User.objects.exclude(name='jason')
print(res) # <QuerySet [<User: 数据对象:kevin>]>
12.reverse()
针对已经排序的结果集颠倒顺序
13.exists()
判断查询结果集是否存在 返回布尔值 但是几乎不用因为所有数据自带布尔值
14.raw()
不想写orm时使用,支持写原汁原味的SQL
还可以借助于模块
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()
神奇的双下划线查询
其实一点也不神奇,就是SQl里的一些运算符
1.比较运算符
字段__gt 大于
字段__lt 小于
字段__gte 大于等于
字段__lte 小于等于
eg:
# 查询年龄大于18岁的用户
res = models.User.objects.filter(age__gt=18)
print(res) # <QuerySet [<User: 数据对象:kevin>]>
2.成员运算符
字段__in
3.返回查询(数字)
字段__range # 左右都包含
eg:
# 查询年龄在18~20之间的用户
res = models.User.objects.filter(age__range=(18, 20))
print(res) # <QuerySet [<User: 数据对象:jaon>, <User: 数据对象:kevin>]>
4.模糊查询
字段__contains 不忽略大小写
字段__icontains 忽略大小写
eg:
# 查询名字中含有 j 的用户
res = models.User.objects.filter(name__icontains='j')
print(res) # <QuerySet [<User: 数据对象:jaon>]>
# 查询名字中含有 J 的用户
res = models.User.objects.filter(name__contains='J')
print(res) # <QuerySet []>
5.日期处理
字段__year
字段__onth
字段__day
eg:
res = models.User.objects.filter(join_time__day=6)
print(res) # <QuerySet [<User: 数据对象:kevin>]>
查看ORM底层的SQL语句
方式1
如果是QuerySet对象,那么可以直接.query查看SQL语句
eg:
res = models.User.objects.filter(join_time__day=6).query
print(res) # <QuerySet [<User: 数据对象:kevin>]>
# SELECT `app01_user`.`uid`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`join_time` FROM `app01_user` WHERE EXTRACT(DAY FROM `app01_user`.`join_time`) = 6
方式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外键创建
先创建四张关系表
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2) # 总共八位小数点后两位
join_time = models.DateField(auto_now_add=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()
address = models.CharField(max_length=255)
一对多
外键字段创在多的一方 models.Foreignkey()
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
# 不需要在外键字段后面手动添加_id后缀,django会自动添加_id后缀
# 一对多和一对一关系需要在后面加 on_delete=models.CASCADE
多对多
ORM中有三种创建多对多字段的方式 models.ManyToManyField()
方式1:直接在查询频率较高的表中填写字段即可 自动创建第三张关系表
方式2:自己创建第三张关系表
方式3:自己创建第三张关系表 但是还要orm做多对多字段做关联
authors = models.ManyToManyField(to='Author')
# to='被关联表的名字'
一对一
ORM中创建在查询频率较高的表中 models.OneToOneField()
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
# 不需要在外键字段后面手动添加_id后缀,django会自动添加_id后缀
# 一对多和一对一关系需要在后面加 on_delete=models.CASCADE
django1.x 针对 models.ForeignKey() 和 models.OneToOneField() 不需要添加on_delete
django2.x 3.x 则需要添加该参数
ORM外键数据增删改查
一对多:
# 方式1:直接给实际字段添加关联数据值 publish_id = 1
models.Book.objects.create(title='java高并发', price=1288.88, publish_id=1)
# 方式2:根据pk=2获取到出版社对象,间接使用外键虚拟字段添加数据对象 publish=publish_obj
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='爬虫入门到入狱', price=2222.22, publish=publish_obj)
一对一:
# 方式1:直接给实际字段添加关联数据值 author_detail_id=5
models.Author.objects.create(name='oscar', age=33, author_detail_id=5)
# 方式2:根据pk=6获取到作者详情对象,间接使用外键虚拟字段创建数据对象 author_detail=author_detail_obj
author_detail_obj = models.AuthorDetail.objects.filter(pk=6).first()
models.Author.objects.create(name='zhangsan', age=50, author_detail=author_detail_obj)
多对多:
# 方式1
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.add(3, 4)
# 方式2:
book_obj = models.Book.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj1, author_obj2)
# 操作类型
add() 添加数据 括号内既可以填写数字,也可以填写数据对象,支持多个
remove() 删除数据 括号内既可以填写数字,也可以填写数据对象,支持多个
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.remove(2, 3)
set() 修改数据 括号内必须是可迭代对象
book_obj.authors.set([2, ])
clear() 清空指定数据 括号内不需要任何参数
book_obj.authors.clear()
正反向概念
正反向的概念的核心就在于外键字段在谁手上
正向查询:通过书查出版社,外键字段在书籍表中 反向查询:通过出版社查书,外键字段不在出版社表中
ORM跨表查询口诀>>>:正向查询按外键字段,反向查询按表名小写
基于对象的跨表查询(子查询)
1.查询主键为1的书籍对应的出版社(书>>>出版社)
# 1.1 先拿到书籍对象
book_obj = models.Book.objects.filter(pk=1).filter().first()
# 1.2 以对象为基准 思考正反向(书查出版社,外键在书表中,正向查询)
print(book_obj.publish) # 出版社对象:北方出版社
2.查询主键为2的书籍对应的作者(书>>>作者)
# 2.1 先拿到书籍对象
book_obj = models.Book.objects.filter(pk=2).first()
# 2.2 以对象为基准 判断正反向(书查作者 多对多 外键在书籍表中 正向)
print(book_obj.authors) # app01.Author.None
print(book_obj.authors.all()) # <QuerySet [<Author: 作者对象:jason>, <Author: 作者对象:kevin>]>
因为查询结果对象有多个,所以book_obj.authors返回的是app01.Author.None。要想获取完整的作者列表,需要用 ll()方法: book_obj.authors.all()
3.查询jason的电话(作者详情)
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.author_detail.phone) # 110
4.查询南方出版社出版的书籍
# 4.1 先拿到出版社对象
publish_obj = models.Publish.objects.filter(name='南方出版社').first()
# 4.2 由出版社到书籍 判断正反向(一对多 外键不在出版社表中 反向)
# print(publish_obj.book) # AttributeError: 'Publish' object has no attribute 'book'
print(publish_obj.book_set) # app01.Book.None
print(publish_obj.book_set.all()) # <QuerySet [<Book: 书籍对象:C语言程序设计>]>
由于一个出版社对应多个书籍,所以用句点符点书籍时应该加上_set
由于查询结果有多条,所以book_set后面应该.all()
5.查询jason写过的书
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set.all()) # <QuerySet [<Book: 书籍对象:java高并发>, <Book: 书籍对象:爬虫入门到入狱>]>
6.查询电话是110的作者
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(author_detail_obj.author) # 作者对象:jason
这里由于是一对一关系,一个作者对应一个作者详情,查询结果仅有一个,所以不用加_set.all()
基于双下划线的跨表查询(连表操作)
# 1.查询主键为1的书籍对应的出版社名称及书名
res = models.Book.objects.filter(pk=1).values('publish__name', 'title')
print(res) # <QuerySet [{'publish__name': '北方出版社', 'title': 'java高并发'}]>
# 2.查询主键为3的书籍对应的作者姓名及书名
res = models.Book.objects.filter(pk=2).values('authors__name', 'title')
print(res) # <QuerySet [{'authors__name': 'jason', 'title': '爬虫入门到入狱'}, {'authors__name': 'kevin', 'title': '爬虫入门到入狱'}]>
# 3.查询jason的作者的电话号码和地址
res = models.Author.objects.filter(name='jason').values('author_detail__phone', 'author_detail__address')
print(res) # <QuerySet [{'author_detail__phone': 110, 'author_detail__address': '芜湖'}]>
# 4.查询南方出版社出版的书籍名称和价格
res = models.Publish.objects.filter(name='南方出版社').values('book__title', 'book__price')
print(res) # <QuerySet [{'book__title': 'C语言程序设计', 'book__price': Decimal('222.22')}]>
# 5.查询jason写过的书的名称和日期
res = models.Author.objects.filter(name='jason').values('book__title', 'book__join_time')
print(res)
# 6.查询电话是110的作者姓名和年龄
res = models.AuthorDetail.objects.filter(phone=110).values('author__name', 'author__age')
print(res) # <QuerySet [{'author__name': 'jason', 'author__age': 19}]>
作者:爱新觉罗吉星
来源:稀土掘金