python:Django Models / ORM
1 django默认支持sqlite,mysql, oracle,postgresql数据库。
<1> sqlite
django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3
<2> mysql
引擎名称:django.db.backends.mysql
2 mysql驱动程序
- MySQLdb(mysql python)# python2使用。
- mysqlclient
- MySQL
- PyMySQL(纯python的mysql驱动程序)# python3使用
3、Django的数据库设置【setings.py】
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'books', #你的数据库名称 'USER': 'root', #你的数据库用户名 'PASSWORD': '', #你的数据库密码 'HOST': '', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
在此之前,需要在项目名下的__init__.py文件中 写入
import pymysql pymysql.install_as_MySQLdb()
在setings设置完数据库模式为mysql之后,需要导入驱动,因为Django默认导入的驱动是mysqldb,所以我们需要更改驱动的设置。
下面要开始学习Django ORM语法了,为了更好的理解,我们来做一个基本的 书籍/作者/出版商 数据库结构。 我们这样做是因为 这是一个众所周知的例子,很多SQL有关的书籍也常用这个举例。
表(模型)的创建:
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名。
作者详细模型:把作者的详情放到详情表,包含性别,email地址和出生日期,作者详情模型和作者模型之间是一对一的关系(one-to-one)(类似于每个人和他的身份证之间的关系),在大多数情况下我们没有必要将他们拆分成两张表,这里只是引出一对一的概念。
出版商模型:出版商有名称,地址,所在城市,省,国家和网站。
书籍模型:书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many),一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many),也被称作外键。
附:数据模型常见的字段类型
参数:
<1> null : 数据库中字段是否可以为空 <2> blank: django的 Admin 中添加数据时是否可允许空值 <3> default:设定缺省值 <4> editable:如果为假,admin模式下将不能改写。缺省为真 <5> primary_key:设置主键,如果没有设置django创建表时会自动加上: id = meta.AutoField('ID', primary_key=True) primary_key=True implies blank=False, null=False and unique=True. Only one primary key is allowed on an object. <6> unique:数据唯一 <7> verbose_name Admin中字段的显示名称 <8> validator_list:有效性检查。非有效产生 django.core.validators.ValidationError 错误 <9> db_column,db_index 如果为真将为此字段创建索引 <10>choices:一个用来选择值的2维元组。第一个值是实际存储的值,第二个用来方便进行选择。 如SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),) gender = models.CharField(max_length=2,choices = SEX_CHOICES)
再进行ORM查询数据时,会经常获得一个QuerySet集合对象。
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。
QuerySet特点:
<1> 可迭代的#可以用for循环
<2> 可切片#【0,1,2,3,4】#取几个对象
常用的API方法
# 查询相关API: # <1>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 # <2>all(): 查询所有结果 # <3>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 #-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()-------- # <4>values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 # <5>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 # <6>order_by(*field): 对查询结果排序 # <7>reverse(): 对查询结果反向排序 # <8>distinct(): 从返回结果中剔除重复纪录 # <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 # <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 # <11>first(): 返回第一条记录 # <12>last(): 返回最后一条记录 # <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False。
<1>Django的queryset是惰性的 Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得 到数据库中名字为‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave") 上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数, 这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。 <2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql. 为了验证这些,需要在settings里加入 LOGGING(验证方式) obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) # if obj: # print("ok") <3>queryset是具有cache的 当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行 (evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset, 你不需要重复运行通用的查询。 obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) ## models.Book.objects.filter(id=3).update(title="GO") ## obj_new=models.Book.objects.filter(id=3) # for i in obj: # print(i) #LOGGING只会打印一次 <4> 简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些 数据!为了避免这个,可以用exists()方法来检查是否有数据: obj = Book.objects.filter(id=4) # exists()的检查可以避免数据放入queryset的cache。 if obj.exists(): print("hello world!") <5>当queryset非常巨大时,cache会成为问题 处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法 来获取数据,处理完数据就将其丢弃。 objs = Book.objects.all().iterator() # iterator()可以一次只从数据库获取少量数据,这样可以节省内存 for obj in objs: print(obj.name) #BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了 for obj in objs: print(obj.name) #当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使 #用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询 总结: queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。 使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能 会造成额外的数据库查询。
一,单表操作
1、在models.py中,通过类来设置表头。
from django.db import models#导入模块 from django.utils import timezone from django.contrib.auth.models import User # Create your models here. # class Book_info(models.Model):#图书表#继承 父类 book_name = models.CharField(max_length=300)#书名 t_publication = models.DateField()#出版时间 t_entering = models.IntegerField#录入时间 book_type = models.IntegerField(max_length=300)#图书类型 b_price = models.FloatField()#价格 Write_info = models.FloatField() # 作者
2、运行下面命令,来进行数据表的创建
创建表
python manage.py makemigrations
python manage.py migrate
3、添加数据
a: 创建记录 :通过views class类实例化数据库添加数据
import Tbook.models# 引入刚才建立的那张表 def add_book(requset):#添加 ad = models.Book_type(book_type="财经")#实例化这张表 ad.save()#保存 return HttpResponse("添加成功") #我在路由中添加了 add_book,views.book. #运行Django服务器,输入,127.0.0.1/add_book.
b:调用object.create方法进行添加记录
import Tbook.models# 引入刚才建立的那张表 def add_book(requset):#添加 models.Book_type.objects.create(book_type="天文") return HttpResponse("添加成功")
4、删除记录
models.Book_type.objects.filter(book_type="水利").delete()
5、查询,并修改数据
def find_book(requset):#查找 及修改 models.Book_type.objects.filter(book_type="言情").update(book_type = "建筑") #filter查询,upadte,更新数据 return HttpResponse("查询成功")
def find_book(requset):#查找 及修改 upd = models.Book_type.objects.get(id = 2)#取一个。 print(upd)#QuerySet 对象合计 upd.book_type = "水利" upd.save() #利用update,少用save,因为,save这个方法会修改整条数据。而,objects只修改指定数据
6.怎么查看你用用的这些命令,原生SQL是怎么执行的?
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
常用的查询命令:
# 查询相关API: # <1>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象f # <2>all(): 查询所有结果 object.all()[0:2] 可以进行切片 # <3>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 #-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()-------- # <4>values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 #取出一个特定的值,filter(字段==)。values(字段,字段) # <5>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 # <6>order_by(*field): 对查询结果排序 # <7>reverse(): 对查询结果反向排序 # <8>distinct(): 从返回结果中剔除重复纪录 对象.object.all().values("name").distinct() # <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 # <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 # <11>first(): 返回第一条记录 # <12>last(): 返回最后一条记录 # <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False。
#---------------了不起的双下划线(__)之单表条件查询---------------- # 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") # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 # # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # # startswith,istartswith, endswith, iendswith,
二、一对多。
例如:有2张表,一张书籍表,一张出版社表,一个出版社可以出多本书,一本书一般情况下是一个出版社出的。所以是1对多的关系。在models中新建表关系时,要在多的那一方新建外键。具体如下:
class Book_info(models.Model):#图书表 name = models.CharField(max_length=50)#书名 Write = models.CharField(max_length=100)# 作者 price = models.IntegerField()#价格 publish = models.ForeignKey("Publish_h")#默认关键主键 class Publish_h(models.Model):#出版社表 name = models.CharField(max_length=300)#书名 city = models.CharField(max_length=32) #新建完了之后,他会在BOOK表中 新建一列,列名是:Pulish_h_id 也就是出版社的表名+ID
查询数据
#查询python这本书籍的出版社 #filter# obj = Publish_h.objects.filter(book_info__name="linux").values('name') print(obj) #values# obj = Book_info.objects.filter(name = "linux").values("publish__name")
#查询城市是上海的出版社出版的书 obj = Publish_h.objects.filter(city = "上海").values("book_info__name") obj = Book_info.objects.filter(publish__city='上海').values("name")
增加数据
#方式1 add_list = Book_info.objects.create(name="色即是空", t_entering="2019-08-09", t_publication="2008-08-06", type="历史", price=88, Write="王龙", publish_id=2) #方式2 # pubobj = Publish_h.objects.filter(name="人民出版社")[0] # Book_info.objects.create(name="色即是空", t_entering="2019-08-09", t_publication="2008-08-06", # type="色情", price=88, Write="王龙", publish_id=pubobj)
二、多对多
创建多对多的表# 一个作者个出版多本书,一本书可以有多个作者。
创建了多对多关系之后,Django会新建一张新表来保存这个关系(book_info__Writer_b)这张表中存放了主键ID,book_info的外键id与Writer_b的外键id
人工用manytomany的方式创建了第三张表,就不能通过ORM直接添加数据。
Write = models.ManyToManyField("Writer_b") # 作者#多对多
1、添加关系,删除关系
#绑定多对多的关系 #给ID等于4的书,绑定3个作者 book_obj = Book_info.objects.get(id = 4) Writer_obj = Writer_b.objects.all()#所有的作者信息 Writer_obj = Writer_b.objects.get(id = 2) # id等于2的作者 book_obj.Write.add(*Writer_obj)#添加关系 book_obj.Write.remove(Writer_obj)#删除关系 book_obj.Write.remove(1) # 删除关系表中ID = 1 的
备注更新数据,
obj = models.book.objcts.filter(id=2).first()
obj.wirte.set([1,2])1 2 2 2 更新
2、如果想直接在第三张表里面添加数据,那就不能用manytomany,需要自己新建一张表。
class Book_write(models.Model): book = models.ForeignKey('Book_info',on_delete=models.CASCADE) write = models.ForeignKey('Writer_b',on_delete=models.CASCADE) 外键模式,必须创建on_delete字段 CASCADE:这就是默认的选项,级联删除,你无需显性指定它。 PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出ProtectedError错误。 SET_NULL: 置空模式,删除的时候,外键字段被设置为空,前提就是blank=True, null=True,定义该字段的时候,允许为空。 SET_DEFAULT: 置默认值,删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。 SET(): 自定义一个值,该值当然只能是对应的实体了
3、自定义创建第三张表查询/manytomany
#manytomany print(Book_info.objects.filter(Write__Write='耳根').values('name'))
4、聚合函数查询
from django.db.models import Avg,Min,Sum,Max,Count #单表聚合查询 #查询书籍表price的平均值 Book_info.objects.all().aggregate(Avg("price")) Book_info.objects.all().aggregate(Sum("price")) #多表聚合 print(Book_info.objects.filter(Write__Write="耳根").aggregate(Sum("price"))) # 默认是字段名是 sum_price Sum("price"). #改名: sjy_mone=Sum("price").
5、分组查询
# 查询每个作者出的书,他们各一共有多少钱 Book_info.objects.values('write__name').annotate(Sum("price")) #按照作者来分组(BOOK表中的write字段双下划线连接至Write表下的name 字段_以作者分组),然后再求和 # 每个出版出版社出版的书最便宜的价格是多少 1、 Book_info.objects.values("publish__name").annotate(Min("price"))
6、模糊查询
F----Q查询
#F 使用查询条件的值,专门取对象中某列值的操作 #每本书 提高10元 # Book_info.objects.all().update(price=F('price')+10) #update修改表 F找到 字段并取值 a = Book_info.objects.filter(Q(price=53)).values('name') print(a)#查询价格等于53的书籍 b = Book_info.objects.filter(~Q(price=53)).values('name') print(b)#查询价格不等于53的书籍 c = Book_info.objects.filter(Q(price=53) | Q(type='历史')).values('name') print(c)#查询 等于 53或者 等于历史的书籍 #如果要查询 并关系的话, filter(name = **,type =1111),就是并的关系 #Q查询和关键字查询组合使用 ret =Book_info.objects.filter(Q(name='仙逆'),price=55)