Django中的ORM进阶操作
Django中是通过ORM来操作数据库的,通过ORM可以很easy的实现与数据库的交互。但是仍然有几种操作是非常绕也特别容易混淆的。于是,针对这一块,来一个分类总结吧。
对于ORM对数据库的基本操作前面model里已经有了介绍,这里专门针对ORM的一对多、多对多、正向、反向等操作来讲解用法和注意事项。
铭记于心的两条:
- 在联表操作过滤查找数据时用双下划线 "__"
- 在取数据时用点 "."
一、一对多
首先来设计两张简单的表格,并在其中一张表中设置一个另外一张表的外键值 ![](https://i-blog.csdnimg.cn/blog_migrate/92a21653d37a12d439644300d73dbe66.jpeg)
1
2
3
4
5
6
7
8
9
10
11
12
|
|
-
–– coding:utf-8 --
from
<code class="python
plain">django.db import
models
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()
1、添加数据:
传id的方式:(字典的形式传参)
1
2
3
|
user_dict
=
{
"username"
:
"chenchao"
,
"age"
:
"18"
,
"user_type_id"
:
1
}
models.UserInfo.objects.create(
*
*
user_dict)
|
传对象的方式:
1
2
3
4
|
user_type_obj
=
models.UserType.objects.get(
id
=
1
)
user_dict
=
{
"username"
:
"chenchao"
,
"age"
:
"18"
,
"user_type"
: user_type_obj}
models.UserInfo.objects.create(
*
*
user_dict)
|
讲解:在我们写的models类时,外键的字段是”user_type",而django在数据库中保存字段时默认会在后面加“_id”,所以可以直接通过传id的方式。
而在表中的外键字段“user_type”又代表的是字典表中的一行数据对象。于是同样可以传对象的方式来传参。
2、删除数据
3、修改数据 (这两个操作与上面的添加用法基本一致)
4、查找数据
正向查找:(基于存在外键值表的查找为正向)
1
|
models.UserInfo.objects.
filter
(user_type__caption
=
"CEO"
)
|
反向查找:(基于不存在外键值表的查找为反向查找,前提是两张表已经建立了关系)
- 我们创建的外键表有两个字段,id、caption。但Django默认在外键的表里还会埋藏一个字段为userinfo。可以get一个表中不存在的值,通过返回的报错黄页里面可以查看到。
![](https://i-blog.csdnimg.cn/blog_migrate/71d873a269f5c51062d9513fcede0426.jpeg)
- 通过models获取的值都是Qureyset。只要是这个类型就可以用.filter .all .count方法
- 我们知道user_type_obj获取的是外键表中的一行数据对象。
- user_type_obj.id 代表一个数据
- user_type_obj.caption 代表一个数据
- user_type_obj.userinfo_set 特殊,代表的是一种能力。这个能力就可以获取到这个用户类型下的所有用户或者某几个用户
- request.user 代指的是django自带的auth表里的一行数据,与userinfo做了一个OneToOne,与正向查询是一样的。所以也可以用request.user.userinfo.filter....
举例:获取某个人是什么用户类型?当前用户类型下有多少人?
1
2
3
|
user_type_obj
=
models.UserType.objects.get(userinfo__username
=
"chenchao"
)
user_type_obj.caption
user_type_obj.userinfo_set.
all
().count()
|
点赞的例子:
首先设计一下数据库表结构,使点赞的一张表与用户和文章建立外键关系
class MyUser(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__unicode__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.username
class News(models.Model):
title = models.CharField(max_length=32)
content = models.CharField(max_length=32)
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__unicode__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self.title
-
class Favor(models.Model):
user_obj = models.ForeignKey(MyUser)
new_obj = models.ForeignKey(News)
def unicode(self):
return “%s -> %s” %(self.user_obj.username, self.new_obj.title)
点赞表结构
通过反向来操作点赞表,获取点赞的数量
def FoverNum(request):
</span><span style="color: #008000;">#</span><span style="color: #008000;"> 获取所有文章的标题 内容和点赞数 </span>
<span style="color: #008000;">#</span><span style="color: #008000;"> n_num = models.News.objects.all() # 获取所有新闻表的数据对象</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> for item in n_num:</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> print items.title</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> print items.content</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> print item.favor_set.all().count() </span>
<span style="color: #008000;">#</span><span style="color: #008000;"> 获取chenchao点过赞的所有的文章</span>
all_new = models.News.objects.filter(favor__user_obj__username=<span style="color: #800000;">"</span><span style="color: #800000;">chenchao</span><span style="color: #800000;">"</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">for</span> items <span style="color: #0000ff;">in</span><span style="color: #000000;"> all_new:
</span><span style="color: #0000ff;">print</span><span style="color: #000000;"> items.title
</span><span style="color: #0000ff;">print</span><span style="color: #000000;"> items.content
</span><span style="color: #0000ff;">print</span><span style="color: #000000;"> items.favor_set.all().count()
</span><span style="color: #0000ff;">return</span> HttpResponse(<span style="color: #800000;">"</span><span style="color: #800000;">Nothing</span><span style="color: #800000;">"</span>)</pre>
操作表
二、多对多
首先设计好多对多的表结构:
![](https://i-blog.csdnimg.cn/blog_migrate/f4ef08e028cebd20593f763373bc8fd9.jpeg)
1
2
3
4
5
6
7
8
9
|
class
Host(models.Model):
hostname
=
models.CharField(max_length
=
32
)
port
=
models.IntegerField()
class
HostAdmin(models.Model):
username
=
models.CharField(max_length
=
32
)
email
=
models.CharField(max_length
=
32
)
host
=
models.ManyToManyField(Host)
|
前两张表通过models就可以创建,而第三张表django自动帮我们创建完成。我们主要针对第三张表,对其操作增删改查。
1、增加数据
正向添加(基于存在外键值表的查找为正向): add
1
2
3
|
user_obj
=
models.HostAdmin.objects.get(username
=
"chenchao"
)
host_obj
=
models.Host.objects.
filter
(id__lt
=
3
)
user_obj.host.add(
*
host_obj)
|
反向添加:(基于不存在外键值表的查找为反向查找,前提是两张表已经建立了关系)
1
2
3
|
host_obj
=
models.Host.objects.get(
id
=
1
)
user_obj
=
models.HostAdmin.objects.
filter
(id__gt
=
1
)
host_obj.hostadmin_set.add(
*
user_obj)
|
2、查找数据
正向查找:(基于存在外键值的表)
1
2
|
user_obj
=
models.HostAdmin.objects.get(
id
=
1
)
print
user_obj.host.
all
().count()
|
反向查找:(基于不存在外键值的表)
1
2
|
host_obj
=
models.Host.objects.get(
id
=
1
)
print
host_obj.hostadmin_set.
all
().count()
|
自定义Django的多对多的第三张表:
django除了能自动创建多对多的第三张表,同样也可以自定义创建多对多的第三张表,而且操作和管理扩展等难易程度要比自动创建的好许多。所以,在之后的models表结构中,推荐使用自定义的方式。
仅仅在创建时添加一个字段即可:through='HostRelation' 。 HostRelation是我们自定义的第三张表名。
class Host1(models.Model):
hostname = models.CharField(max_length=32)
port = models.IntegerField()
class HostAdmin1(models.Model):
username = models.CharField(max_length=32)
email = models.CharField(max_length=32)
host = models.ManyToManyField(Host1, through='HostRelation')
class HostRelation(models.Model): #自定义创建第三张表,其中的外键都为一对多的关系
c1 = models.ForeignKey(Host1)
c2 = models.ForeignKey(HostAdmin1)
1、创建数据
操作自定义创建的多对多关系表的两种方式:
def ManytoM(request):
models.HostRelation.objects.create( # <span style="color: #ff0000;"><strong>传对象</strong></span>的方式向第三张表中添加数据,笨法
c1</span>=models.Host1.objects.get(id=1<span style="color: #000000;">),
c2</span>=models.HostAdmin1.objects.get(id=2<span style="color: #000000;">)
)
models.HostRelation.objects.create(c1_id</span>=2, c2_id=1,) </span><span style="font-size: 15px;"># <span style="color: #ff0000;"><strong>传id</strong></span>的方式向第三张表中添加数据,easy idea</span></pre>
return HttpResponse("add_many to many OK")
2、查找数据
1
2
3
4
5
6
7
|
relation_list
=
models.HostRelation.objects.
all
()
for
item
in
relation_list:
print
item.c1.hostname
print
item.c2.username
print
models.HostRelation.objects.
filter
(c2__username
=
"chenchao"
)
|
三、select_related
select_related:用来优化数据库查询的操作,可以没有,但优化的不够彻底。
用于在foreignkey查询的时候使用。可以通过query来查看一下django执行的sql语句。
1
2
3
4
|
ret1
=
models.UserInfo.objects.
all
()
ret2
=
models.UserInfo.objects.
all
().select_related()
print
ret1.query
print
ret2.query
|
SELECT
“App01_userinfo”.“id”,
“App01_userinfo”.“user_type_id”,
“App01_userinfo”.“username”,
“App01_userinfo”.“age”
FROM “App01_userinfo”
ret2:
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”)
通过sql语句我们可以清晰地看到select_related不仅把当前表的内容查找出来,而且还把外键的表里的数据也查了出来。如果我们按ret1的方式,需要在多执行一次sql的查找操作。而ret2只需要执行一次。
四、Django神奇的F
如果一张表中的数字列需要增加,那么F是最神奇的操作。
例如我们需要把user_info表里的所有age加1:
1
2
3
|
from
django.db.models
import
F
models.user_info.objects.
all
().update(age
=
F(
'age'
)
+
1
)
|
五、Django更神奇的Q
当做复杂的搜索查找条件时,django的Q可以提供非常便利的方法。
在设计搜索条件时,相同的字段为或操作(OR),不同的字段之间是且操作(AND)
from django.db.models import Q # 导入Q
con = Q() # 创建Q对象
q1 = Q()
q1.connector = ‘OR’ # q1的元素为OR或的关系
q1.children.append((‘id’, 1))
q1.children.append((‘id’, 10))
q1.children.append((‘id’, 9))
q2 = Q()
q2.connector = ‘OR’ # q2的元素为OR或的关系
q2.children.append((‘c1’, 1))
q2.children.append((‘c1’, 10))
q2.children.append((‘c1’, 9))
con.add(q1, ‘AND’) # 将q1添加到con对象中,与其他的Q为且的关系
con.add(q2, ‘AND’)
models.Tb1.objects.filter(con) #将总的Q对象添加到model的查找条件中
提示:
1、之前所有的方法 如__gt,__lt,__contains. 双下划线联表查询等等都可以继续使用
2、append添加的是一个元组
3、最外层是用AND连接