ORM —— 关系对象映射,是Object Relational Mapping的简写,是用来简化数据库操做的框架
Django ORM遵循Code Frist原则,即根据代码中定义的类来自动生成数据库表,对于ORM框架:
(1)自定义的类表示待建立数据库的表
(2)根据自定义类建立的对象obj表示数据库表中的一行数据
(3)obj.字段一、obj.字段二、...、obj.字段n表示每一行数据中相应字段的值
ORM中的一对多、一对一以及多对多的概念及应用场景,具体参见前期博客。
ORM中的正向和反向操做,这是相对而言的,主要取决于一对多/一对一/多对多字段写在哪一个类中。html
class UserType(models.Model): caption = models.CharField(max_length=32) class UserInfo(models.Model): user_type = models.ForeignKey('UserType') username = models.CharField(max_length=32) age = models.IntegerField()
对于上面两张表,UserInfo表是关联UserType表的,即一对多字段(外键)在UserInfo表中。那么根据UserInfo表去操做数据库,就表示正向;反之根据UserType表去操做数据库,就表示反向。数据库
models.py中的表结构django
class UserType(models.Model): caption = models.CharField(max_length=32) class UserInfo(models.Model): # 此处user_type是models.ForeignKey类封装了UserType类生成的的对象
# 即user_type是包含了id和caption字段的对象
user_type = models.ForeignKey(UserType) username = models.CharField(max_length=32) age = models.IntegerField()
views.py中生成三种用户类型的代码app
def user_type(request): # 添加user_type表的数据
# dic = {'caption': 'CEO'} 对应id为1
# dic = {'caption': 'CTO'} 对应id为2
# dic = {'caption': 'COO'} 对应id为3
# models.UserType.objects.create(**dic)
print models.UserType.objects.all() return HttpResponse('ok')
生成的表结构以下图所示:框架
1 UserInfo表添加数据的两种方法性能
(1)数据库级别优化
上表是UserInfo数据库表结构,外键user_type字段默认会加上"_id"进行存储,那么从数据库级别添加数据的代码以下:spa
def user_info(request): # 一对多:外键
# 一对多中添加user_info表数据:数据库级别
# 第1种方法(数据库级别):按外键在数据库中的存储方式
dic = {'username':'xx','age':18,'user_type_id':1} models.UserInfo.objects.create(**dic) result = models.UserInfo.objects.all() for item in result: print item.username,item.age,item.user_type.caption
(2)对象级别code
def user_info(request): # 一对多:外键
# 一对多中添加user_info表数据:对象级别
# 第2种方法(对象级别):UserInfo表中的user_type字段对应models中的UserType对象
#先获取用户类型的对象
type = models.UserType.objects.filter(id=2) #添加时直接添加对象
models.UserInfo.objects.create(username='xxx',age=28,user_type=type) #或写成下面形式,省略中间步骤
dic = {'username':'xxx','age':28,'user_type':models.UserType.objects.get(id=2)} models.UserInfo.objects.create(**dic)
get方法是从数据库中取一个匹配的结果,返回一个对象,若是不存在,则会报错 |
2 查找数据
(1)正向查
def user_info(request): # 查找表中的全部数据
result = models.UserInfo.objects.all() # 查找指定数据
# 正向查找:查询当前表中数据
result = models.UserInfo.objects.filter(username='xx') # 跨表查询(使用双线划线"__"),user_type是UserType类的对象,因此这里是查询UserType表中的caption字段,便是跨表查询
result = models.UserInfo.objects.filter(user_type__caption='CTO') for item in result: # 与跨表查询数据不一样的是,当查询完数据再取数据时,使用的是".",这里要注意
print item.username,item.age,item.user_type.caption return HttpResponse('ok')
(2)反向查
def user_info(request): # 反向查找
# 搜索条件:id/caption/userinfo(该字段是Django在UserType表中自动生成的,关联到UserInfo表,这个字段很重要,为反向查找提供了可能)
line = models.UserType.objects.get(id=1) print line.id #输出"1"
print line.caption #输出"CEO"
#输出"[<UserInfo: UserInfo object>]",表示当前用户类型UserType(id=1)对应的用户对象
''' 注意下面的line.userinfo_set等价于models.UserInfo.objects.filter(user_type=line) 好好理解这种等价关系 '''
print line.userinfo_set.all() print line.userinfo_set.filter(username='xx') for item in line.userinfo_set.all(): # 输出"xx 18 UserType object"
print item.username, item.age, item.user_type # 输出"xx 18 CEO",注意user_type是UserType的对象
print item.username, item.age, item.user_type.caption # 一、查找某我的是哪一种用户类型;
user_type_obj = models.UserType.objects.get(userinfo__username='xx') print user_type_obj.caption
# 二、查找指定用户类型下有哪些用户 # 下面两种方法是等价的,注意在Django中,UserType表默认会增长一列表示与UserInfo表的关系
print user_type_obj.userinfo_set.count() # 下面方法是从UserInfo表出发查找的
print models.UserInfo.objects.filter(user_type=user_type_obj).count() return HttpResponse('ok')
3 新闻点赞实例
models.py中的表结构
#用户表
class MyUser(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=64) def __unicode__(self): return self.username
#新闻表 class New(models.Model): title = models.CharField(max_length=32) content = models.CharField(max_length=32) def __unicode__(self): return self.title
#点赞表,这里须要注意:1 哪一个用户点的赞;2 给哪一个新闻点的赞;3 不能重复点赞class Favor(models.Model): # 外键关联MyUser
user_obj = models.ForeignKey(MyUser) # 外键关联New
new_obj = models.ForeignKey(New) def __unicode__(self): return "%s -> %s" %(self.user_obj.username, self.new_obj.title)
配置admin以及建立admin用户,并建立几篇文章
from django.contrib import admin from app01 import models # Register your models here.
admin.site.register(models.MyUser) admin.site.register(models.New) admin.site.register(models.Favor) admin.site.register(models.HostAdmin) admin.site.register(models.Host)
views.py中的相关代码,主要完成两个功能:1 查询某人点赞过的文章; 2 查询某篇文章被点赞过的次数
# 新闻点赞实例(相关表数据已在Django的admin中添加完成)
def favor_new(request): # 获取全部的新闻列表
# new_list = models.New.objects.all()
# 获取alex点赞过的新闻列表,注意这里的反向查询和跨表查询
new_list = models.New.objects.filter(favor__user_obj__username='alex') for item in new_list: print '====================='
print item.title print item.content # 打印每条新闻的点赞次数
print item.favor_set.all().count() return HttpResponse('ok')
Django中的多对多与一对多没有联系,它的实际操做比一对多简单,Django默认会为多对多生成第3张表。下面是对应数据库表的关系:
models.py中的数据库结构
#============多对多================ #下面创建多对多关系的字段,写了其中任何一个便可,它们的区别只在于哪一个是"正向查找",哪一个是"反向查找",即查找时的参照类有区别。
class Host(models.Model): hostname = models.CharField(max_length=32) port = models.IntegerField() # admin = models.ManyToManyField(HostAdmin)
class HostAdmin(models.Model): username = models.CharField(max_length=32) email = models.CharField(max_length=32) host = models.ManyToManyField(Host)
views.py中添加数据
def many_to_many(request): #建立主机表
models.Host.objects.create(hostname='c1',port=80) models.Host.objects.create(hostname='c2',port=80) models.Host.objects.create(hostname='c3',port=80) #建立用户表,注意这里只须要添加username和email字段,host字段不须要指定
models.HostAdmin.objects.create(username='alex',email='alex@qq.com') models.HostAdmin.objects.create(username='eric',email='eric@qq.com') models.HostAdmin.objects.create(username='pony',email='pony@qq.com') models.HostAdmin.objects.create(username='rain',email='rain@qq.com') return HttpResponse('ok')
生成的三张表以下:
1 正向添加数据(从包含多对多字段的表操做数据库)
def many_to_many(request): # 正向添加
# 目的:给alex分配两个主机的管理权限
# 一、获取指定的HostAdmin对象
admin_obj = models.HostAdmin.objects.get(username='alex') # 二、获取指定的Host,条件为id小于3
host_list = models.Host.objects.filter(id__lt=3) # 三、将用户alex和指定的两个主机添加到对应的第3张表中,注意host_list是列表,全部加*号
admin_obj.host.add(*host_list) return HttpResponse('ok')
Django自动生成的第3张表以下所示:
2 反向添加数据(从不包含多对多字段的表操做数据库)
def many_to_many(request): # 反向添加
# 一、获取主机表
# host_obj = models.Host.objects.get(id=3)
# 二、获取用户表
# admin_list = models.HostAdmin.objects.filter(id__gt=1)
# 三、添加数据,注意hostadmin_set,这是在Host表中,Django自动生成的关联HostAdmin表的字段
# host_obj.hostadmin_set.add(*admin_list)
return HttpResponse('ok')
不管是正向添加仍是反向添加,其本质都是基于主机表或用户表的一行数据(对象)对应另外一张表中的一行或多行数据(对象)。 正向添加,对于ID=2的用户,添加多台主机(好比主机ID为1,2,3,4),那么Django自动生成的第3张表信息以下: # 用户ID 主机ID 2 1 2 2 2 3 2 4 反向添加,对于ID=2的主机,添加多个用户(好比用户ID为1,2,3),那么Django自动生成的第3张表信息以下: # 用户ID 主机ID 1 2 2 2 3 2 |
3 自定义第3张表
对于Django自动生成的第3张表,在使用的过程当中不是很灵活,也不能增长字段。对于这种状况,Django容许自定义生成第3张表,不须要使用默认的表结构。分析下三张表之间的关系:
models.py中自定义第3张表
# 主机表
class Host(models.Model): hostname = models.CharField(max_length=32) port = models.IntegerField() # admin = models.ManyToManyField(HostAdmin)
# 用户表
class HostAdmin(models.Model): username = models.CharField(max_length=32) email = models.CharField(max_length=32) host = models.ManyToManyField(Host1,through='HostRelation') # 自定义的第3张表
class HostRelation(models.Model): # 外键关系
c1 = models.ForeignKey(Host) c2 = models.ForeignKey(HostAdmin) # 还能够自定义字段
自定义第3张表中添加数据(在自定义的第3张表中,咱们在添加数据时其实跟其它两张表没有关系了,由于自定义时咱们定义了数据库类)
def many_to_many(request): # 在自定义多对多的第3张表中添加数据
# 第1种方法:对象级别
models.HostRelation.objects.create( c1=models.Host.objects.get(id=1), c2=models.HostAdmin.objects.get(id=2) )
# 第2种方法:数据库级别 # models.HostRelation.objects.create( # c1_id = 2, # c2_id = 1 # ) return HttpResponse('ok')
上述两种方法性能比较:第1种方法中两次数据库查询,1次数据库插入;第2种方法中0次数据库查询,1次数据库插入。
4 查询数据库
Django默认生成第3张表的查询/自定义第3张表的查询
def many_to_many(request): # 第1种 Django默认生成第3张表的查询
# 正向查
admin_obj = models.HostAdmin.objects.all(id=1) for item in admin_obj: item.host.all() # 反向查
host_obj = models.Host.objects.get(id=1) host_obj.hostadmin_set.all() # 第2种 自定义第3张表的查询
#relation_list = models.HostRelation.objects.all()
relation_list = models.HostRelation.objects.filter(c2__username='alex') for item in relation_list: print item.c1.hostname print item.c2.username return HttpResponse('ok')
5 select_related的做用
数据库表结构
class UserType(models.Model): caption = models.CharField(max_length=32) class UserInfo(models.Model): user_type = models.ForeignKey('UserType') #这个user_type是一个对象,对象里面封装了ID和caption
username = models.CharField(max_length=32) age = models.IntegerField()
select_related是用来优化查询的,主要优化ForeignKey的查询
def user_info(request): # 普通查询
ret = models.UserInfo.objects.all() #打印执行的SQL语句
print ret.query ''' SELECT "app01_userinfo"."id", "app01_userinfo"."user_type_id", "app01_userinfo"."username", "app01_userinfo"."age"
FROM "app01_userinfo" '''
# select_related优化查询
ret = models.UserInfo.objects.all().select_related() #打印执行的SQL语句
print ret.query ''' SELECT "app01_userinfo"."id", "app01_userinfo"."user_type_id", "app01_userinfo"."username", "app01_userinfo"."age", "app01_usertype"."id", "app01_usertype"."caption" FROM "app01_userinfo" INNER JOIN "app01_usertype" ON ("app01_userinfo"."user_type_id" = "app01_usertype"."id") '''
return HttpResponse('ok')
若是是普通查询,从执行的SQL语句能够看出,它只会获取UserInfo表中的数据;若是使用select_related,从执行的SQL语句能够看出它会把ForiegnKey关联的表自动作一次关联查询。它既获取UserInfo表的数据,同时也获取UserType表的数据。
1 F — 批量修改或更新数据
假设数据库中有1个age字段,我想让全部age字段自加1或某些人自加1,那么能够利用ORM中F:
#导入F模块
from django.db.models import F #F指代当前行的age字段
model.tb.object.all().update(age=F('age')+1)
2 Q — 条件查询,很是有用
默认状况下Django的查询
#查找username='alex'且age=18的对象,条件不是很灵活
models.UserInfo.objects.filter(username='alex',age=18)
假如要查找username='alex'或username='eric'或username='rain',而且age=18的对象,那么默认的查询实现起来比较麻烦,为了解决这种状况,Django为咱们提供了Q操做,下面是操做步骤:
#导入Q模块
from django.db.models import Q
#第1步: #生成一个搜索对象
search_q = Q() #再生成两个搜索对象
search1 = Q() search2 = Q() #第2步: #标记search1中的搜索条件的链接符为'OR'(或条件)
search1.connector = 'OR'
#把搜索条件加入到search1中,注意都是元组的形式
search1.children.append(('字段名','字段内容')) search1.children.append(('字段名','字段内容')) search1.children.append(('字段名','字段内容')) search1.children.append(('字段名','字段内容')) #标记search2中的搜索条件的链接符为'OR'(或条件)
search2.connector = 'OR'
#把搜索条件加入到search2中
search2.children.append(('字段名','字段内容')) search2.children.append(('字段名','字段内容')) search2.children.append(('字段名','字段内容')) search2.children.append(('字段名','字段内容')) #第3步: #把多个搜索条件进行合并,以'AND'的形式(与条件)
search_q.add(search1,'AND') search_q.add(search2,'AND') #第4步: #执行搜索,仍是利用filter
models.HostInfo.objects.filter(search_q)
一、一对多
(1)添加数据
经过对象级别
经过数据库级别(数据在数据库中的存储方式,对象字段_id)
(2)查找
正向查找
经过filter跨表,对象__跨表的字段
获取值,obj.对象.跨表的字段
反向查找
经过filter跨表,Django自动生成与表名相同的对象__跨表的字段
获取值,obj.Django自动生成与表名相同的对象_set.filter()/all()[0:]
二、多对多
(1)Django自动生成关系表
正向:一行数据的对象.ManyToMany字段
反向:一行数据的对象.表名_set
(2)自定义关系表(推荐)
不论是添加、查询,只须要操做自定义关系表
三、select_related
优化查询,一次性将查询的表和ForiegnKey关联的表加载到内存。
四、Django中的F&Q操做
参考资料:
http://www.cnblogs.com/luotianshuai/p/5403513.html