Django框架05 /orm单表操作
1. orm使用流程
-
配置mysql
# django 连接mysql,settings配置文件中
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'orm02',
'USER':'root',
'PASSWORD':'123',
'HOST':'127.0.0.1',
'PORT':3306,
}
} -
用pymysql替换mysqldb
# 项目文件夹下的init文件中写上下面内容,用pymysql替换mysqldb
import pymysql
pymysql.install_as_MySQLdb() -
models文件中创建一个类
class UserInfo(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=10)
bday = models.DateField()
checked = models.BooleanField() BooleanField() # 不能设置空值,只能设置默认值 -
执行数据库同步指令
python manage.py makemigrations
python manage.py migrate # 执行数据库同步指令,添加字段的时候别忘了,该字段不能为空,所有要么给默认值,要么设置它允许为空 null=True -
创建记录(实例一个对象,调用save方法)
from app01 import models def query(request):
# 创建一条记录,增
new_obj = models.UserInfo(
id=2,
name='张三',
bday='2019-09-27',
checked=1,
)
new_obj.save()
# 翻译成sql语句,然后调用pymysql,发送给服务端
# insert into app01_userinfo values(2,'张三','2019-09-27',1) return HttpResponse('xxx') -
orm语句执行流程
orm语句 -- sql -- 调用pymysql客户端发送sql -- mysql服务端接收到指令并执行
2. orm字段
-
CharField
- 字符串字段, 用于较短的字符串.
- CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数. -
IntegerField
用于保存一个整数.
-
DecimalField
一个浮点数. 必须 提供两个参数: 参数 描述
max_digits 总位数(不包括小数点和符号)
decimal_places 小数位数 # 举例
# 要保存最大值为 999 (小数点后保存2位),要这样定义字段:
models.DecimalField(..., max_digits=5, decimal_places=2) # 要保存最大值一百万(小数点后保存10位)的话,要这样定义:
models.DecimalField(..., max_digits=17, decimal_places=10)
# max_digits大于等于17就能存储百万以上的数了 -
BooleanField
用 checkbox 来表示此类字段,用来存储布尔值
-
TextField
一个容量很大的文本字段
-
EmailField
判断输入的是不是一个合法的email字段,不接受 maxlength 参数.
-
DateField
一个日期字段, 共有下列额外的可选参数:
Argument 描述
auto_now 当对象被保存时(更新或者添加都行),自动将该字段的值设置为当前时间.
通常用于表示 "last-modified" 时间戳.
auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间. -
DateTimeField
一个日期时间字段. 类似 DateField 支持同样的附加选项.
-
AutoField
一个 IntegerField, 添加记录时它会自动增长. 通常不需要直接使用这个字段;
自定义一个主键:my_id=models.AutoField(primary_key=True)
如果不指定主键的话,系统会自动添加一个主键字段到model. -
URLField
用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在
即URL是否被有效装入且没有返回404响应 -
FileField
一个文件上传字段. 要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime 注意:
在一个 model 中使用 FileField 或 ImageField 需要以下步骤:
1、在settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件.
(出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对
WEB服务器用户帐号是可写的.
2、在model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django
使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). -
ImageField
它有两个可选参数:height_field和width_field,
类似 FileField, 不过要校验上传对象是否是一个合法图片.
如果提供这两个参数,则图片将按提供的高度和宽度规格保存.
3. orm参数
-
null
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
-
blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。 -
default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
-
primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。 -
unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
-
choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。
-
auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
-
auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
DatetimeField、DateField、TimeField这个三个时间字段,都可以设置auto_now_add、auto_now属性。
-
时间问题:auto_now_add/auto_now
models.UserInfo.objects.create(
name='张三',
bday=current_date, # 直接插入时间没有时区问题
checked=0
) now = models.DateTimeField(auto_now_add=True,null=True)
# 如果自动来插入时间,就会有时区的问题,auto_now_add创建记录时自动添加当前创建记录时的时间,存在时区问题 # 解决方法:
# settings配置文件中将USE_TZ的值改为False
# USE_TZ = True
USE_TZ = False # 告诉mysql存储时间时按照当地时间来存,不要用utc时间
# 使用pycharm的数据库客户端的时候,时区问题要注意
4. orm单表简单增/删/改
-
增:
# 方式1:
new_obj = models.UserInfo(
id=2,
name='张三',
bday='2019-09-27',
checked=1,
)
new_obj.save() # 方式2:
# ret 是创建的新的记录的model对象
ret = models.UserInfo.objects.create(
name='张三',
bday='2019-08-07',
checked=0
) print(ret) # UserInfo object
print(ret.name) # '张三'
print(ret.bday) # '2019-08-07' -
批量增加/bulk_create
book_list = []
for i in range(10):
bk_obj = models.Book(
name='张三%s'%i,
addr='北京%s'%i
)
book_list.append(bk_obj) models.Book.objects.bulk_create(book_list) # 批量插入,速度快 -
删
# 简单查询:filter()
# 结果是queryset类型的数据里面是一个个的model对象,类似于列表 models.UserInfo.objects.filter(id=6).delete() # queryset对象调用
models.UserInfo.objects.filter(id=6)[0].delete() # model对象调用 -
改
# 方式1:update
models.UserInfo.objects.filter(id=2).update(
name='李四',
checked = 0,
) # 错误示例,model对象不能调用update方法
models.UserInfo.objects.filter(id=2)[0].update(
name='李四',
checked = 0,
)
# 方式2
ret = models.UserInfo.objects.filter(id=2)[0]
ret.name = '李四'
ret.checked = 1
ret.save() nowtime = models.DateTimeField(auto_now=True,null=True)
# 注意:
# 更新时的auto_now参数
# 更新记录时,自动更新时间,创建新纪录时也会帮你自动添加创建时的时间,但是在更新时只有使用方式2的save方法才能自动更新时间,有缺陷
5. orm单表查询
5.1 查询api
-
all()
- 查询所有结果,结果是queryset类型
ret = models.Books.objects.all()
print(ret) < QuerySet[ < Books: Books object >, < Books: Books object >] > -
filter(**kwargs) -- 条件查询
- 包含与所给筛选条件相匹配的对象,结果也是queryset类型
- 如果没有写查询条件会获取所有数据,queryset类型的数据还能够继续调用fitler方法
- 查询条件不能匹配到数据时,不会报错,返回一个空的queryset,<QuerySet []>
ret = models.Books.objects.filter(name='linux')
print(ret)
<QuerySet [<Books: Books object>, <Books: Books object>]> #多条件查询用,隔开--是and的关系
ret = models.Books.objects.filter(name='linux',price=113)
print(ret)
<QuerySet [<Books: Books object>, <Books: Books object>]> -
get(kwargs) ***
- 返回与所给筛选条件相匹配的对象,不是queryset类型,是model对象
- 返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。
ret = models.Books.objects.get(id=5)
print(ret)
Books object # 报错原因:
# 1. 查不到数据会报错 :Book matching query does not exist.
# 2. 超过一个就报错 :returned more than one Book -- it returned 13! -
exclude(**kwargs) -- 排除
- 排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作,用这个exclude,返回值是queryset类型
- model对象和queryset对象都可以调用
Book.objects.exclude(id=6) # 返回id不等于6的所有的对象
Book.objects.all().exclude(id=6) # 在queryset基础上调用# model对象调用:
ret = models.Books.objects.exclude(name='python')
print(ret) <QuerySet [<Books: Books object>, <Books: Books object>, <Books: Books object>]> # queryset对象调用:
ret = models.Books.objects.all().exclude(name='python')
print(ret) <QuerySet [<Books: Books object>, <Books: Books object>, <Books: Books object>]> -
order_by(*field) -- 排序
- queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型
- 两种方式改变升序和降序:
- 1.在传入fields参数的时候,在参数前加-代表降序,不加代表升序.
- 2.在fields的后面,再加上方法进行改变, asc( )代表升序 ,desc( )代表降序.
- 切忌:
- 不要在一个order_by后又加一个order_by,后面的会覆盖掉之前的查询结果.
升序排序:
models.Book.objects.all().order_by('price') # 默认是按照price升序排列 降序排序:
models.Book.objects.all().order_by('-price') # 按照字段降序排列,就写个负号就行了 多条件排序:
models.Book.objects.all().order_by('price','id') # 是多条件排序,按照price进行升序,price相同的数据,按照id进行升序 -
reverse() -- 反转
- queryset类型的数据来调用,返回值还是queryset类型
- 对查询结果反向排序,只适用于已经默认排序或者使用order_by排序过后的查询集,如果未排序,则没有效果.
-
count() -- 计数、统计返回结果的数量
- queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量
-
first()
- queryset类型的数据来调用,得到的都是model对象,返回第一条记录
Book.objects.all()[0] = Book.objects.all().first()
# 得到的都是model对象,不是queryset -
last()
- queryset类型的数据来调用,返回最后一条记录
-
exists() -- 判断返回结果是否有数据
- queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False,效率高
models.Book.objects.all().exists() # exists() 括号内不能放数据 # 翻译成的sql是SELECT (1) AS `a` FROM `app01_book` LIMIT 1,
# 就是通过limit 1,取一条来看看是不是有数据 -
values(*field)
- queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet
- model的实例化对象来调用,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。
# 示例一:
ret = models.Books.objects.all().values('name')
print(ret) <QuerySet [{'name': 'python'}, {'name': 'linux'}, {'name': 'Go'}]> # 示例二:
ret = models.Books.objects.values('name')
print(ret) <QuerySet [{'name': 'python'}, {'name': 'python'}, {'name': 'linux'}, {'name': 'python'}, {'name': 'linux'}, {'name': 'Go'}]>
# 调用values或者values_list的对象是objects控制器,那么返回所有数据 -
values_list(*field)
- 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
ret = models.Books.objects.all().values_list('name')
print(ret)
<QuerySet [('python',), ('linux',), ('Go',)]> -
distinct() -- 去重、配置values和values_list来使用
- distinct()括号里不能加去重的条件
- values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录
ret = models.Books.objects.values('name').distinct()
print(ret) <QuerySet [{'name': 'python'}, {'name': 'linux'}, {'name': 'Go'}]> # 错误示例:
ret = models.Books.objects.all().distinct()
print(ret)
<QuerySet [<Books: python>, <Books: python>, <Books: linux>, <Books: python>, <Books: linux>, <Books: Go>]> # 注意:
# 不在values()、values_list()后面使用没有意义,因为要是所有的内容全相同才会去重
5.2 基于双下划线的模糊查询 / filter双下划线查询
-
在里边:
__in
Book.objects.filter(price__in=[100,200,300])
# price值等于这三个里面的任意一个的对象 -
大于:
__gt
Book.objects.filter(price__gt=100) # 大于
-
大于等于:
__gte
Book.objects.filter(price__gte=100)
# 大于等于,别写price>100,这种参数不支持 -
小于:
__lt
Book.objects.filter(price__lt=100) # 小于
-
小于等于:
__lte
Book.objects.filter(price__lte=100) # 小于
# 小于等于,别写price>100,这种参数不支持 -
包含:
__contains
Book.objects.filter(title__contains="python")
# title值中包含python的 -
包含,不区分大小写:
__icontains
Book.objects.filter(title__icontains="python")
# 不区分大小写 -
以什么开头:
__startswith
Book.objects.filter(title__startswith="py")
# 以什么开头 -
以什么开头,不区分大小写:
__istartswith
Book.objects.filter(title__istartswith="py")
# 不区分大小写 -
日期相关
# 筛选日期
Book.objects.filter(pub_date='2012-9-12') # 筛选年份
Book.objects.filter(pub_date__year=2012)
# 筛选大于该年份的
Book.objects.filter(pub_date__ year_gt='2018') # 大于2018年的、2018数字类型也可以 # 筛选某年某月
Book.objects.filter(pub_date__year='2019' ,pub_ date__ month='8') # 筛选某年某月某日
Book.objects.filter(pub_date__ year='2019' ,pub_date__month='8',pub_date__day='01') -
判断是否为空
models.Book.objects.filter(publish_date__isnull=True)
# 这个字段值为空的那些数据
总结:
新增字段(必须设置可以为空或者设置默认值)
UTC时间格式格林尼治时间
model对象不能调用update方法
USE_TZ = False
-
orm整体流程代码示例
orm > settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'orm1',
'HOST':'127.0.0.1',
'PORT':3306,
'USER':'root',
'PASSWORD':'123'
}
} # 设置mysql默认使用当地时间
USE_TZ = False # 不是跨时区的应用,不需要考虑时区问题,就将这个值改为False
# mysql是对时区不敏感,django往mysql里面出数据的时候
# 如果这里的值为True,那么将让mysql强制使用UTC时间
# 那么存储时间后,当查询的时候,就会发现,时间晚了8小时orm1 > __init__.py
import pymysql
pymysql.install_as_MySQLdb()models.py
from django.db import models class Userinfo(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=16)
register_time = models.DateField(null=True)
# auto_now_add=True 修改记录不会改变,只会在添加数据的时候添加时间
update_time = models.DateTimeField(auto_now=True,null=True)
# auto_now=True, 修改记录用方式一/update不会改变、用方式二/ret.save()会更新
checked = models.BooleanField(default=0)views.py
from django.shortcuts import render,HttpResponse
from app01 import models
from datetime import datetime def query(request):
# 创建记录
# 方式一
current_time = datetime.now()
new_obj = models.Userinfo(
id = 1,
name = 'ZHANGSAN'
)
new_obj.save() # 方式二
ret =models.Userinfo.objects.create(
name='ZHANGSAN',
register_time='2019-8-8',
update_time='2019-8-10 12:12:12',
checked=1, )
ret = models.Userinfo.objects.filter(name='ZHANGSAN')
print(ret) # 删除
ret = models.Userinfo.objects.filter(id=7)[0].delete()
print(ret)
# 将符合条件的第一条记录删除 ret = models.Userinfo.objects.filter(id=7).delete()
print(ret)
# 将符合条件的所有记录删除
# 注意:filter搜索出来的是queryset类型的列表:queryset<[对象1,对象2,对象3]> # 修改
# 方式一
models.Userinfo.objects.filter(id=1).update(
name='ZHANGSAN',
checked=1,
register_time = '2019-1-1',
update_time = '2020-06-03',
) # 方式二
ret = models.Userinfo.objects.filter(id=1)[0]
ret.name='LISI'
ret.save()
return HttpResponse('OK')
# 注意:model对象不能调用update()方法 -
单表查询返回models对象的方法
1.get(**kwargs)
2.create(**kwargs)
3.count()
# 该方法返回与之匹配的数据库中对象的个数,并且有一个最大的特点,永远不会返回异常
4.first()
5.last()
6.get_or_create(defaults=None,**kwargs)
# 如果存在则返回查找的对象,如果不存在则创建一个新的对象并且保存.
7.update_or_create(defaults= None,** kwargs)
# 该方法查找defaults中的对象,如果找到则更新数值,如果没有,则创建对象. -
单表查询返回queryset对象的方法
1.all()
2.filter(**kwargs)
3.exclude(**kwargs)
4.order_by( *fields)
5.reverse()
6.distinct()
7.values(*fields,)
8.values_list(*fields,)