ORM
ORM框架的目的是不再使用SQL语句,让用户使用对象类和对象的方式和这个框架内部的方法来进行数据库操作,对于ORM框架来说,把类和对象转换成SQL语句.
优点:
1.ORM提供了对数据库的映射,不用直接编写SQL代码,只需要操作对象就能对数据库操作数据
2.让软件开发人员专注于业务逻辑的处理,提高了开发的效率
缺点:
1.ORM的缺点是会在一定程度上牺牲程序的执行效率
2.ORM的操作是有限的,也就是ORM定义好的操作是可以完成的
对应关系:
类 --> 数据表
对象 --> 数据行
属性 --> 字段
ORM表模型
一对一(one-to-one)
实质就是在主外键的关系基础上,给外键加了一个unique=Ture的属性,方法OneToOnefiled()
一对多(one-to-many)
什么时候需要外键
一张表里的数据会在其它多个表中被使用,这个数据就会被称为外键
在Django中使用外键
publisher = models.Foreignkey(to='类名')
ORM在数据库中创建该字段的时候会默认在后面加_id
在ORM中使用外键
book_obj.publisher --> 表示的是和书关联的出版社对象(ORM帮我们封装的)
book_obj.publisher_id --> 表示数据库中真正保存的字段(int)
多对多(many-to-many)
语法:book = models.ManyToManyField(to='book')
自动创建第三张表:实质就是创建了两个foreign_key
什么是QuerySet
从数据库中查询出来的结果一般是一个集合,这个集合叫做QuerySet。
注意:
(1)QuerySet是可以迭代的
(2)支持切片
(3)可以用len()和count()函数测量数量。
(4)可以通过list()函数强行变成列表。
ORM的增删查改
查
类名.objects.all() --> 查找数据库中该表的所有数据
类名.objects.filter(条件) --> 根据给定的条件取数据库中查找,返回的是列表
类名.objects.get(条件) --> 根据条件找到一个对象(找到多个或者找不到都报错),返回的是对象
多对多的查询:
author_obj.book.all() ---> 返回一个书籍的列表
增
类名.objects.create()
多对多的增加:
对象.book.add(id1, id2) --> 多对多添加关联的数据
删
类名.objects.filter(条件).delete()
类名.objects.get(条件).delete()
多对多的删除:
对象.book.clear()
补充Django2.0以上的版本,外键需要手动设置级联操作 on_delete=models.CASCADE
改
obj = 类名.objects.get(条件)
obj.属性 = ‘新值’
obj.save()
类名.objects.filter(条件).update(字段=‘新值’)
多对多的修改:
author_obj.book.set([id1, id2]) --> 多对多的设置关联的数据
外键的增删查改
- 查
Book.objects.all() --> 返回的是一个列表(QuerySet)
Book.objects.filter() --> 返回的是一个列表(QuerySet)
Book.objects.get() --> 返回的是一个具体书籍对象
- 删
Book.objects.filter().delete()
Book.objects.get().delete()
- 增
Book.objects.create(title='', publisher='出版社对象')
Book.objects.create(title='', publisher_id='出版社id值')
- 改
(1) 基于对象的修改
book_obj = Book.objects.get(id='id值')
book_obj.title = '书名'
book_obj.publisher_id = '出版社id值'
book_obj.save()
(2) 基于QuerySet的修改
Book.objects.filter(id='id值').update(title='值',publisher_id='值')
ORM常用字段和参数
常用字段
AutoField
int自增列,必须填入参数primary_key=Ture.当model中如果没有自增列的时候会自动创建
IntegerField
一个整数类型,范围在-2147483648 to 2147483647,只有10位不能表示手机号。
CharField
字符类型,必须提供max_length参数, max_length表示字符长度。
DataField
日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。
Data TimeField
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例
DecimalField
小数字段
DecimalField(max_digits=5, decimal_places=2),
max_digits定义小数总长度,
decimal_places`定义小数位数
其它字段
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
自定义字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
自定义char类型字段:
class FixedCharField(models.Field):
"""
自定义的char类型的字段类
"""
def __init__(self, max_length, *args, **kwargs):
super().__init__(max_length=max_length, *args, **kwargs)
self.length = max_length
def db_type(self, connection):
"""
限定生成数据库表的字段类型为char,长度为length指定的值
"""
return 'char(%s)' % self.length
class Class(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=25)
# 使用上面自定义的char类型的字段
cname = FixedCharField(max_length=25)
常用的字段参数
null 数据库中字段是否可以为空
db_column 数据库中字段的列名
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引
DatetimeField和Datefield独有:
auto_now_add --> 当前数据的创建时间
auto_now --> 当前数据的最后修改时间
带choice参数的字段
get_字段名_display()
建表的元信息
class Meta:
db_table = '表名'
unique_together = (('ip', 'port'),)
index_together = (("pub_date", "deadline"),)
必知必会13条
- 返回QuerySet列表类型的
1. filter():筛选条件匹配的对象
2. all():查询所有的结果
3. exclude():筛选出与条件不匹配的对象
4. order_by():对查询的结果排序
5. reverse():对查询的结果反向排序
6. distinct():从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)
7. values(): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
8. values_list() :它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
2. 返回具体对象
1. get():返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
2. first():返回第一条记录
3. last():返回最后一条记录
3. 返回布尔值
1. exists():如果QuerySet包含数据,就返回True,否则返回False
4. 返回数字的
1. count():返回数据库中匹配查询(QuerySet)的对象数量。
神奇的下划线(单表查询)
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 3]) # id范围是1到3的,等价于SQL的bettwen and
类似的还有:startswith,istartswith, endswith, iendswith
date字段还可以:
models.Class.objects.filter(first_day__year=2017)
ORM关联查询
基于对象的查询
正向查询
对象.关联字段
反向查询
1.默认不设置related_name属性
(1)查找的对象是多个的时候(一对多或者多对多)
publisher_obj.book_set.all()
(2)查找一个对象时(一对一)
author_info_obj.autjor.name
2.设置related_name=‘books’属性
publisher_obj.books.all()
基于QuerySet的查询
正向查询
Book.objects.filter(id=1).values_list(‘publisher__name’)
反向查询
1.默认不设置related_name属性,默认就用类名的小写
Publisher.objects.filter(id=1).values_list(‘book__price’) 2.设置related_name属性
Publisher.objects.filter(id=1).values_list(‘books__price’)
聚合查询和分组查询
聚合
Django在django.db.models中提供了许多聚合函数,aggregate()
是Queryset的一个终止子句,意思是它返回一个包含键值对的字典不能使用values()和value_list()
聚合函数:
from django.db.models import Avg,Sum,Max,Min,Count:
假如要求一个表中某个数值列的平均数
from django.db.models import Avg,Sum,Max,Min,Count:
表名.objects.all().aggregate(Avg('列名'))
也可以指定一个名字
表名.objects.all().aggregate(昵称=Avg('列名'))
也可以使用多个聚合函数
表名.objects.all().aggregate(Avg('列名'),Max('列名'))
分组
假设现在有一张公司职员表:
image
我们使用原生SQL语句,按照部分分组求平均工资:
select dept,AVG(salary) from employee group by dept;
ORM查询:
from django.db.models import Avg
Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")
连表查询的分组:
image
SQL查询:
select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;
ORM查询:
from django.db.models import Avg
models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")
F查询和 Q查询
F查询
当我们需要进行两个字段的值作比较的时候我们使用F()来做比较.F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
查询评论数大于收藏数的书籍
from django.db.models import F
models.Book.objects.filter(commnet_num__gt=F('keep_num'))
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)
修改操作也可以使用F函数,比如将每一本书的价格提高30元
models.Book.objects.all().update(price=F("price")+30)
如果要修改char字段咋办?
如:把所有书名后面加上(第一版)
>>> from django.db.models.functions import Concat
>>> from django.db.models import Value
>>> models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))
Q查询
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。
你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
示例:查询作者名字是小仙女并且不是2018年出版的书的书名
models.Book.objects.filter(Q(author__name="小仙女") & ~Q(publish_date__year=2018)).values_list("title")
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。
例如:查询出版年份是2017或2018,书名中带物语的所有书。
models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="物语")
在Django终端打印SQL语句
在Django项目的settings.py文件中,在最后复制粘贴如下代码:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
网友评论