Django model sql 操作

创建对象

假设模型位于mysite/blog/models.py文件中,那么创建对象的方式如下:

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

在后台,这会运行一条SQL的INSERT语句。如果你不显式地调用save()方法,Django不会立刻将该操作反映到数据库中。save()方法没有返回值,它可以接受一些额外的参数。

如果想要一行代码完成上面的操作,请使用creat()方法,它可以省略save的步骤:

1
b = Blog.objects.create(name='Beatles Blog', tagline='All the latest Beatles news.')

保存对象

使用save()方法,保存对数据库内已有对象的修改。例如如果已经存在b5对象在数据库内:

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

在后台,这会运行一条SQL的UPDATE语句。如果你不显式地调用save()方法,Django不会立刻将该操作反映到数据库中。

保存外键和多对多字段

保存一个外键字段和保存普通字段没什么区别,只是要注意值的类型要正确。下面的例子,有一个Entry的实例entry和一个Blog的实例cheese_blog,然后把cheese_blog作为值赋给了entry的blog属性,最后调用save方法进行保存。

1
2
3
4
5
>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

多对多字段的保存稍微有点区别,需要调用一个add()方法,而不是直接给属性赋值,但它不需要调用save方法。如下例所示:

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

在一行语句内,可以同时添加多个对象到多对多的字段,如下所示:

1
2
3
4
5
>>> 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)

如果你指定或添加了错误类型的对象,Django会抛出异常。

数据检索

检索所有对象

使用all()方法,可以获取某张表的所有记录。

1
>>> all_entries = Entry.objects.all()
过滤对象

有两个方法可以用来过滤QuerySet的结果,分别是:

  • filter(**kwargs):返回一个根据指定参数查询出来的QuerySet 类似于ruby里的where
  • exclude(**kwargs):返回除了根据指定参数查询出来结果的QuerySet 非查询 类似于ruby里的where.not

其中,**kwargs参数的格式必须是Django设置的一些字段格式。

例如:

1
Entry.objects.filter(pub_date__year=2006)

它等同于:

1
Entry.objects.all().filter(pub_date__year=2006)

链式过滤

filter和exclude的结果依然是个QuerySet,因此它可以继续被filter和exclude,这就形成了链式过滤:

1
2
3
4
5
6
7
>>> Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.date.today()
... ).filter(
... pub_date__gte=datetime(2005, 1, 30)
... )

(这里需要注意的是,当在进行跨关系的链式过滤时,结果可能和你想象的不一样,参考下面的跨多值关系查询)

被过滤的QuerySets都是唯一的

每一次过滤,你都会获得一个全新的QuerySet,它和之前的QuerySet没有任何关系,可以完全独立的被保存,使用和重用。例如:

1
2
3
>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())

例子中的q2和q3虽然由q1得来,是q1的子集,但是都是独立自主存在的。同样q1也不会受到q2和q3的影响。

QuerySets都是懒惰的

一个创建QuerySets的动作不会立刻导致任何的数据库行为。你可以不断地进行filter动作一整天,Django不会运行任何实际的数据库查询动作,直到QuerySets被提交(evaluated)。

简而言之就是,只有碰到某些特定的操作,Django才会将所有的操作体现到数据库内,否则它们只是保存在内存和Django的层面中。这是一种提高数据库查询效率,减少操作次数的优化设计。看下面的例子:

1
2
3
4
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)

上面的例子,看起来执行了3次数据库访问,实际上只是在print语句时才执行1次访问。通常情况,QuerySets的检索不会立刻执行实际的数据库查询操作,直到出现类似print的请求,也就是所谓的evaluated。

检索单一对象

类似于ruby里的find 但是他非常容易报错 除非肯定数据一定存在且只有一条的时候才会用 否则就会报错 可用filter()[0]代替

ps:

使用类似Python对列表进行切片的方法可以对QuerySet进行范围取值。它相当于SQL语句中的LIMIT和OFFSET子句。参考下面的例子:

1
2
>>> Entry.objects.all()[:5]      # 返回前5个对象
>>> Entry.objects.all()[5:10] # 返回第6个到第10个对象

注意:不支持负索引!例如 Entry.objects.all()[-1]是不允许的

通常情况,切片操作会返回一个新的QuerySet,并且不会被立刻执行。但是有一个例外,那就是指定步长的时候,查询操作会立刻在数据库内执行,如下:

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

若要获取单一的对象而不是一个列表(例如,SELECT foo FROM bar LIMIT 1),可以简单地使用first。 不要用索引([0]) 或者 切片 这两种如果数据不存在 会报错

1
>>> Entry.objects.order_by('headline').first()

字段查询

字段查询其实就是filter()、exclude()和get()等方法的关键字参数。 其基本格式是:field__lookuptype=value注意其中是双下划线

字段名 说明
exact 精确匹配
iexact 不区分大小写的精确匹配
contains 包含匹配
icontains 不区分大小写的包含匹配
in 在..之内的匹配
gt 大于
gte 大于等于
lt 小于
lte 小于等于
startswith 从开头匹配
istartswith 不区分大小写从开头匹配
endswith 从结尾处匹配
iendswith 不区分大小写从结尾处匹配
range 范围匹配
date 日期匹配
year 年份
month 月份
day 日期
week 第几周
week_day 周几
time 时间
hour 小时
minute 分钟
second
isnull 判断是否为空
search 1.10中被废弃
regex 区分大小写的正则匹配
iregex 不区分大小写的正则匹配

具体参考:https://www.liujiangblog.com/course/django/132

使用F表达式引用模型的字段

到目前为止的例子中,我们都是将模型字段与常量进行比较。但是,如果你想将模型的一个字段与同一个模型的另外一个字段进行比较该怎么办?

使用Django提供的F表达式!

例如,为了查找comments数目多于pingbacks数目的Entry,可以构造一个F()对象来引用pingback数目,并在查询中使用该F()对象:

1
2
>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

Django支持对F()对象进行加、减、乘、除、取模以及幂运算等算术操作。两个操作数可以是常数和其它F()对象。例如查找comments数目比pingbacks两倍还要多的Entry,我们可以这么写:

1
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)

为了查询rating比pingback和comment数目总和要小的Entry,我们可以这么写:

1
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))

你还可以在F()中使用双下划线来进行跨表查询。例如,查询author的名字与blog名字相同的Entry:

1
>>> Entry.objects.filter(authors__name=F('blog__name'))

对于date和date/time字段,还可以加或减去一个timedelta对象。下面的例子将返回发布时间超过3天后被修改的所有Entry:

1
2
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

F()对象还支持.bitand().bitor().bitrightshift().bitleftshift()4种位操作,例如:

1
>>> F('somefield').bitand(16)

使用Q对象进行复杂查询

可以使用“&”或者“|”或“~”来组合Q对象,分别表示与或非逻辑。它将返回一个新的Q对象。

当关键字参数和Q对象组合使用时,Q对象必须放在前面

1
2
3
4
from django.db.models import Q
Q(question__startswith='Who')|Q(question__startswith='What')
# 这相当于:
WHERE question LIKE 'Who%' OR question LIKE 'What%'

删除对象

删除对象使用的是对象的delete()方法。该方法将返回被删除对象的总数量和一个字典,字典包含了每种被删除对象的类型和该类型的数量。如下所示:

1
2
>>> e.delete()
(1, {'weblog.Entry': 1})

也可以批量删除。每个QuerySet都有一个delete()方法,它能删除该QuerySet的所有成员。例如:

1
2
>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})

需要注意的是,有可能不是每一个对象的delete方法都被执行。如果你改写了delete方法,为了确保对象被删除,你必须手动迭代QuerySet进行逐一删除操作。

当Django删除一个对象时,它默认使用SQL的ON DELETE CASCADE约束,也就是说,任何有外键指向要删除对象的对象将一起被删除。例如:

1
2
3
b = Blog.objects.get(pk=1)
# 下面的动作将删除该条Blog和所有的它关联的Entry对象
b.delete()

这种级联的行为可以通过的ForeignKey的on_delete参数自定义。

注意,delete()是唯一没有在管理器上暴露出来的方法。这是刻意设计的一个安全机制,用来防止你意外地请求类似Entry.objects.delete()的动作,而不慎删除了所有的条目。如果你确实想删除所有的对象,你必须明确地请求一个完全的查询集,像下面这样:

1
Entry.objects.all().delete()

复制模型实例

创建一个新的实例并将原实例的所有字段都拷贝过来。最简单的方法是将原实例的pk设置为None,这会创建一个新的实例copy。示例如下:

1
2
3
4
5
blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1
#
blog.pk = None
blog.save() # blog.pk == 2

但是在使用继承的时候,情况会变得复杂,如果有下面一个Blog的子类:

1
2
3
4
5
class ThemeBlog(Blog):
theme = models.CharField(max_length=200)

django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save() # django_blog.pk == 3

基于继承的工作机制,你必须同时将pk和id设为None:

1
2
3
django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4

批量更新对象

使用update()方法可以批量为QuerySet中所有的对象进行更新操作。

唯一的约束是:只能访问一张数据库表。你可以根据关系字段进行过滤,但你只能更新模型主表的字段

要注意的是update()方法会直接转换成一个SQL语句,并立刻批量执行。它不会运行模型的save()方法,或者产生pre_savepost_save信号(调用save()方法产生)或者服从auto_now字段选项。如果你想保存QuerySet中的每个条目并确保每个实例的save()方法都被调用,你不需要使用任何特殊的函数来处理。只需要迭代它们并调用save()方法

简单的大概这些 其余复杂的可以去看参考链接 还有很多方式

参考:https://www.liujiangblog.com/course/django/129