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,)