一 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. haploview出现"invalid affected status"的解决方法

    haploview弹出这种错误是因为haploview的缺失值默认为0,而plink文件的缺失值一般用"-9"表示,当ped文件的缺失值为"-9"时,haplo ...

  2. easyui中tree使用simpleData的形式加载数据

    了解了zTree的使用, 发现它的simpleData是非常好用的, 由后台返回一个扁平数据, 直接在前台解析成树形菜单, 网上查了一下, easyui也可以简单实现, 不过....没看懂, 先记录一 ...

  3. String、StringBuffer、StringBuilder源码解读

    序 好长时间没有认真写博客了,过去的一年挺忙的.负责过数据库.线上运维环境.写代码.Code review等等东西挺多. 学习了不少多方面的东西,不过还是需要回归实际.加强内功,方能扛鼎. 去年学习M ...

  4. 线程学习笔记 等待句柄和线程池(摘自https://blog.gkarch.com/threading/part2.html#manualresetevent)

    //如果你的应用有很多线程,这些线程大部分时间都在阻塞,那么可以通过调用ThreadPool.RegisterWaitForSingleObject来减少资源消耗.这个方法接受一个委托,它会在向等待句 ...

  5. C#事务的使用

    1.引入相应的命名空间 using System.Transactions; 2.代码事例(using (TransactionScope ts = new TransactionScope())) ...

  6. js 判断字符为空

    function checkIsNull(value){ if(typeof value=='undefined'){ return true; } if(value==null){ return t ...

  7. struts2获取web元素的方式和方法

    获取web资源的方式按是否与servlet耦合可分为两种,再细分按照依赖方式又有两种即 依赖容器 和  依赖注入 什么是依赖容器 就是依赖  ActionContext或者ServletActionC ...

  8. singleton注意

    如果singleton里面的构造函数里面对资源进行了初始化,那么程序退出时,需要一个release进行资源释放,并且设置instance = null;

  9. EF 实践

    一.CodeFirst 1.新建MVCEFManger(业务层)项目 在该项目下引入entityframework 安装包(用来生成数据库或者实体) PM> install-package en ...

  10. when you're gone

      when you're gone ——艾薇儿 I always needed time on my own 我真的需要一些独处的时间 I never thought I'd need you th ...