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

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

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

(1)数据库级别

上表是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)

             

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张表。下面是对应数据库表的关系:

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分配两个主机的管理权限
# 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张表如下所示:

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张表,不需要使用默认的表结构。分析下三张表之间的关系:

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

Django基础——Model篇(三)的更多相关文章

  1. Django基础——Model篇(二)

    一 Model连表关系 一对多:models.ForeignKey(其他表)    多对多:models.ManyToManyField(其他表)    一对一:models.OneToOneFiel ...

  2. Django基础——Model篇(一)

    到目前为止,当程序涉及到数据库相关操作时,我们一般都会这么操作:    (1)创建数据库,设计表结构和字段    (2)使用MySQLdb来连接数据库,并编写数据访问层代码    (3)业务逻辑层去调 ...

  3. Django基础第一篇

    目录 1.Django MTV框架简介 2.基础命令创建项目的配置说明 3.前后端交互案例 4.基于数据库实现数据交互增删改查 Django简介 Django框架的设计模式借鉴了MVC的思想,和MVC ...

  4. Django进阶Model篇—数据库操作(ORM)

    一.数据库配置 django 默认支持sqlite.mysql.oracle.postgresql数据库,像db2和sqlserver之类的数据库需要第三方的支持,具体详见https://docs.d ...

  5. Django进阶Model篇008 - 使用原生sql

    注意:使用原生sql的方式主要目的是解决一些很复杂的sql不能用ORM的方式写出的问题. 一.extra:结果集修改器-一种提供额外查询参数的机制 二.执行原始sql并返回模型实例 三.直接执行自定义 ...

  6. Django进阶Model篇004 - ORM常用操作

    一.增加 create和save方法 实例: 1.增加一条作者记录 >>> from hello.models import * >>> Author.object ...

  7. Django进阶Model篇002 - 模型类的定义

    一.创建数据模型. 实例: 作者模型:一个作者有姓名. 作者详情模型:把作者的详情放到详情表,包含性别.email 地址和出生日期,作者详情模型与作者模型之间是一对一的关系(OneToOneField ...

  8. django 基础框架学习 (三)

    Django框架基础-03数据库新增数据    1.save⽅法        >>> from datetime import date        >>> f ...

  9. Django基础-04篇 Django开发前后端联动

    1. 写views views.py代码块 1.在前端以/article/{{ article.id }}这种方式请求后台, 参数配置在urls.py中path('category/<int:i ...

随机推荐

  1. MySQL Batch 与 Transaction

    最近在数据库上经常遇到死锁问题. 表现的问题有 1. 有一个查询为: 1) 一个复杂的 select 查处一组大数据 2) 使用事务 update 这组数据的状态 为了让锁定的时间变短, 我将这整个大 ...

  2. 【转】HTTP状态码(HTTP Status Code)

    原文链接:http://www.chaoji.com/features/httpstatus.aspx 一些常见的状态码为: 200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - ...

  3. 20145225《Java程序设计》 第9周学习总结

    20145225<Java程序设计> 第9周学习总结 教材学习内容总结 第十六章 整合数据库 16.1JDBC JDBC是用于执行SQL的解决方案,开发人员使用JDBC的标准接口,数据库厂 ...

  4. (Python)list的内建函数 filter(), map(), 和 reduce()

    这一节,我们将主要学习用于list的三个内建函数: filter(), map(), 和 reduce(). 1.filter(function, sequence)  逐个从sequence中取一个 ...

  5. ios 上架需要注意的问题

    前段时间上架产品的时候,因为定位提示问题被拒绝了,而且集成了融云及时聊天的key,没有从测试环境升级到生产环境,所以记录一下APP上架所需要注意的事项. 1.程序没有崩溃性BUG 2.程序不存在逻辑性 ...

  6. Html.DropDownList 选中 mvc view 弱类型

    List<Model.SysGroup> listGroup = sysGroupBll.Where(o => o.IsSb == true, o => o.Id).ToLis ...

  7. C++矢量图形库系列(1)——矢量图形库乱谈(转)

    转自:http://blog.sina.com.cn/s/blog_4265e1760100lg03.html 本系列篇章的主要内容是讲解矢量图形库的编译.开发和使用.并不对他们周边的内容做过多的描述 ...

  8. step by step 之餐饮管理系统五(Util模块)------附上篇日志模块源码

    这段时间一直在修改日志模块,现在基本上写好了,也把注释什么的都加上了,昨天邮件发送给mark的园友一直报失败,老是退回来,真是报歉,如下图所示:

  9. (转)ASP.NET Mvc 2.0 - 1. Areas的创建与执行

    转自:http://www.cnblogs.com/terrysun/archive/2010/04/13/1711218.html ASP.NET Mvc 2.0 - 1. Areas的创建与执行 ...

  10. 如何自适应网页的协议(http/https/……)

    今天在百度统计(tongji.baidu.com)看到了一种脚本写法,以前确实不知道,记录一下,也算有所收获. 一个站点被部署的时候,同时支持http也支持https,但是当一个站点采用https访问 ...