python---django中orm的使用(1)
首先推荐两篇文章:Django之ORM操作,http://www.cnblogs.com/yuanchenqi/articles/6083427.html十分全面。
另外和python---ORM之SQLAlchemy(1)等前面几篇文章相结合
1.ORM对象关系映射(Object Relational Mapping)
优点:
使用orm,对于数据库迁移十分有用,只需要修改相关引擎即可,
不用考虑是sqlite还是mysql,oracle等,
而且设计好类后,会自动生成相关表,数据。
但是对于一些简单的表可能会稍微有点多余,但是毕竟优点多余这些
orm类大概了解:
类名对应 数据库表名
类成员属性对应 数据库字段
类实例对应 数据库表中的一行数据
注意:使用orm时对应的数据库是在settings文件中红设置的,必须提前建库
create database 库名 default character set utf8 collate utf8_general_ci
相关配置请看python---django中models配置
2.模型常用字段类型介绍
models.CharField #字符串字段,用于可变长字符串(varchar) #要求必须有一个参数maxlength,用于设置其所允许的最大字符串长度 models.IntegerField #用于保存一个整数 choice=( (1,'男'), (2,"女"), (3,"其他"), ) models.IntegerField(choices=choice) # 枚举类型 models.FloatField #浮点数保存,必须提供两个参数,max_digits总位数,decimal_places小数位数 models.AutoField #一个IntegerField,添加记录时,自动增长 #一般主键设置为他,models.AutoField(primary_key=True) #若是不指定主键,系统会自动添加一个主键字段到该表 models.TextField #一个容量很大的文本字段
models.DateField #一个日期字段,可以带以下可选参数 #Argument 描述 #auto_now 当对象被保存时自动将该字段的值设为当前时间,通常表示“last-modified”时间戳,最后一次修改的时间 #auto_new_add 当对象被创建时设置,即首次添加时的创建时间 models.DateTimeField #一个日期时间字段,和DateField类似
以下在数据库中本质都是字符串数据类型,此类字段只是在Django自带的admin中有用
models.EmailField
#检测email合法性的CharField,不需要maxlength
models.FileField
#一个文件上传字段
models.ImageField
#图片上传字段,类似FileField
models.IPAddressField
#ip检验字段
.....
其他字段:
db_index = True 表示设置索引 unique(唯一的意思) = True 设置唯一索引 联合唯一索引 class Meta: unique_together = ( ('email','ctime'), ) 联合索引(不做限制) index_together = ( ('email','ctime'), ) ManyToManyField(RelatedField) #多对多操作
3.字段参数:
null : 为空 default : 设置默认值 primary_key : 主键 若是自己没有设置主键,Django会自动添加上id = meta.AutoField('ID', primary_key=True) unique : 数据唯一 verbose_name : admin中字段的显示名称 db_column, db_index : 如果为真,将为这个字段创建索引 choices : 一个用于选择的二维元组,枚举 .....
4.数据表的增删改查
表创建:
from django.db import models<br> class Publisher(models.Model): name = models.CharField(max_length=30, verbose_name="名称") address = models.CharField("地址", max_length=50) city = models.CharField('城市',max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() class Meta: verbose_name = '出版商' verbose_name_plural = verbose_name def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=30) def __str__(self): return self.name class AuthorDetail(models.Model): sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),)) email = models.EmailField() address = models.CharField(max_length=50) birthday = models.DateField() author = models.OneToOneField(Author) class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2,default=10) def __str__(self): return self.title
from 应用APP.models import *
增(create, save):
方式一: Author.objects.create(name='das') 方法二: Author.objects.create(**{"name":"dsaf"}) 推荐 方法三: author = Author(name="dsa") author.save() 方法四: author = Author() author.name="dsa" author.save()
删delete:
Book.objects.filter(id=1).delete() #先查找到这条数据,再删除
默认是级联删除,含有相关的这条记录的数据,也会被删除
对于外键中的级联删除,我们可以使用on_delete参数进行修改
改update,save:
author = Author.objects.get(id=5) author.name="gae" author.save() Author.objects.filter(id=5).update(name="geag")
update效率高于save,save会重新保存所有字段,update只会修改你修改的相关字段
注意:第二种方式修改不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySe集合对象(filter里面的条件可能有多个条件符合,比如name='alvin',可能有两个name='alvin'的行数据),get返回的是一个记录对象。
查:
filter(**kwargs) all() get(**kwargs)
下面方法都是对查询的结果进行操作
values(*field) #返回字典序列 exclude(**kwargs) #它包含了与所给筛选条件不匹配的对象
order_by(*field)
reverse()
distinct()#剔除重复
values_list(*field) #元组序列
count()
first()
last()
exists() #如果QuerySet包含数据,就返回True,否则返回False。判断对象是否存在数据,不会去取出数据(关于惰性机制)
更多方法查看:http://www.cnblogs.com/yuanchenqi/articles/6083427.html
queryset惰性机制了解:
惰性机制:当使用filter()或者all()等都是返回一个QuerySet()查询结果集对象,
他不会马上执行SQL语句,只有当调用QuerySet的时候才会被执行
可以查看SQL语句来进行分析:可以使用queryset.query() { queryset是获取的queryset结果集 }获取该对象这一条数据的SQL语句
queryset = MyModel.objects.all() print 'query sql: ' + str(queryset .query)
也可以通过日志来查看,需要先配置settings文件
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
class Handler(Filterer) #基类 有下面两种方式: class StreamHandler(Handler) #流输出 class FileHandler(StreamHandler) #文件形式
然后可以开始测试:
from blog import models def data_oper(req): obj_set = models.Book.objects.filter(id=2) #获取queryset结果集 return HttpResponse("<h1>ok</h1>")
执行后发现,并没有SQL日志产生,所有SQL语句没有执行,只有当使用queryset集合是才会开始执行。
def data_oper(req): obj_set = models.Book.objects.filter(id=2) #获取queryset结果集 for obj in obj_set : print(obj.title) return HttpResponse("<h1>ok</h1>")
此时会发现执行了查询数据的SQL语句。
缺点分析:
def data_oper(req): obj_set = models.Book.objects.filter(id=2) if obj_set: #if 逻辑判断只需要知道其中是否包含数据,需要快速执行,但是在这里会去执行SQL语句,将逻辑语句效率降低 print("ok") return HttpResponse("<h1>ok</h1>")
#高效判断,可以使用exists方法
#取数据时,也可以不用一次性取完,可以使用迭代器,一条一条数据取出
obj_set.iterator()
缓存:
def data_oper(req): obj_set = models.Book.objects.filter(id=2) #获取queryset结果集 for obj in obj_set : #去数据库中取数据,第一次执行后会将queryset数据存放在缓存中,后面获取数据时,则不需要在进行SQL语句 print(obj.title)
#若是在这里修改 #obj_set.update(title="dsaf") #会修改数据库数据,也会修改缓存数据
for obj in obj_set : #不需要在进行SQL语句,缓存中取数据 print(obj.title) return HttpResponse("<h1>ok</h1>")
补充:QuerySet
QuerySet特点:可迭代,可切片
objs=models.Book.objects.all()#[obj1,obj2,ob3...] #QuerySet: 可迭代 for obj in objs:#每一obj就是一个行对象 或者:obj_set.iterator()获取数据,可用while
print("obj:",obj)
# QuerySet: 可切片 # print(objs[1]) # print(objs[1:4]) # print(objs[::-1])
高效使用:
<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,可能 会造成额外的数据库查询。
5.一对多,多对多
一对多,外键foreign_key
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2,default=10) def __str__(self): return self.title
class Publisher(models.Model): name = models.CharField(max_length=30, verbose_name="名称") address = models.CharField("地址", max_length=50) city = models.CharField('城市',max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() class Meta: verbose_name = '出版商' verbose_name_plural = verbose_name def __str__(self): return self.name
增加:
一对多:
方法一:直接添加外键值 Book.objects.create(title='php', publisher_id=2, #这里的2是指为该book对象绑定了Publisher表中id=2的行对象 publication_date='2017-7-7', price=99) 注意: publisher = models.ForeignKey(Publisher) 外键就是在foreign基础上,加上一个_id 例如:publisher外键,在数据库存在为publisher_id,所以调用时也应该使用publisher_id 方法二:直接添加对象,则不需要管外键_id了 对象创建或查找: pub_obj=Publisher(name='河大出版社',address='保定',city='保定',state_province='河北',country='China',website='http://www.hbu.com') #直接新建 或者 pub_obj=Publisher.objects.get(id=1) #查找 添加加入:将 publisher_id=2 改为 publisher=pub_obj Book.objects.create(title='php', publisher=pub_obj, publication_date='2017-7-7', price=99)
补充:连续跨多张表:
class School(models.Model):
title = models.CharField(max_length=32)
class Classes(models.Model):
sc = models.ForeignKey(School)
caption = models.CharField(max_length=32)
def __str__(self):
return self.caption
class Student(models.Model):
username = models.CharField(max_length=32)
age = models.IntegerField()
gender = models.BooleanField()
cls = models.ForeignKey("Classes")
#跨多表查询
#跨一表查询班级信息
stu_list = Student.objects.all().values('username',"cls__caption")
#跨两张表查询学校
stu_list = Student.objects.all().values('username',"cls__caption","cls__sc__title")
补充:related_name:相当于在Classes表中加入了一个ccc字段,是指向Student表,反向查询(有这个就可以直接使用这个字段进行反向查询,不需要字段_set来进行操作)
class Student(models.Model):
username = models.CharField(max_length=32)
age = models.IntegerField()
gender = models.BooleanField()
cls = models.ForeignKey("Classes",related_name="ccc")
obj = Classes.objects.filter(caption="python").first()
Stu_set = obj.ccc.all() #关于python班级下的所有学生的query_set集合
就是tornado中的relationship中的backref参数python---ORM之SQLAlchemy(3)外键与relationship的关系
注:尽量使用正向查询
补充:一对多---->一对一 对应关系
只需要在外键中设置为unique=true
一对一:反向查找只需要在后面加上反向的表名即可
一对多:反向查找需要在后面跟上反向的小写表名+_set
多对多:像书籍和作者
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2,default=10) def __str__(self): return self.title
class Author(models.Model): name = models.CharField(max_length=30) def __str__(self): return self.name
这个多对多键也可以设在Author中,最好应该是在数据较多的一侧,去查找少的
多对多一般会创建第三张表来保存数据,第三张表是通过models.ManyToManyField()自动创建,默认是 应用APP_表一名_表二名,会自动生成主键,将两张表的主键作为记录,字段名为:表名_id,并且是联合唯一。
但是:
对于Django自动创建的这张表,我们无法自己直接使用,使用方法如下:add remove
因为author字段放在book表中,所以下面是正向查询
author1=Author.objects.get(id=1) author2=Author.objects.filter(name='alvin')[0] book=Book.objects.get(id=1) book.authors.add(author1,author2)
#上面可以写为
book.authors.add(*[author1,author2])
对应删除操作:
book.authors.remove(*[author1,author2])
或者:
author表中并没有book字段,所以为反向查询 ---》 反向查询直接找author中book对象是找不到的,
需要使用_set才可以找到
book=models.Book.objects.filter(id__gt=1) authors=models.Author.objects.filter(id=1)[0] authors.book_set.add(*book)
对应删除操作:
authors.book_set.remove(*book)
或者:
book.authors.add(1) authors.book_set.add(1)
对应删除操作:
book.authors.remove(1)
authors.book_set.remove(1)
查询方法:
# Create your models here. class Classes(models.Model): caption = models.CharField(max_length=32) class Teacher(models.Model): name = models.CharField(max_length=32) cls = models.ManyToManyField('Classes',related_name="ssss") Teacher.objects.all().values('id','name','cls','cls__caption') #默认直接访问cls是获取其id,使用__连表获取信息
Classes.objects.all().values('id','caption','ssss','ssss_name')
要想直接使用第三张表进行操作,需要我们自己创建该表(使用方便)
当不使用ManyToManyField自动创建表时,自己手动创建第三张表:
from django.db import models
class Book2Author(models.Model) book=models.ForeignKey("Book") author=models.ForeignKey("Author") #最好使用引号,若是想不加引号,那么必须将那个类放在前面
#联合唯一
class Meta: #在元类中添加联合唯一,建联合唯一要根据需求来看
unique_together = ['book','author']
此时可以直接使用第三张表去添加数据:
from 应用APP import models
models.Book2Author.objects.create( book_id=2, author_id=3 )
注:上面两类models是不一样的
补充:正向操作也可以操作关联表(咂filter字段操作时)
class Role(models.Model): caption = models.CharField(max_length=32) class Meta: verbose_name_plural = "角色表" def __str__(self): return self.caption class User2Role(models.Model): u = models.ForeignKey("User") r = models.ForeignKey("Role") class Meta: verbose_name_plural = "用户角色分配表" def __str__(self): return "%s:%s"%(self.u.username,self.r.caption)
class Permission2Action2Role(models.Model):
p2a = models.ForeignKey("Permission2Action")
r = models.ForeignKey("Role")
class Meta:
verbose_name_plural = "角色权限分配表"
def __str__(self):
return "%s:%s"%(self.r.caption,self.p2a)
使用时:
role_list = models.Role.objects.filter(user2role__u__id=id).all() user = models.User.objects.filter(id=int(id)).get() role_list2 = models.Role.objects.filter(user2role__u = user)
测试可用字段:
role_list2 = models.Role.objects.filter(aaa = user) #aaa不存在,错误提示中会显示我们所能调用的字段 django.core.exceptions.FieldError: Cannot resolve keyword 'aaa' into field.
Choices are: caption, id, permission2action2role, user2role
filter使用时可以正向操作字段,对象使用时需要加上_set