# 单表操作
# 前期准备
models.py 编写对应生成的数据库的表,创建后makemigration ---> migrate生成表在数据库中
from django.db import models class Book(models.Model): # 注释这个类后,makemigrations再migrate就会在库中删除这个类对应的表 id = models.AutoField(primary_key=True) book = models.CharField(max_length=32,unique=True) price = models.DecimalField(max_digits=8,decimal_places=2) # 后面一个参数是小数位数 pub_date = models.DateField() publish = models.CharField(max_length=32)
在视图函数中添加一个用于存储数据的函数(前期通过这种方式来演示过程)
from django.shortcuts import render,HttpResponse from app.models import Book # 导入Book类(表) def add(request): return HttpResponse('执行成功')
中心思想
在python中ORM的对应关系有三种: 类(Book) ----------> 表 (table) 类对象(obj_book) ----------> 行(记录) 类属性(obj_book.book) ----------> 表的字段(重点)
# 添加表记录
在views.py文件中的添加数据方式
from django.shortcuts import render,HttpResponse from app.models import Book import datetime def index(request): return render(request,'index.html') def add(request): #方式一:通过save()来保存,这里的obj_book是Book的一个实例化,也就是一条记录 time = datetime.datetime.today() #这里使用datetime结构插入时间,还可以使用引号的字符串插入 obj_book = Book(book='Python',price = 123,pub_date=time,publish='人民出版社') obj_book.save() #方式二:通过create方法 (Book继承的父类Model中的有create方法,其返回值book_obj就是插入book表中的对象(记录) obj_book = Book.objects.create(book='葵花宝典',price=123.4,pub_date="2013-12-12",publish="中国人民音乐出版社") print(obj_book.book) #这里create已经存储好我们的对象了,并且返回这个对象(记录)给我们,我们来打印下名字(对象的属性等于表的字段) -------------------------------------------------------------------- #我们甚至可以使用打散的字典来输入,相应的字段属性要使用引号 dic2={'book':'linux','price':125,'pub_date':'2012-12-14','publish':'老男孩出版社'} obj_book1 = Book.objects.create(**dic2) print(obj_book1.book) return HttpResponse('执行成功') # 这个Book.objects就像是一个Book表的管理器一样,提供了增删改查所有的方法(其实是继承的Model) #objects:表示管理器。 #Book.objects:表示管理book表。 #book.objects.create:表示增加操作。 #如果最终在创建新对象的时候比较麻烦,因为你的每条记录都要给他传参: 1、先使用name = request.POST.get('name')…… 2、然后将每个name、属性赋值到Book类中实例化,属性一多就很难麻烦 3、所以我们可以使用字典的打散传参:dic=request.POST.dict(), 需要注意的是里面有一个crsf的键值对,需要删除:del dic['crf……'] ,然后Book.objects.create(**dic)
# 批量插入(bulk_create)
def add(request): # 使用 bulk_create() 来批量插入 book_list = [] for i in range(10): bk_obj = Book( book='Python%s' % i, price=123 + i, pub_date="2013-12-12", publish="人民出版社" ) book_list.append(bk_obj) Book.objects.bulk_create(book_list) # bulk_create() 对一个列表中的对象执行save() return HttpResponse('执行完成') #上面的方法太麻烦了,我们可以使用打散的方式,来传参(下面有讲)
# 有则更新,无则添加(update_or_create)
def add(request): # 需要中注意的是,update_or_create使用get方法,只不过在没有找到时做了异常处理(get不允许找不到) # 且在筛选条件能筛选出多条时,也会报错(没有作异常处理:get() returned more than one Book -- it returned 3 重复3次) # 所以要尽量筛选唯一的主键 obj,created = Book.objects.update_or_create( book = 'python15', # 筛选条件,有没有,没有执行下面的,有就更新, 这里筛选price=123,会筛选多条而出错 defaults={ 'book':'Python15', 'price':123, 'pub_date':'2014-12-14', 'publish':'北京出版社' } ) print(obj,created) # Book object True True表示没有查询到,重新创建的 return HttpResponse('执行完成')
# 注意事项
#当我们忘了给一个默认不能为空的字段,插入数据时,可能会报错:"Column 'price' cannot be null",我们能可以在models.py中重新修改Book类,比如:price设置为默认可为空,且添加一个新字段: class Book(models.Model): # 注释这个类后,makemigrations再migrate就会在库中删除这个类对应的表 id = models.AutoField(primary_key=True) book = models.CharField(max_length=32,unique=True) price = models.DecimalField(max_digits=8,decimal_places=2,null=True) # 后面一个参数是小数位数 pub_date = models.DateField() publish = models.CharField(max_length=32) is_pub = models.BooleanField()
会出现下面情况:(因为在类中is_pub中没有设置初始值。)
实例:实现表单登陆,登陆成功跳转到一个网址
#views.py文件中 from django.shortcuts import render,HttpResponse,redirect from app1.models import User def index(request): if request.method == 'GET': return render(request,'index2.html') elif request.method == 'POST': ret = User.objects.filter(name=request.POST['username'],password=request.POST['password']) if ret: return redirect('https://www.baidu.com/') else: return HttpResponse('Failed') --------------------------------------------------------------- #models.py文件 from django.db import models class User(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) password = models.CharField(max_length=12)
# 查询表记录
还记得表类.objects像是一个管理器,提供了增删改查的方法,Book.objects.all()获取所有的书籍,查询这里大家就掌握谁调用的下面的方法。
<1> all(): 查询所有结果,结果是queryset类型 <2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象,结果也是queryset类型 Book.objects.filter(title='linux',price=100) #里面的多个条件用逗号分开,并且这几个条件必须都成 立,是and的关系,or关系的我们后面再学,直接在这里写是搞不定or的 <3> get(**kwargs): 返回与所给筛选条件相匹配的对象,不是queryset类型,是行记录对象,返回结果有且只有一个, 如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。 Book.objects.get(id=1) <4> exclude(**kwargs): 排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作昂,用这个exclude,返回值是queryset 类型 Book.objects.exclude(id=6),返回id不等于6的所有的对象,或者在queryset基础上调用, Book.objects.all().exclude(id=6) <5> order_by(*field): queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型 models.Book.objects.all().order_by('price','id') #直接写price,默认是按照price升序排列,按 照字段降序排列,就写个负号就行了order_by('-price'),order_by('price','id')是多条件排序,按照 price进行升序,price相同的数据,按照id进行升序 <6> reverse(): queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型 <7> count(): queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量。 <8> first(): queryset类型的数据来调用,返回第一条记录 Book.objects.all()[0] = Book.objects.all().first(),得到的都是model对象,不是queryset <9> last(): queryset类型的数据来调用,返回最后一条记录 <10> exists(): queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False 空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的 数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits 例:all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS `a` FROM `app01_book` LIMIT 1,就是通过limit 1,取一条来看看是不是有数据 <11> values(*field): 用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不 是一系列 model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类 型的其他的查找方法,其他方法也是一样的。 <12> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 <13> distinct(): values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪
# all() 查询所有结果
它返回的是QuerySet数据类型对象,它是django ROM特有的数据类型。 数据格式为:[model对象1,model对象2,model对象3 ...] ;它和下面的 animal_list类似。先是一个列表,列表的元素是一个对象。 一个对象,就是一条记录。有多少条记录,就有多少个对象。那么Queryset就是对象(记录)的集合体,我们可以通过下标访问每个对象(记录)
实例
#查询环节的配置,url.py中新添加一个路径 urlpatterns=[ url('^$',views.index), url('^query/$',views.query) ] ---------------------------------------------------------- 修改views.py文件 def query(request): ret = Book.objects.all() #可进行切片 print(ret) #<QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>,……] print(ret[0].book) # Python0 return HttpResponse('执行完成')
格式化输出:上面的放回的QuerySet太长了,因为对象没有设置__str__函数,当对象被打印时显示的是自己的名字(没人会叫你身份证的号码)
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name
dog = Animal("旺财", 3)
print(dog) #旺财
#修改models.py
class Book(models.Model): # 注释这个类后,makemigrations再migrate就会在库中删除这个类对应的表
id = models.AutoField(primary_key=True)
book = models.CharField(max_length=32,unique=True)
price = models.DecimalField(max_digits=8,decimal_places=2,null=True) # 后面一个参数是小数位数
pub_date = models.DateField()
publish = models.CharField(max_length=32)
is_pub = models.BooleanField(default=False)
def __str__(self):
return self.book
#再次执行query函数
def query(request):
ret = Book.objects.all()
print(ret) #<QuerySet [<Book: Python0>, <Book: Python1>, <Book: Python2>,……]
print(ret[0].book) #Python0
return HttpResponse('执行完成')
#需要注意的是:QuerySet其实就是ORM对表对象的一个存储的数据类型,里面装得就是model对象,只有model对象才能调用属性,QuerySet只能对model对象进行筛选,组织等,也就是说QuerySet通过索引可以讲里面的元素转化为model对象。QuerySet不能直接调用属性,即使它只有一个对象。
# filter(**kwargs): 它包含了与所给筛选条件相匹配的对象(可多条件筛选)
def query(request): # ret = Book.objects.filter(publish="北京出版社").filter(book='python15') #等价下面的 ret = Book.objects.filter(publish="北京出版社",book='python15') #多条件是and关系,全对才对 print(ret) #<QuerySet [<Book: Python15>]> print(ret[0].book) return HttpResponse('执行完成') #fliter 之前本身只能实现and条件判断,即全对才行,下面我们能将然他实现or条件判断:需要导入 from django.db.models import Q def search(request,msg): # 实现搜索出版社或则书名的其中一种 if request.method == "GET": flag = models.Book.objects.filter(Q(title__contains=msg)|Q(publish=msg)).exists() if flag: ret = models.Book.objects.filter(Q(title__contains=msg)|Q(publish=msg)) return render(request,'index.html',{'book_list':ret}) else: hint = '<script> alert("没有相应结果,请输入书名搜索");window.location.href="/app/"</script>' return HttpResponse(hint)
# first(): 返回第一条记录
#first用来取一条记录,如果返回结果有多条,只会取第一条。它的返回结果是model对象。 #last同样用法:用来取最后一条记录,如果返回结果有多条,只会取最后一条。它的返回结果是model对象 def query(request): ret = Book.objects.filter(publish="北京出版社").filter() print(ret) #Python%s15 return HttpResponse('执行完成')
# get(**kwargs): 返回与所给筛选条件相匹配的对象
#返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。(get取值只能是唯一的) #多了会报错: get() return more than one book; 没有:book matching query does not exist def query(request): ret = Book.objects.get(id=3) print(ret) #Python2 return HttpResponse('执行完成') #使用get有且只有一个结果时才有意义。推荐使用get时,利用主键查询,因为主键是唯一的
# exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象(排除)
def query(request): ret = Book.objects.exclude(publish='人民出版社') #排除在这个出版社的其他 print(ret) #<QuerySet [<Book: Python%s15>, <Book: Python15>]> return HttpResponse('执行完成'
# order_by(*field): 对查询结果排序
def query(request): ret = Book.objects.order_by("price","-id","-pub_date") print(ret) # 先按照price升序,再按照id、时间降序 return HttpResponse('执行完成')
# reverse(): 对查询结果反向排序
def query(request): obj = Book.objects.all().order_by("price").reverse() #先升序,再反序 print(obj) return HttpResponse("查询成功")
# count(): 返回数据库中匹配查询的对象数量。
#它的返回结果数据类型是int,也就是说他返回的不再是一个QuerySet类型的结果,所以不能继续链式操作 def query(request): ret = Book.objects.order_by("price","-id","-pub_date").count() print(ret) # 12 return HttpResponse('执行完成') #链式操作:因为queryset可以调用API接口(各种函数),只要前一个接口的返回值是queryset,它可以可以一直调用API接口,除非遇到返回值不是queryset的情况下,链式操作,才可以终止。因为count的返回值是int,所以到这里,就结束了!不能再调用API接口了!
# exists(): 如果QuerySet包含数据,就返回True,否则返回False
def query(request): ret = Book.objects.all().exists() #这样之判断有无,也就避免了大数据被传入打印 if ret: print('ok') return HttpResponse("查询成功")
# values(*field): 返回一个ValueQuerySet
#它是一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列。它的返回结果是一个特殊的QuerySet,列表每一个元素都是字典。也就是循环QuerySet中的每一个model对象,将values中的值为键,去每个对象中取值,最后做成一个字典添加到QuerySet列表中 def query(request): ret = Book.objects.all().values("book","price") print(ret) #<QuerySet [{'book': 'Python0', 'price': Decimal('123.00')}, {'book': 'Python1', 'price': Decimal('124.00')},……] return HttpResponse('执行完成')
# values_list(*field): values返回的是一个元组序列
#通过组织等的值,在每个对象中找,返回一个元组 def query(request): ret = Book.objects.all().values_list("book","price") print(ret) #<QuerySet [('Python0', Decimal('123.00')), ('Python1', Decimal('124.00')), ……] return HttpResponse('执行完成')
# distinct(): 从返回结果中剔除重复纪录(针对values、values_list的返回对象QuerySet类型)
#它的返回结果是QuerySet,注意:是一个常规的QuerySet。如果使用distinct对主键做去重,是没有意义的。因为主键是唯一的! def query(request): # 对价格进行去重 ret = Book.objects.all().values("price").distinct() print(ret) #<QuerySet [{'price': Decimal('123.00')}, {'price': Decimal('124.00')}, {'price': Decimal('126.00')}, ……] return HttpResponse('执行完成') #查看所有书籍的价格以及出版社,价格和出版社同时不能重复 def query(request): ret = Book.objects.all().values("price","publish").distinct() print(ret) return HttpResponse("查询成功") #总结 # all_books = models.Book.objects.all().distinct() #这样写是表示记录中所有的字段重复才叫重复,但是我们知道有主键的存在,所以不可能所有字段数据都重复(无意义,不可能所有有重复) # all_books = models.Book.objects.all().distinct('price') #报错,不能在distinct里面加字段名称 # all_books = models.Book.objects.all().values('price').distinct()#<QuerySet [(Decimal('11.00'),), (Decimal('111.00'),), (Decimal('120.00'),), (Decimal('11111.00'),)]> all_books = models.Book.objects.all().values_list('price').distinct()#<QuerySet [{'price': Decimal('11.00')}, {'price': Decimal('111.00')}, {'price': Decimal('120.00')}, {'price': Decimal('11111.00')}]> 只能用于valuse和values_list进行去重 all_books = models.Book.objects.all().values_list('title','price').distinct() #title和price两个同时重复才算一条重复的记录
# 基于双下划线的模糊查询
#其实在底层转换为like的sql语句
Book.objects.filter(price__in=[100,200,300]) #price值等于这三个里面的任意一个的对象
Book.objects.filter(price__gt=100) #大于,大于等于是price__gte=100,别写price>100,这种参数不支持
Book.objects.filter(price__lt=100)
Book.objects.filter(price__range=[100,200]) #sql的between and,大于等于100,小于等于200
Book.objects.filter(title__contains="python") #title值中包含python的
Book.objects.filter(title__icontains="python") #不区分大小写
Book.objects.filter(title__startswith="py") #以什么开头,istartswith 不区分大小写
Book.objects.filter(pub_date__year=2012) #查询某一年的
def query(request):
ret = Book.objects.filter(book__contains='Py').count() #含有py的
ret1 = Book.objects.filter(price__gte=130).count() #大于130的个数
print(ret,ret1) #12 3
return HttpResponse('执行完成')
# 字段名__startswith(以什么开头的)
def query(request): ret = Book.objects.filter(title__startswith="北京") print(ret) return HttpResponse("查询成功")
# 字段名__gte(大于等于)(同样适用于小于等于)
def query(request): ret = Book.objects.filter(price__lte=100) print(ret) return HttpResponse("查询成功")
# 删除表记录
删除方法就是 delete()。它运行时立即删除对象而不返回任何值。例如:
model_obj.delete() #delete()方法的调用者可以是一个model对象,也可以是一个queryset集合。 #你也可以一次性删除多个对象。每个 QuerySet 都有一个 delete() 方法,它一次性删除 QuerySet 中所有的对象。 #例如,下面的代码将删除 pub_date 是2005年的 Entry 对象: Book.objects.filter(pub_date__year=2005).delete() #等学到外键的时候再说,在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象。例如: b = Blog.objects.get(pk=1) #pk就是主键 primary key b.delete() #要注意的是: delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Book.objects.delete() 方法导致 所有的记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用: Book.objects.all().delete() #修改是基于查询的结果来修改的,所以先查询再修改 #举例:删除价格等于100的 def delbook(request): ret = Book.objects.filter(price=100).delete() print(ret) return HttpResponse("删除成功") #如果不想级联删除,可以设置为: pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)
还可以结合视图函数的参数来进行操作:
url.py文件中 urlpatterns = [ url('delbook/(?P<id>\d+)', views.delbook), ] --------------------------------------------------------------- views.py文件中 def delbook(request,id): ret = Book.objects.filter(nid=id).delete() print(ret) print(ret,type(ret)) if ret[0]: return HttpResponse("删除成功") else: return HttpResponse("删除失败")
# 修改表记录
修改表记录都是基于查询之上
ret = Book.objects.filter(book__startswith="py").update(price=120) #update只能是querset类型才能调用,model对象不能直接调用更新方法,所以使用get方法获取对象的时候是不能update的,ret为一个数字,表示受影响的记录数 def query(request): ret = Book.objects.filter(book__contains='Py').update(price=1222) print(ret) #12 return HttpResponse('执行完成') #此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。 #注意:<input type="date" class="form-control" id="book_pub_date" placeholder="出版日期" name="book_pub_date" value="{{ edit_obj.pub_date|date:'Y-m-d' }}">,type='date'的input标签,value的值必须是'Y-m-d'的格式,这个标签才能认识并被赋值,所以,要通过date过滤给它改变格式。