阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Django 数据库操作详解

454次阅读
没有评论

共计 7723 个字符,预计需要花费 20 分钟才能阅读完成。

你创建完数据模型,Django 会自动提供给你数据库抽象的 API,可以创建, 获取, 修改, 删除对象. 本篇文档讲解如何使用 API,包括外键相关查询以及多对多模式的操作等

我们参考下面模型,一个 weblog:

class Blog(models.Model):  
    name = models.CharField(max_length=100)  
    tagline = models.TextField()  

    def __unicode__(self):  
        return self.name  

class Author(models.Model):  
    name = models.CharField(max_length=50)  
    email = models.EmailField()  

    def __unicode__(self):  
        return self.name  

class Entry(models.Model):  
    blog = models.ForeignKey(Blog)  
    headline = models.CharField(max_length=255)  
    body_text = models.TextField()  
    pub_date = models.DateTimeField()  
    authors = models.ManyToManyField(Author)  
    n_comments = models.IntegerField()  
    n_pingbacks = models.IntegerField()  
    rating = models.IntegerField()  

    def __unicode__(self):  
        return self.headline

创建对象

用 Python 对象描述数据库表的数据,django 使用一个直观的系统,一个模型类描述一个数据表,一个类的实例描述表的一条详细记录。使用模型的 save() 方法将对象创建到数据库。

from mysite.blog.models import Blog  
b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')  
b.save() 

只有执行 save 方法时,django 才会执行 sql 把对象写入数据库。

保存修改的对象

保存修改仍然使用 save() 方法

b5.name = 'New name'  
b5.save()

保存 ForeignKey 和 ManyToManyField 字段

cheese_blog = Blog.objects.get(name="Cheddar Talk")  
entry.blog = cheese_blog  
entry.save() 

为 ManyToManyField 增加记录

joe = Author.objects.create(name="Joe")  
entry.authors.add(joe)

检索对象

从数据库里检索对象,可以通过模型的 Manage 来建立 QuerySet, 一个 queryset 表现为一个数据库中对象的结合,他可以有 0 个一个或多个过滤条件,在 SQL 里 QUERYSET 相当于 SELECT 语句用 WHERE 或 LIMIT 过滤。
你通过模型的 manager 来获取 QUERYSET,每个模型至少有一个 manager

检索所有的对象

检索表中所有数据,最简单的方式是用 all().

all_entries = Entry.objects.all()

过滤检索特定对象

检索过滤特定查询结果,有两个方法。
filter(**kwargs) 返回一个新的匹配查参数件后的 QuerySet
exclude(**kwargs) 返回一个新的不匹配查询参数后的 QuerySet

例如:

Entry.objects.filter(pub_date__year=2006)

链接过滤

Entry.objects.filter(...     headline__startswith='What'  
... ).exclude(...     pub_date__gte=datetime.now()  
... ).filter(...     pub_date__gte=datetime(2005, 1, 1)  
... ) 

过滤结果集是唯一

每次你完善一个 QuerySet,你获得一个全新的结果集,不包括前面的。每次完成的结果集是可以贮存,使用或复用

q1 = Entry.objects.filter(headline__startswith="What")  
q2 = q1.exclude(pub_date__gte=datetime.now())  
q3 = q1.filter(pub_date__gte=datetime.now())

三个 QuerySets 是分开的,第一个是 headline 以”What”单词开头的结果集,第二个是第一个的子集,即 pub_date 不大于现在的,第三个是第一个的子集,pub_date 大于现在的

结果集是延迟的

QuerySets 是延迟的,创建 QuerySets 不会触及到数据库动作,你可以多个过滤合并到一起,直到求值的时候 django 才会开始查询。如:

q = Entry.objects.filter(headline__startswith="What")  
q = q.filter(pub_date__lte=datetime.now())  
q = q.exclude(body_text__icontains="food")  
print q

虽然看起来执行了三个过滤条件,实际上最后执行 print q 的时候,django 才开始查询执行 SQL 到数据库。

其他的 QuerySet 方法

大多数情况你使用 all(), filter() 和 exclude()
限制 QuerySets
使用 python 的数组限制语法限定 QuerySet, 如:
取前 5 个

Entry.objects.all()[:5]

取第五个到第十个

Entry.objects.all()[5:10]

一般的,限制 QuerySet 返回新的 QuerySet,不会立即求值查询,除非你使用了”step”参数

Entry.objects.all()[:10:2]
Entry.objects.order_by('headline')[0]  

Entry.objects.order_by('headline')[0:1].get()

字段查找

字段查找是指定 SQL 语句的 WHERE 条件从句,通过 QuerySet 的方法 filter(), exclude() 和 get() 指定查询关键字
基本查询 field__lookuptype=value
例如:

Entry.objects.filter(pub_date__lte='2006-01-01')

转换为 SQL:

SELECT * FROM blog_entry WHERE pub_date <=‘2006-01-01’;

如果你传了无效的参数会抛异常

数据库 API 支持一些查询类型,下面体验一下

exact

Entry.objects.get(headline__exact="Man bites dog") 

转化为 SQL:
Sql 代码 收藏代码
SELECT … WHERE headline =‘Man bites dog’;

如果查询没有提供双下划线,那么会默认 __exact=

Blog.objects.get(id__exact=14)  # Explicit form  
Blog.objects.get(id=14)         # __exact is implied

iexact

忽略大小写

Blog.objects.get(name__iexact="beatles blog")

blog title 会匹配“Beatles Blog”,“beatles blog”, 甚至“BeAtlES blOG”.

contains

包含查询,区分大小写

Entry.objects.get(headline__contains='Lennon') 

转化为 SQL

SELECT … WHERE headline LIKE‘%Lennon%’;
icontains 不区分大小写

startswith, endswith,istartswith,iendswith
前模糊匹配,后模糊匹配
跨关系查询

Entry.objects.filter(blog__name__exact='Beatles Blog')

这个可以跨越你想要的深度。
反向跨关系查询

Blog.objects.filter(entry__headline__contains=’Lennon’)
如果跨越多层关系查询,中间模型没有值,django 会作为空对待不会发生异常

Blog.objects.filter(entry__author__name='Lennon'  )
Blog.objects.filter(entry__author__name__isnull=True)  
Blog.objects.filter(entry__author__isnull=False,  
                    entry__author__name__isnull=True) 

过滤器可参考模型字段

目前给的例子里,我们建立了过滤,比照模型字段值和一个固定的值,但是如果我们想比较同一个模型里的一个指端和另一个字段的值,django 提供 F()

from django.db.models import F  
Entry.objects.filter(n_pingbacks__lt=F('n_comments'))

django 支持加减乘除和模计算

Entry.objects.filter(n_pingbacks__lt=F('n_comments') * 2)  
Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))  
Entry.objects.filter(author__name=F('blog__name'))

主键查询捷径

Blog.objects.get(id__exact=14) # Explicit form  
Blog.objects.get(id=14) # __exact is implied  
Blog.objects.get(pk=14) # pk implies id__exact

不仅限于__exact 查询

# Get blogs entries with id 1, 4 and 7  
Blog.objects.filter(pk__in=[1,4,7])  

# Get all blog entries with id > 14  
Blog.objects.filter(pk__gt=14)

跨越查询

Entry.objects.filter(blog__id__exact=3) # Explicit form  
Entry.objects.filter(blog__id=3)        # __exact is implied  
Entry.objects.filter(blog__pk=3)        # __pk implies __id__exact

like 语句转义百分号

Entry.objects.filter(headline__contains='%')

转义为
SELECT … WHERE headline LIKE‘%\%%’;

缓存查询集

每个 QuerySet 都包含一个缓存,以尽量减少对数据库的访问。理解他的工作原理很重要,可以写出最高效的代码。
在最新创建的 QuerySet 里,缓存是空的。在第一次 QuerySet 被取值,因此数据库查询发生,django 把查询结果放入缓存,并返回给请求,随后的查询取值会复用缓存中的结果。

保持缓存的思想习惯,因为如果你不正确使用查询缓存会有麻烦。例如下面例子会创建两个 QuerySet

print [e.headline for e in Entry.objects.all()]  
print [e.pub_date for e in Entry.objects.all()]

这样意味着数据库查询会执行两次,实际两次数据库加载
为了避免这个问题,简单保存 QuerySet 复用

queryset = Poll.objects.all()  
print [p.headline for p in queryset] # Evaluate the query set.  
print [p.pub_date for p in queryset] # Re-use the cache from the evaluation.

通过 Q 对象进行复合查询

关键字查询使用 filter 是 and 集合,如果你想要执行更多的复合查询如“or”可以 Q 对象。
Q 对象(django.db.models.Q)是一个关键字集合封装的参数。
例如

Q(question__startswith='What')

Q 对象可以使用 & 和 | 链接
例如一个 OR 查询

Q(question__startswith='Who') | Q(question__startswith='What')

相当于下面的 SQL
Sql 代码 收藏代码
WHERE question LIKE‘Who%’OR question LIKE‘What%’
取消一个 Q 对象使用~

Q(question__startswith='Who') | ~Q(pub_date__year=2005)

可以传入多个 Q 对象

Poll.objects.get(Q(question__startswith='Who'),  
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))  
) 

翻译成 SQL
SELECT * from polls WHERE question LIKE‘Who%’
AND (pub_date =‘2005-05-02’OR pub_date =‘2005-05-06’)

比较对象

比较两个模型实例,使用 python 标准的运算符,两个等号 ==

some_entry == other_entry  
some_entry.id == other_entry.id  
some_obj == other_obj  
some_obj.name == other_obj.name

删除对象

删除方法是很方便的,方法名为 delete(),这个方法直接删除对象没有返回值
例如

e.delete()

你也可以批量删除对象,每个 QuerySet 有一个 delete() 方法,能删除 QuerySet 里所有对象
例如:删除 pub_date 为 2005 年的对象

一次修改多个对象

有时候你想给 QuerySet 里所有对象的一个字段赋予特定值,你可以使用 update() 方法
例如

# Update all the headlines with pub_date in 2007.  
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')

这个方法只能用于无关联字段和外键

b = Blog.objects.get(pk=1)  
# Change every Entry so that it belongs to this Blog.  
Entry.objects.all().update(blog=b) 

update() 方法不返回任何值,QuerySet 不支持 save 方法,如果要执行 save,可以如下:

for item in my_queryset:  
    item.save() 

update 也可以使用 F()

# THIS WILL RAISE A FieldError  
Entry.objects.update(headline=F('blog__name')) 

关系对象

当你在 model 里定义一个关系时,模型实例会有一个方便的 API 来访问关系对象。用本页上面的模型举个例子,一个 Entry
对象可以得到 blog 对象,访问 blog 属性 e.blog。
django 也创建 API 来访问关系对象的另一边,一个 blog 对象访问 Entry 列表

b.entry_set.all().

One-to-many 关系

如果一个对象有 ForeignKey,这个模型实例访问关系对象通过简单的属性

e = Entry.objects.get(id=2)  
e.blog # Returns the related Blog object.  

你可以凭借外键属性获取和赋值,修改外键值知道执行 save() 方法才会保存到数据库

e = Entry.objects.get(id=2)  
e.blog = some_blog  
e.save()  

如果 ForeignKey 设置了 null=True 你可以赋值为 None

e = Entry.objects.get(id=2)  
e.blog = None  
e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"  

e = Entry.objects.get(id=2)  
print e.blog  # Hits the database to retrieve the associated Blog.  
print e.blog  # 不会在向数据库取; 使用缓存中的值.  

e = Entry.objects.select_related().get(id=2)  
print e.blog  # 不会在向数据库取; 使用缓存中的值.  
print e.blog  # 不会在向数据库取; 使用缓存中的值.  

b = Blog.objects.get(id=1)  
b.entry_set.all() # 返回所有 blog 的关联对象.  

# b.entry_set is a Manager that returns QuerySets.  
b.entry_set.filter(headline__contains='Lennon')  
b.entry_set.count()  

b = Blog.objects.get(id=1)  
b.entries.all() # 返回所有 blog 的关联对象  

# b.entries is a Manager that returns QuerySets.  
b.entries.filter(headline__contains='Lennon')  
b.entries.count()  

add(obj1, obj2, ...) 增加多个关系对象
create(**kwargs) 建立新对象
remove(obj1, obj2, ...) 去除多个关系对象
clear() 清理所有关系对象

b = Blog.objects.get(id=1)  
b.entry_set = [e1, e2]  

Many-to-many 关系
e = Entry.objects.get(id=3)  
e.authors.all() # 返回 Entry 所有 authors .  
e.authors.count()  
e.authors.filter(name__contains='John')  

a = Author.objects.get(id=5)  
a.entry_set.all() # 返回 Author 所有 entry .  

#One-to-one 关系 

class EntryDetail(models.Model):  
    entry = models.OneToOneField(Entry)  
    details = models.TextField()  

ed = EntryDetail.objects.get(id=2)  
ed.entry # 返回 Entry 对象. 

本文永久更新链接地址 :http://www.linuxidc.com/Linux/2017-02/140378.htm

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计7723字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19350
评论数
4
阅读量
7964299
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击

恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击

恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击 PHP-FPM(FastCGl Process M...
升级自动部署更新SSL证书系统、申请godaddy的APIKEY

升级自动部署更新SSL证书系统、申请godaddy的APIKEY

升级自动部署更新 SSL 证书系统、申请 godaddy 的 APIKEY 公司之前花钱购买的 ssl 证书快...
颠覆 AI 开发效率!开源工具一站式管控 30+大模型ApiKey,秘钥付费+负载均衡全搞定

颠覆 AI 开发效率!开源工具一站式管控 30+大模型ApiKey,秘钥付费+负载均衡全搞定

  颠覆 AI 开发效率!开源工具一站式管控 30+ 大模型 ApiKey,秘钥付费 + 负载均衡全...
星哥带你玩飞牛NAS-12:开源笔记的进化之路,效率玩家的新选择

星哥带你玩飞牛NAS-12:开源笔记的进化之路,效率玩家的新选择

星哥带你玩飞牛 NAS-12:开源笔记的进化之路,效率玩家的新选择 前言 如何高效管理知识与笔记,已经成为技术...
星哥带你玩飞牛NAS-1:安装飞牛NAS

星哥带你玩飞牛NAS-1:安装飞牛NAS

星哥带你玩飞牛 NAS-1:安装飞牛 NAS 前言 在家庭和小型工作室场景中,NAS(Network Atta...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛 NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手! 作为动漫爱好者,你是否还在为...
开发者福利:免费 .frii.site 子域名,一分钟申请即用

开发者福利:免费 .frii.site 子域名,一分钟申请即用

  开发者福利:免费 .frii.site 子域名,一分钟申请即用 前言 在学习 Web 开发、部署...
150元打造低成本NAS小钢炮,捡一块3865U工控板

150元打造低成本NAS小钢炮,捡一块3865U工控板

150 元打造低成本 NAS 小钢炮,捡一块 3865U 工控板 一块二手的熊猫 B3 工控板 3865U,搭...
Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集 在云原生体系中,Prometheus 已成为最主流的监控与报警...
星哥带你玩飞牛NAS-16:飞牛云NAS换桌面,fndesk图标管理神器上线!

星哥带你玩飞牛NAS-16:飞牛云NAS换桌面,fndesk图标管理神器上线!

  星哥带你玩飞牛 NAS-16:飞牛云 NAS 换桌面,fndesk 图标管理神器上线! 引言 哈...