Django基础——Model篇(三)

时间:2021-04-30 09:22:38

一 Django ORM中的概念

ORM —— 关系对象映射,是Object Relational Mapping的简写,是用来简化数据库操作的框架

Django ORM遵循Code Frist原则,即根据代码中定义的类来自动生成数据库表,对于ORM框架:
  (1)自定义的类表示待创建数据库的表
  (2)根据自定义类创建的对象obj表示数据库表中的一行数据
  (3)obj.字段1、obj.字段2、...、obj.字段n表示每一行数据中相应字段的值

ORM中的一对多、一对一以及多对多的概念及应用场景,具体参见前期博客。
ORM中的正向和反向操作,这是相对而言的,主要取决于一对多/一对一/多对多字段写在哪个类中。

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表去操作数据库,就表示反向。

二 ORM中一对多的操作

models.py中的表结构

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中生成三种用户类型的代码

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')

生成的表结构如下图所示:

Django基础——Model篇(三)

1 UserInfo表添加数据的两种方法

(1)数据库级别

Django基础——Model篇(三)

上表是UserInfo数据库表结构,外键user_type字段默认会加上"_id"进行存储,那么从数据库级别添加数据的代码如下:

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)对象级别

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方法是从数据库中取一个匹配的结果,返回一个对象,如果不存在,则会报错
 filter方法是从数据库中取匹配的结果,返回一个对象列表,如果不存在,则会返回[]

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 # 1、查找某个人是哪种用户类型;
user_type_obj = models.UserType.objects.get(userinfo__username='xx')
print user_type_obj.caption # 2、查找指定用户类型下有哪些用户
# 下面两种方法是等价的,注意在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)

Django基础——Model篇(三)        Django基础——Model篇(三)      Django基础——Model篇(三)

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')

三 ORM中多对多的操作

Django中的多对多与一对多没有联系,它的实际操作比一对多简单,Django默认会为多对多生成第3张表。下面是对应数据库表的关系:

Django基础——Model篇(三)

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')

生成的三张表如下:

Django基础——Model篇(三)   
  1 正向添加数据(从包含多对多字段的表操作数据库)

def many_to_many(request):
# 正向添加
# 目的:给alex分配两个主机的管理权限
# 1、获取指定的HostAdmin对象
admin_obj = models.HostAdmin.objects.get(username='alex')
# 2、获取指定的Host,条件为id小于3
host_list = models.Host.objects.filter(id__lt=3)
# 3、将用户alex和指定的两个主机添加到对应的第3张表中,注意host_list是列表,所有加*号
admin_obj.host.add(*host_list)
return HttpResponse('ok')

Django自动生成的第3张表如下所示:

Django基础——Model篇(三)

2 反向添加数据(从不包含多对多字段的表操作数据库)

def many_to_many(request):
# 反向添加
# 1、获取主机表
# host_obj = models.Host.objects.get(id=3)
# 2、获取用户表
# admin_list = models.HostAdmin.objects.filter(id__gt=1)
# 3、添加数据,注意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张表,不需要使用默认的表结构。分析下三张表之间的关系:

Django基础——Model篇(三)

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表的数据。

四 ORM中的F&Q

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)

五 Django ORM总结

1、一对多
 (1)添加数据
    通过对象级别
    通过数据库级别(数据在数据库中的存储方式,对象字段_id)
 (2)查找
    正向查找
     通过filter跨表,对象__跨表的字段
     获取值,obj.对象.跨表的字段
    反向查找
          通过filter跨表,Django自动生成与表名相同的对象__跨表的字段
     获取值,obj.Django自动生成与表名相同的对象_set.filter()/all()[0:]
 2、多对多
 (1)Django自动生成关系表
    正向:一行数据的对象.ManyToMany字段
        反向:一行数据的对象.表名_set
 (2)自定义关系表(推荐)
       不管是添加、查询,只需要操作自定义关系表
 3、select_related
  优化查询,一次性将查询的表和ForiegnKey关联的表加载到内存。

4、Django中的F&Q操作

参考资料:

http://www.cnblogs.com/luotianshuai/p/5403513.html