python---django中orm的使用(1)

it2022-05-05  131

首先推荐两篇文章: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 View Code

 

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', }, } } settings.py中SQL日志配置 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 Book 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 Publisher

增加:

一对多:

方法一:直接添加外键值 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 modelsmodels.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

转载于:https://www.cnblogs.com/ssyfj/p/8647352.html


最新回复(0)