Django的ORM

Django查询集是写在model或者view文件里面,对数据库进行增删改查,其实就是封装了MySQL的语句,使其变得简单易学,但是查询集可能还是解决不了全部问题,如果遇到查询集解决不了的情况,应该使用MySQL语句进行操作

添加数据库内容,包括建立模型(创建数据表),创建对象(添加每行内容),保存对象的改动(修该内容),添加外键

建立模型

一旦你建立好数据模型,Django 会自动为你生成一套数据库抽象的API,可以让你创建、检索、更新和删除对象。这篇文档阐述如何使用这些API。

首先建立如下模型,这是一个图书管理应用,图书和作者是一个多对多的关系:

from django.db import models
# Create your models here.

class Author(models.Model):
    name = models.CharField(max_length=32)
    sex = models.CharField(max_length=32)
    age = models.IntegerField()
    
    def __str__(self):
        return self.name

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    date = models.DateField()
    publish = models.CharField(max_length=32)
    author = models.ManyToManyField(Author)
    
    def __str__(self):
        return self.title

创建对象

Django 使用一种直观的方式把数据库表中的数据表示成Python 对象:一个模型类代表数据库中的一个表,一个模型类的实例代表这个数据库表中的一条特定的记录。

创建对象有两种方法:

  • 使用关键字参数实例化模型实例来创建一个对象,然后调用save()把他保存到数据库中

    a = Author(name='alex', sex='male', age=4)
    a.save()

  • 使用create方法

    Author.objects.create(name='yuan', sex='male', age=8)

保存对象的改动

假设Blog 的一个实例b5 已经被保存在数据库中,下面这个例子将更改它的name 并且更新数据库中的记录:

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

保存字段

保存ForeignKey字段

  • 保存ForeignKey字段的方式和保存普通字段相同,只要把一个正确类型的对象赋值给该字段
  • 更改后save()保存

保存ManyToMany字段

更新ManyToManyField的方式有一些不同,需要使用字段的add()方法来增加关联关系的一条记录,

    >>> from blog.models import Author
    >>> joe = Author.objects.create(name="Joe")
    >>> entry.authors.add(joe)

也可以一次性增加多条记录

    >>> john = Author.objects.create(name="John")
    >>> paul = Author.objects.create(name="Paul")
    >>> george = Author.objects.create(name="George")
    
    >>> ringo = Author.objects.create(name="Ringo")
    >>> entry.authors.add(john, paul, george, ringo)

注意:取对象的时候要拿id取

  • 先给两个表分别实例化对象生成每一行数据
  • 然后在外面先分别选取需要添加关系的对象
  • 然后给有外键字段的对象add另一个,也可以add多个

查主要是对数据库的搜索查询,找到适合条件的数据可以通过模型中的管理器构造一个查询集,来从你的数据库总获取对象。

获得所有对象

获得一个表中所有的对象的最简单的方式是全部获取,可以使用管理器的all()方法

>>> all_entries = Entry.objects.all()

获取一个单一对象

过滤器始终会给你一个查询集,即使只有一个对象满足条件,你如果只想查询一个的话,可以使用get方法,他直接返回该对象

>>> one_entry = Entry.objects.get(pk=1)

使用过滤器获取特定对象集

  • all()方法返回了一个包含数据库表中所有记录查询集。但在通常情况下,你往往想要获取的是完整数据集的一个子集。
  • filter(**kwargs)返回一个新的[查询集,它包含满足查询参数的对象。
  • exclude(**kwargs)返回一个新的查询集,它包含不满足查询参数的对象。

比如要获取年份为2006的所有文章的查询集,可以使用filter()方法

Entry.objects.filter(pub_date__year=2006)

相当于

Entry.objects.all().filter(pub_date__year=2006)
  • 查询集的筛选结果本身还是查询集,所以可以将筛选语句链接在一起
  • 每次筛选一个查询集,得到的都是全新的另一个查询集,它和之前的查询集之间没有任何绑定关系每次筛选都会创建一个独立的查询集,它可以被存储以及反复使用。
  • 查询集是惰性执行的,创建查询集不会带来任何数据库的访问,直到查询集需要求值时,Django才会真正运行这个查询。

限制查询集

可以使用切片语法来限制查询集

下面语法返回前面5个对象

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

下面语句返回6到10个对象

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

它不支持负的索引

通常,查询集的切片返回一个新的查询集—— 它不会执行查询。有一个例外,是如果你使用Python 切片语法中"step"参数。例如,下面的语句将返回前10 个对象中每隔2个对象,它将真实执行查询:

>>> Entry.objects.all()[:10:2]

如果要选取一个单一的对象而不是列表,则使用索引

>>> Entry.objects.order_by('headline')[0]

字段查询

字段查询广泛应用于filter(),exclude()和get()的关键字参数指定

查询的关键字参数的基本形式是 field__lookuptype=value,其中field为字段,lookuptype是匹配样式,value为值

如果要涉及到外键参数过滤,形式应该为 class__field__lookuptype=value,其中class为表名

gt 大于

ltn 小于

exact 精确匹配

例如:

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

将生成下面的SQL:

SELECT ... WHERE headline = 'Man bites dog';

iexact大小写不敏感的匹配。

所以,查询:

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

将匹配标题为"Beatles Blog"、"beatles blog" 甚至"BeAtlES blOG" 的Blog。

contains大小写敏感的包含关系测试

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

大体可以翻译成下面的SQL:

SELECT ... WHERE headline LIKE '%Lennon%';

Icontains大小写不敏感的包含关系测试

startswith,endswith

分别表示以XXX开头和以XXX结尾。

跨关联关系的查询

若要跨越关联关系,只需要使用关联的模型字段的名称,并使用双下划线分隔,直到你想要的字段,这种跨越可以是任意的深度,

下面这个例子获取所有Blog 的name 为'Beatles Blog' 的Entry 对象:

>>> Entry.objects.filter(blog__name='Beatles Blog')

它还可以反向工作,若要引用一个反向的关系,只需要使用该模型的小写的名称

下面的示例获取所有的Blog 对象,它们至少有一个Entry 的headline 包含'Lennon':

>>> Blog.objects.filter(entry__headline__contains='Lennon')

分组查询

如果要查询所有书籍的平均价格

from django.db.models import Avg,Min,Sum,Max
# 求所有书籍的平均价格
ret=Book.object.all().aggregate(Avg('price'))

分组使用value()来分组

F() 对象:引用模型的字段

假设要将所有的书的价格改为1000,可以使用如下语句

Book.object.filter(id=5).update(price=1000)

但是,如果要给每本书的价格价格加20,这就涉及到如何取得前面的价格,这就用到了F对象

from django.db.models import F,Q
Book.object.all().update(price=F('price')+20)

Q对象精确查找

如果要查询书籍中名字的来头为'"老"字,且价格大于100的书籍,应该这样

ret = Book.object.filter(title__startwith='老',price__gt=100)
print(ret)

但是如果要查询书籍中名字的开头为"老"字,或者价格大于100的书,用逗号就没法完成,这就用到了Q对象

from django.db.models import F,Q
et = Book.object.filter(Q(title__startwith='老') | Q(price__gt=100)

Q对象可以使用 & | 以及 ~分别代表,and,or和not用法

访问关联的对象

模型的实例可以很方便的进行关联对象的查询

一对多关系

正向查询

如果一个模型具有ForeignKey,那么该模型的实例将可以通过属性访问关联的(外部)对象。

例如:

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

对外键的修改如下

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

反向查询

反向查询使用foo_set管理器,其中,foo为源模型的小写名称

比如

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.

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

你可以在ForeignKey定义时设置related_name参数来覆盖foo_set 的名称。例如,如果Entry 模型改成blog = ForeignKey(Blog, related_name='entries'),那么上面的示例代码应该改成这样:

>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to 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()

从关联的对象集中删除所有的对象。

多对多关系

多对多关系的两端都会自动获得访问另一端的API。这些API 的工作方式与上面提到的“方向”一对多关系一样。

唯一的区别在于属性的名称:定义 ManyToManyField的模型使用该字段的属性名称,而“反向”模型使用源模型的小写名称加上'_set' (和一对多关系一样)。

一个例子可以让它更好理解:

# 正向查询
e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains='John')

# 反向查询
a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.

一对一关系

一对一关系与多对一关系非常相似。如果你在模型中定义一个OneToOneField,该模型的实例将可以通过该模型的一个简单属性访问关联的模型。

例如:

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

ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.

在“反向”查询中有所不同。一对一关系中的关联模型同样具有一个管理器对象,但是该管理器表示一个单一的对象而不是对象的集合:

e = Entry.objects.get(id=2)
e.entrydetail # ret

改(update())

修改的方式比较简单,使用update对象

比如要修改id为5的书的价格为1000

Book.object.filter(id=5).update(price=1000)

如果要一次性修改多个对象

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

删(delete())

例如,下面的语句删除pub_date 为2005 的所有Entry对象

Entry.objects.filter(pub_date__year=2005).delete()

注意,当Django要删除一个对象时,任何有外键指向要删除对象的对象将一起被删除

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容