1. ORM简介
ORM:Object Relational Mapping(对象关系映射),通常把一个类和一张表一一对应,类的每个实例对应表的一条记录,类的每个属性对应表的每个字段。
ORM操作本质:根据对接的数据库引擎,翻译成对应的SQL语句;所有使用Django开发的项目无需关心底层使用的MySQL、Oracle、sqlite、PostgreSQL......,如果数据库迁移,只需要更换Django的数据库引擎即可。
ORM缺点:相比较直接使用SQL语句操作数据库,有性能损失;根据对象的操作转换成SQL语句,根据查询的结果转化成对象,在映射过程中有性能损失。
2. Django ORM
以Django项目使用PG数据库为例。
- 手动创建数据库
- 创建Django项目,在Django项目中的settings.py配置数据库连接信息
3)使用psycopg2连接PG数据库,一般是在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
4)创建模型,
模型成员objects : 管理器对象
- 是Manager类型的对象,定义在from django.db import models中
- 用于模型对象和数据库交互
- 是默认自动生成的属性,但是可以自定义管理器对象
- 自定义管理器对象后,Django不再生成默认管理器对象objects
在项目中的models.py中添加如下类:
创建模型的参数介绍如下:
字段类型 | 描述 |
AutoField | 自动增长的IntegerField, 不指定时Django会自动创建属性名为id的自动增长属性 |
BooleanField | 布尔字段,值为True或False |
NullBooleanField | 支持Null、True、False三种值 |
CharField | 字符串参数max_length表示最大字符个数 |
TextFiled | 大文本字段,一般超过4000个字符时使用 |
IntegerField | 整数 |
DecimalField | 可以指定精度的十进制浮点数,max_digits表示总位数,decimal_places表示小数位数 |
FloatField | 浮点数 |
DateField | 日期,格式 YYYY-MM-DD,相当于Python中的datetime.date()实例 auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认false auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认false auto_now_add和auto_now是相互排斥的,组合将会发生错误 |
TimeField | 参数和DateField一样 |
DateTimeField | 日期,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例 |
FileField | 上传文件字段,以二进制的形式 |
ImageField | 继承于FileField,对上传的内容进行校验,确保是有效的图片 |
ForeignKey | 外键,models.ForeignKey("Publish", on_delete=models.CASCADE) on_delete=models.CASCADE为级联删除 |
ManyToManyField | 多对多,models.ManyToManyField("Author") |
EmailField | 邮箱类型,底层继承 CharField |
OneToOneField | OneToOneField = ForeignKey(...,unique=True)设置一对一 models.OneToOneField("AuthorDetail", on_delete=models.CASCADE) |
SmallIntegerField | 整数 |
字段选项 | 描述 |
null | True,表示空,默认False |
blank | True,表示空白,默认False ,null是数据库范畴的概念,blank是表单验证范畴 |
db_column | 字段的名称,如果未指定,则使用属性的名称(只限于数据库表中的名字,操作数据库还是类属性的名字) |
db_index | True, 则在表中会为此字段创建索引,默认False |
default | 默认值,这可以是值或可调用对象,如果可调用,则每次创建新对象时都会调用它 |
primary_key | True,则该字段会成为模型的主键字段,默认False,一般作为AutoField的选项使用 |
unique | True, 这个字段在表中必须有唯一值,这个值不能重复,默认False |
verbose_name | 后台显示字段名 |
5)使用,在views.py中:
- 模型类实例化对象,执行对象.save()
- 通过 ORM objects 提供的方法来实现(推荐),其方法总结如下:
方法 | 用法描述 |
create | 添加数据,Book.objects.create(title="test",price=20,publish="test",pub_date="2021-12-22") |
all | 查询所有内容,返回QuerySet 类型数据,类似于 list,里面是一个个模型类的对象,可用索引下标取出模型类的对象,Book.objects.all() |
filter | 查询符合条件的数据,返回QuerySet 类型数据,Book.objects.filter(pk=3), 注:pk=3的意思是主键primary key=3,相当于id=3 因为id在pycharm里有特殊含义,是看内存地址的内置函数id(),因此用pk。 filter 中运算符号只能使用等于号 = ,不能使用大于号 > ,小于号 < ,等等其他符号。 |
exclude | 查询不符合条件的数据,返回QuerySet 类型数据,Book.objects.exclude(price=30) |
get | 查询符合条件的返回模型类的对象符合条件的对象只能为一个,如果符合筛选条件的对象超过了一个或者没有一个都会抛出错误,Book.objects.get(pk=5) |
order_by | 对查询结果进行排序,返回QuerySet 类型数据,降序Book.objects.order_by("-price") |
reverse | 对查询结果进行反转,返回QuerySet 类型数据,Book.objects.order_by("-price").reverse() |
count | 查询数据的数量,返回的数据是整数,Book.objects.filter(price=200).count() |
first | 返回所有数据的第一条,返回的数据是模型类的对象也可以用索引下标 [0],Book.objects.first() |
last | 返回最后一条数据,返回的数据是模型类的对象不能用索引下标 [-1],ORM 没有逆序索引,Book.objects.last() |
exists | 判断查询的结果 QuerySet 列表里是否有数据,返回的数据类型是布尔, true或false判断的数据类型只能为 QuerySet 类型数据,不能为整型和模型类的对象。
|
values | 查询部分字段的数据,返回QuerySet 类型数据,想要字段名和数据用 values books = Book.objects.values("pk","price"), print(books[0]["price"] type(books)) |
values_list | 查询部分字段的数据,返回QuerySet 类型数据,只想要数据用 values_list books = models.Book.objects.values_list("price","publish") print(books[0][0],type(books)) |
distinct | 对数据进行去重,distinct() 一般是联合 values 或者 values_list 使用 Book.objects.values_list("publish").distinct() |
delete | 删除,模型类的对象.delete();推荐,QuerySet 类型数据.delete()。 Book.objects.filter(pk=8).first().delete() Book.objects.filter(pk__in=[1,2]).delete() Book.objects.all().delete() |
save | 修改,模型类的对象.属性 = 更改的属性值,模型类的对象.save() books = models.Book.objects.filter(pk=7).first() books.price = 400 books.save() |
update | 推荐,修改,QuerySet 类型数据.update(字段名=更改的数据) Book.objects.filter(pk__in=[7,8]).update(price=888) |
查询条件 | 描述 |
= | 等于号 |
__in | 区间,Book.objects.filter(price__in=[200,300]) |
__gt | 大于号,Book.objects.filter(price__gt=200) |
__gte | 大于等于号,Book.objects.filter(price__gte=200) |
__lt | 小于号,Book.objects.filter(price__lt=300) |
__lte | 小于等于号,Book.objects.filter(price__lte=300) |
__range | 在……之间,左闭右闭区间,Book.objects.filter(price__range=[200,300]) |
__contains | 包含,Book.objects.filter(title__contains="菜") |
__icontains | 不区分大小写的包含,Book.objects.filter(title__icontains="python") |
__startswith | 以指定字符开头,Book.objects.filter(title__startswith="菜") |
__endswith | 以指定字符结尾,Book.objects.filter(title__endswith="教程") |
__year | DateField 数据类型的年份,Book.objects.filter(pub_date__year=2008) |
__month | DateField 数据类型的月份,Book.objects.filter(pub_date__month=10) |
__day | DateField 数据类型的天数,Book.objects.filter(pub_date__day=01) |
__week_day | DateField 数据类型的星期几,0-6表示周一到周天 Book.objects.filter(pub_date__week_day=4).count() |
3. 分组和聚合查询
3.1 分组查询
annotate(*args,**kwargs) 分组函数,查看 每个出版社出版的最贵的一本书
Book.objects.values('publish').annotate(Min('price'))
# < QuerySet[
# {'publish': '北大出版社','price__min': Decimal('67.000')},
# {'publish': '山西出版社','price__min': Decimal('34.000')},
# {'publish': '河北出版社', 'price__min': Decimal('123.000')},
# {'publish': '浙江出版社', 'price__min': Decimal('2.000')},
# {'publish': '湖北出版社', 'price__min': Decimal('124.000')},
# {'publish': '湖南出版社',price__min': Decimal('15.000')}
# ] >
3.2 聚合查询
aggregate(*args,**kwargs) 聚合函数,求书籍的平均价
Book.objects.all().aggregate(Avg('price')) #{'price__avg': 145.23076923076923}
4. Q和F查询
仅仅靠单一的关键字参数查询已经很难满足查询要求。为此Django提供了F和Q查询。
4.1 Q查询
Q多条件组合查询,Q查询条件和非Q查询条件混合使用注意,不包Q()的查询条件一点要放在Q(查询条件)后面。
Q()可以使ORM的fifter()方法支持, 多个查询条件,使用逻辑关系(&、|、~)包含、组合到一起进行多条件查询;
语法:
- fifter(Q(查询条件1)| Q(查询条件2))
- fifter(Q(查询条件1)& Q(查询条件2))
- Fifter(Q(查询条件1)& ~Q(查询条件1))
- fifter(Q(查询条件1)| Q(Q(查询条件2)& ~ Q(Q(查询条件3)& Q(查询条件4)))
用法1:
from django.db.models import F,Q
from app.models import Book # from app import models models.Book.objects.all()
Book.objects.filter(Q(title__icontains='伟')|Q(publish__contains='山西')).values('title')
用法2:
from django.db.models import F,Q
con = Q()
price_list = [34.00,60.00,100.00]
pub_date = [2008,2010,2020]
for index,value in price_list:
sql1 = Q()
sql1.connector = 'OR'
sql1.children.append('price',value)
con.add(sql1, 'AND')
for index,value in pub_date:
sql2 = Q()
sql2.connector = 'OR'
sql2.children.append('pub_date__year',value)
con.add(sql2, 'AND')
sql = Book.objects.using('bookinfo').filter(con).values('title','price')
4.2 F查询
F查询可以获取对象中的字段的属性(列),并对其进行操作。
from django.db.models import F,Q
Book.objects.all().update(price=F('price')+1) #对图书馆里的每一本书的价格上调1块钱
5. 多表查询
多表模型建立如下:
关联管理器(对象调用):多对多(双向均有关联管理器);一对多(只有多的那个类的对象有关联管理器,即反向才有)。
语法格式:
正向:属性名
book_obj = models.Book.objects.get(id=10)
author_list = models.Author.objects.filter(id__gt=2)
book_obj.authors.add(*author_list) #将id大于2的作者对象添加到这本书的作者集合中
# 方式二:传对象 id
book_obj.authors.add(*[1,3]) #将id=1和id=3的作者对象添加到这本书的作者集合中
反向:小写类名加 _set
ying = models.Author.objects.filter(name="任盈盈").first()
book = models.Book.objects.filter(title="冲灵剑法").first()
ying.book_set.add(book)
常用方法 | 描述 |
add() | 用于多对多,把指定的模型对象添加到关联对象集(关系表)中,注: add() 在一对多(即外键)中,只能传对象( *QuerySet数据类型),不能传 id(*[id表]) |
create() | 创建一个新的对象,并同时将它添加到关联对象集之中,返回新创建的对象 |
remove() | 从关联对象集中移除执行的模型对象, 对于 ForeignKey 对象,这个方法仅在 null=True(可以为空)时存在,无返回值。 |
clear() | 从关联对象集中移除一切对象,删除关联,不会删除对象。 对于 ForeignKey 对象,这个方法仅在 null=True(可以为空)时存在,无返回值。 |
__lt | 小于号,Book.objects.filter(price__lt=300) |
__lte | 小于等于号,Book.objects.filter(price__lte=300) |
__range | 在……之间,左闭右闭区间,Book.objects.filter(price__range=[200,300]) |
__contains | 包含,Book.objects.filter(title__contains="菜") |
__icontains | 不区分大小写的包含,Book.objects.filter(title__icontains="python") |
__startswith | 以指定字符开头,Book.objects.filter(title__startswith="菜") |
__endswith | 以指定字符结尾,Book.objects.filter(title__endswith="教程") |
__year | DateField 数据类型的年份,Book.objects.filter(pub_date__year=2008) |
__month | DateField 数据类型的月份,Book.objects.filter(pub_date__month=10) |
__day | DateField 数据类型的天数,Book.objects.filter(pub_date__day=01) |
__week_day | DateField 数据类型的星期几,0-6表示周一到周天 Book.objects.filter(pub_date__week_day=4).count() |
5.1 正向查找
一对多:
res1 = models.Book.objects.filter(title='Python').values('publish__city')
print(res1) #[{'publish__city': '北京'}]
多对多:
ret2 = models.Book.objects.filter(title='Python').values('author__name')
print(res2)
ret3 = models.Book.objects.filter(author__name="alex").values('title')
print(ret3)
正向查找的publish__city或者author__name中的publish,author是book表中绑定的字段
5.2 反向查找
一对多:
res4 = models.Publish.objects.filter(book__title='Python').values('name')
print(res4) #[{'name': '人大出版社'}] book__title中的book就是Publish的关联表名
res5 = models.Publisher.objects.filter(book__title='Python').values('book__authors')
print(res5) #[{'book__authors': 1}, {'book__authors': 2}]
多对多:
res6 = models.Author.objects.filter(book__title='Python').values('name')
print(res6) #[{'name': 'alex'}, {'name': 'alvin'}]
正向查找的book__title中的book是小写类名book
6.参考链接
博客园:Django之ORM查询操作详解 - 小帅帅呆了 - 博客园
博客园:Django ORM之F与Q查询 - 秋刀鱼的滋味w - 博客园
Django使用union合并:
Django 使用 union 合并不同模型(Model) 的查询集(QuerySet)_Django ORM 高级操作_追梦人物的博客