一、ORM概述

用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。

到目前为止,当我们的程序涉及到数据库相关操作时,一般操作流程如下:

  • 创建数据库,设计表结构和字段;
  • 使用 MySQLdb 来连接数据库,并编写数据访问层代码,使用原生SQL语句进行访问数据;
  • 业务逻辑层去调用数据访问层执行数据库操作,获取结果;

ORM是什么?Object Relational Mapping(关系对象映射)

1、类名------>数据库中的表名

2、类属性--------->数据库的字段

3、类实例--------->数据库表里的一条记录

4、obj.id  obj.name------>获取类实例对象的属性

Django ORM的优势:

1、Django的orm操作本质是根据对接的数据库引擎,翻译成对应的sql语句, 避免新手写sql语句带来的性能问题,同时ORM使我们的通用数据库交互变得简单易行,而且完全不用考虑复杂的SQL语句。

2、所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite....等数据库,如果数据库需要迁移,只需要更换Django的数据库引擎即可;

二、ORM实现

为了更好的理解,我们来做一个基本的 地名(比如某个门牌号码)/餐馆/服务员 数据库结构,这个也是受Django官网的启发而自己定义的,可能不是非常恰当,但是可以帮助理解。

1、梳理关系和建模

场景如下:

在某条街上Place,有一家餐馆Restaurant,在这家餐馆里面有很多服务员Waiter,每个服务员在这条街上可能有多个住处Place,同时,这个Place可能住着多个服务员Waiter。

所以,这其中的关系可以梳理如下:

Place---->Restaurant(一对一):一条街上对应一家餐馆名

Restaurant---->Waiter(一对多):一个餐馆里面有多名服务员

Place---->Waiter(多对多):一个地方可能住着好几个服务员,而一个服务员可能在这条街上有好几个住所

2、定义表和确定字段

一个place有地名和地址

一个餐馆有唯一的地址和经营的餐饮项目

一个服务员有自己的姓名、住所、工作的餐馆

from django.db import models

class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80) def __str__(self): # __unicode__ on Python 2
return "%s the place" % self.name class Restaurant(models.Model):
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
primary_key=True,
)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False) def __str__(self): # __unicode__ on Python 2
return "%s the restaurant" % self.place.name class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
places = models.ManyToManyField(Place)
name = models.CharField(max_length=50) def __str__(self): # __unicode__ on Python 2
return "%s the waiter at %s" % (self.name, self.restaurant)

  

3、setttings和同步数据库

a、在settings里的INSTALLED_APPS中加入'app'

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', ]

  

b、同步数据库。

1、数据同步的准备工作
python manager makemigrations

2、数据同步
python manager migrate

 

 3、数据表结构

三、ORM的关系

关系数据库的威力体现在表之间的相互关联。 Django 提供了三种最常见的数据库关系:多对一(many-to-one),多对多(many-to-many),一对一(one-to-one)。其中多对一和多对多更为常见。

1、多对一(many-to-one)

Django 使用 django.db.models.ForeignKey 定义多对一关系。 和使用其它Field类型一样:在模型当中把它做为一个类属性包含进来。

ForeignKey 需要一个位置参数:与该模型关联的类。记住:哪个是多,就在哪个里面写Foreiginkey。

比如,一个餐馆有多个服务员,但是一个服务员只能在一家餐馆工作,当然这里不考虑兼职的情况了

2、一对一(one-to-one)

OneToOneField用来定义一对一关系。 和使用其它Field类型一样:在模型当中把它做为一个类属性包含进来。

当某个对象想扩展自另一个对象时,最常用的方式就是在这个对象的主键上添加一对一关系。

OneToOneField要一个位置参数:与模型关联的类。

例如,如果你正在建立一个“places”的数据库,那么你将建立一个非常标准的地址、电话号码等 在数据库中。 接下来,如果你想在place数据库的基础上建立一个restaurant数据库,而不想将已有的字段复制到Restaurant模型,那你可以在 Restaurant 添加一个OneToOneField 字段,这个字段指向Place(因为Restaurant 本身就是一个Place;事实上,在处理这个问题的时候,你应该使用一个典型的 inheritance,它隐含一个一对一关系)。

ForeignKey一样,可以定义递归关系,并可以引用尚未定义的模型

3、多对多(many-to-many)

ManyToManyField 用来定义多对多关系, 和使用其它Field类型一样:在模型当中把它做为一个类属性包含进来。

ManyToManyField 需要一个位置参数:和该模型关联的类。

例如,一个Pizza可以有多种Topping 即一种Topping 也可以位于多个Pizza上,而且每个Pizza有多个topping

4、实例

a、我们进行对象的建立,首先进入python 的shell环境,然后导入对应的对象。建立2个place、2个restaurant、4个waiter

place1===>restaurant1(111街上有一间餐馆,名为:restaurant1)

waiter1和waiter2==>restaurant1(waiter1和waiter2工作在restaurant1)

waiter1==>hourse1和hourse2:(waiter1有hourse1和hourse2两个住所

waiter2==>hourse2(waiter2只有hourse2一个住所

place2===>restaurant2(222街上有一间餐馆,名为:restaurant2)

waiter3和waiter4===>restaurant2(waiter3和waiter4工作在restaurant2)

waiter3和waiter4==>hourse3(waiter3和waiter4同居在hourse3

(venv) D:\xuequn\venv\Scripts\firstapp>python manage.py shell
Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AM
D64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from blog import models
>>> from models import Place
Traceback (most recent call last):
File "<console>", line 1, in <module>
ImportError: No module named models
>>> from blog import models
>>> from blog.models import Place
>>> from blog.models import Restaurant
>>> from blog.models import Waiter
>>> place1=Place(name='place1',address='111-place1-west-road')
>>> place1.save()
>>> place2=Place(name='place2',address='222-place2-west-road')
>>> place2.save()
>>> res1=Restaurant(place=place1,serves_hot_dogs=True,serves_pizza=True)
>>> res1.save()
>>> res2=Restaurant(place=place2,serves_hot_dogs=True,serves_pizza=False)
>>> res2.save()
>>> w1=Waiter(name='waiter1',restaurant=res1)
>>> w1.save()
>>> w2=Waiter(name='waiter2',restaurant=res1)
>>>
>>> w2.save()
>>> w3=Waiter(name='waiter3',restaurant=res2)
>>> w3.save()
>>> w4=Waiter(name='waiter4',restaurant=res2)
>>> w4.save()
>>> hourse1=Place(name='hourse1',address='hourse1-east-road')
>>> hourse1.save()
>>> hourse2=Place(name='hourse2',address='hourse2-east-road')
>>> hourse2.save()
>>> hourse3=Place(name='hourse3',address='hourse3-east-road')
>>> hourse3.save()
>>> w1.places.add(hourse1)
>>> w1.save()
>>> w1.places.add(hourse2)
>>> w1.save()
>>> w2.places.add(hourse2)
>>> w2.save()
>>> w3.places.add(hourse3)
>>> w3.save()
>>> w4.places.add(hourse3)
>>> w4.save()
>>>

  

注意:

1、一对一使用场景:当某个对象想扩展自另一个对象时,最常用的方式就是在这个对象的主键上添加一对一关系。这里的餐馆扩展了街道,餐馆除了有街道的地址属性以外,还有餐馆名称和经营项目等。

2、表A是表B的属性时,表A必须先save,表B才能使用表A,不然会报错:ValueError: save() prohibited to prevent data loss due to unsaved related object......

下面在此基础上进行增、删、改、查操作。

四、ORM的操作

1、增

在学习增操作前,我们先了解一下模型中常用的字段。

#################################################################常用字段

<1> CharField

 字符串字段, 用于较短的字符串.
CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.

<2> IntegerField

 用于保存一个整数.

<3> FloatField

一个浮点数. 必须 提供两个参数:
参数 描述
max_digits 总位数(不包括小数点和符号)
decimal_places 小数位数 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
models.FloatField(..., max_digits=5, decimal_places=2) 要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
models.FloatField(..., max_digits=19, decimal_places=10)
admin 用一个文本框(<input type="text">)表示该字段保存的数据.

<4> AutoField

 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段;
自定义一个主键:my_id=models.AutoField(primary_key=True)
如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.

<5> BooleanField

 A true/false field. admin 用 checkbox 来表示此类字段.

<6> TextField

 一个容量很大的文本字段.
admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框).

<7> EmailField

一个带有检查Email合法性的 CharField,不接受 maxlength 参数. 

<8> DateField

一个日期字段. 共有下列额外的可选参数:
参数 描述
auto_now 当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳.
auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间.(仅仅在admin中有意义...)

<9> DateTimeField

一个日期时间字段. 类似 DateField 支持同样的附加选项.

<10> ImageField

类似 FileField, 不过要校验上传对象是否是一个合法图片.

它有两个可选参数:height_field和width_field,
如果提供这两个参数,则图片将按提供的高度和宽度规格保存.

<11> FileField

 # 一个文件上传字段.
#要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting,
#该格式将被上载文件的 date/time
#替换(so that uploaded files don't fill up the given directory).
# admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) . #注意:在一个 model 中使用 FileField 或 ImageField 需要以下步骤:
#(1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件.
# (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对
# WEB服务器用户帐号是可写的.
#(2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django
# 使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT).
# 出于习惯你一定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来说,如果你的 ImageField
# 叫作 mug_shot, 你就可以在模板中以 {{ object.#get_mug_shot_url }} 这样的方式得到图像的绝对路径.

<12> URLField

用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且 没有返回404响应).
admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框)

<13> NullBooleanField

 类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项
admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据.

<14> SlugField

 # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们通常用于URLs
# 若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. #在
# 以前的 Django 版本,没有任何办法改变50 这个长度.这暗示了 db_index=True.
# 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate
# the slug, via JavaScript,in the object's admin form: models.SlugField
# (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields.

<15> XMLField

 #一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径.

<16> FilePathField

 # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的.
# 参数 描述
# path 必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目.
# Example: "/home/images".
# match 可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名.
# 注意这个正则表达式只会应用到 base filename 而不是
# 路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif.
# recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录.
# 这三个参数可以同时使用.
# match 仅应用于 base filename, 而不是路径全名. 那么,这个例子:
# FilePathField(path="/home/images", match="foo.*", recursive=True)
# ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif

<17> IPAddressField

一个字符串形式的 IP 地址, (i.e. "24.124.1.30").

<18>CommaSeparatedIntegerField

用于存放逗号分隔的整数值. 类似 CharField, 必须要有maxlength参数.

  

#######################################################Field重要参数

<1> null

数据库中字段是否可以为空

<2> blank

django的 Admin 中添加数据时是否可允许空值

<3> default

设定缺省值

<4> editable

如果为假,admin模式下将不能改写。缺省为真

<5> primary_key

设置主键,如果没有设置django创建表时会自动加上:
id = meta.AutoField('ID', primary_key=True)
primary_key=True implies blank=False, null=False and unique=True. Only one
primary key is allowed on an object.

<6> unique

数据唯一

<7> verbose_name

 Admin中字段的显示名称

<8> validator_list

有效性检查。非有效产生 django.core.validators.ValidationError 错误

<9>db_column

db_index 如果为真将为此字段创建索引

<10>choices

一个用来选择值的2维元组。第一个值是实际存储的值,第二个用来方便进行选择。
如:
SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),)
gender = models.CharField(max_length=2,choices = SEX_CHOICES)

-------------------------------------增操作(create方式,无需显示save) ------------------------------

>>> from blog.models import *
>>> Place.objects.create(name='place3',address='333-place3-west-road')
<Place: place3 the place>
>>> Place.objects.create(**{'name':'place4','address':'444-place4-west-road'})
<Place: place4 the place>

  

-------------------------------------增操作(显示save方式) ------------------------------

>>> place1=Place(name='place1',address='111-place1-west-road')
>>> place1.save()
>>> place2=Place(name='place2',address='222-place2-west-road')
>>> place2.save()

2、删

-------------------------------------删操作(delete方法) ------------------------------

>>> p3=Place.objects.filter(name='place3')
>>> p3
<QuerySet [<Place: place3 the place>]>
>>> p3=Place.objects.filter(name='place3').delete()

  

------------------------------删操作( remove()和clear()方法) --------------

book = models.Book.objects.filter(id=1)

book.author.clear()        #清空与book中id=1 关联的所有数据
book.author.remove(2) #可以为id
book.author.remove(*[1,2,3,4]) #可以为列表,前面加* #反向
author = models.Author.objects.filter(id=1)
author.book_set.clear() #清空与boy中id=1 关联的所有数据

  

3、改

----------------------------------------改(直接修改属性后save) ----------------------------------------

>>> h3=Place.objects.get(id=9)
>>> h3
<Place: hourse3 the place>
>>> h3.address='hourse3-west-road'
>>> h3.save()
>>>

  这种方法需要知道被修改内容的ID号。

----------------------------------------改(级联update方法) ----------------------------------------

>>> Place.objects.filter(id=9).update(address='new_place')
1
>>> h3=Place.objects.get(id=9)
>>> h3.address
u'new_place'
>>>

   第二种方式修改不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象(filter里面的条件可能有多个条件符合,比如name='alvin',可能有两个name='alvin'的行数据)。

在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。

一、update方法直接设置对应的属性
#---------------- update方法直接设定对应属性----------------
models.Book.objects.filter(id=3).update(title="PHP")
##sql:
##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3) 二、save方法会把所有属性都重新设定一遍
#--------------- save方法会将所有属性重新设定一遍,效率低-----------
obj=models.Book.objects.filter(id=3)[0]
obj.title="Python"
obj.save() 1、先查出所有数据
# SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price",
# "app01_book"."color", "app01_book"."page_num",
# "app01_book"."publisher_id" FROM "app01_book" WHERE "app01_book"."id" = 3 LIMIT 1;
2、把所有字段都更新一次
# UPDATE "app01_book" SET "title" = 'Python', "price" = 3333, "color" = 'red', "page_num" = 556,
# "publisher_id" = 1 WHERE "app01_book"."id" = 3;

  

在这个例子里我们可以看到Django的save()方法更新了不仅仅是title列的值,还有更新了所有的列。 若title以外的列有可能会被其他的进程所改动的情况下,只更改title列显然是更加明智的。更改某一指定的列,我们可以调用结果集(QuerySet)对象的update()方法,与之等同的SQL语句变得更高效,并且不会引起竞态条件。

此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。

注意,这里因为update返回的是一个整形,所以没法用query属性;对于每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分:

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
} LOGGING

  注意:如果是多对多的改:

    obj=Book.objects.filter(id=1)[0]
author=Author.objects.filter(id__gt=2) obj.author.clear()
obj.author.add(*author)

  

4、查

---------------------------------------查(filter,value等) -------------------------------------

# 查询相关API:
# <1>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
# <2>all(): 查询所有结果
# <3>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
#-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()--------
# <4>values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列
# <5>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
# <6>order_by(*field): 对查询结果排序
# <7>reverse(): 对查询结果反向排序
# <8>distinct(): 从返回结果中剔除重复纪录
# <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
# <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。
# <11>first(): 返回第一条记录
# <12>last(): 返回最后一条记录
# <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False。

  

扩展:

#扩展查询,有时候DJANGO的查询API不能方便的设置查询条件,提供了另外的扩展查询方法extra:
#extra(select=None, where=None, params=None, tables=None,order_by=None, select_params=None (1) Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
(2) Blog.objects.extra(
select=SortedDict([('a', '%s'), ('b', '%s')]),
select_params=('one', 'two')) (3) q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
q = q.extra(order_by = ['-is_recent']) (4) Entry.objects.extra(where=['headline=%s'], params=['Lennon'])

  

五、惰性机制

所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。其实就是迭代器。

1、QuerySet特点

<1>  可迭代的

<2>  可切片

#objs=models.Book.objects.all()#[obj1,obj2,ob3...]

    #QuerySet:   可迭代

    # for obj in objs:#每一obj就是一个行对象
# print("obj:",obj)
# QuerySet: 可切片 # print(objs[1])
# print(objs[1:4])
# print(objs[::-1])

  

2、QuerySet的高效使用

<1>Django的queryset是惰性的

     Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得
到数据库中名字为‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave")
上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数,
这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。 <2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql.
为了验证这些,需要在settings里加入 LOGGING(验证方式)
obj=models.Book.objects.filter(id=3)
# for i in obj:
# print(i) # if obj:
# print("ok") <3>queryset是具有cache的
当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行
(evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,
你不需要重复运行通用的查询。
obj=models.Book.objects.filter(id=3) # for i in obj:
# print(i)
## models.Book.objects.filter(id=3).update(title="GO")
## obj_new=models.Book.objects.filter(id=3)
# for i in obj:
# print(i) #LOGGING只会打印一次 <4>简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些数据!为了避免这个,可以用exists()方法来检查是否有数据: obj = Book.objects.filter(id=4)
# exists()的检查可以避免数据放入queryset的cache。
if obj.exists():
print("hello world!") <5>当queryset非常巨大时,cache会成为问题 处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统
进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法
来获取数据,处理完数据就将其丢弃。
objs = Book.objects.all().iterator()
# iterator()可以一次只从数据库获取少量数据,这样可以节省内存
for obj in objs:
print(obj.name)
#BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
for obj in objs:
print(obj.name) #当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使
#用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询 总结:
queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。
使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能
会造成额外的数据库查询。

  

六、多种查询

1、对象查询,单表条件查询,多表条件关联查询

#--------------------对象形式的查找--------------------------
# 正向查找
ret1=models.Book.objects.first()
print(ret1.title)
print(ret1.price)
print(ret1.publisher)
print(ret1.publisher.name) #因为一对多的关系所以ret1.publisher是一个对象,而不是一个queryset集合 # 反向查找
ret2=models.Publish.objects.last()
print(ret2.name)
print(ret2.city)
#如何拿到与它绑定的Book对象呢?
print(ret2.book_set.all()) #ret2.book_set是一个queryset集合 #---------------了不起的双下划线(__)之单表条件查询---------------- # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
#
# models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
# models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
#
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
#
# models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and
#
# startswith,istartswith, endswith, iendswith, #----------------了不起的双下划线(__)之多表条件关联查询--------------- # 正向查找(条件) # ret3=models.Book.objects.filter(title='Python').values('id')
# print(ret3)#[{'id': 1}] #正向查找(条件)之一对多 ret4=models.Book.objects.filter(title='Python').values('publisher__city')
print(ret4) #[{'publisher__city': '北京'}] #正向查找(条件)之多对多
ret5=models.Book.objects.filter(title='Python').values('author__name')
print(ret5)
ret6=models.Book.objects.filter(author__name="alex").values('title')
print(ret6) #注意
#正向查找的publisher__city或者author__name中的publisher,author是book表中绑定的字段
#一对多和多对多在这里用法没区别 # 反向查找(条件) #反向查找之一对多:
ret8=models.Publisher.objects.filter(book__title='Python').values('name')
print(ret8)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的关联表名 ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors')
print(ret9)#[{'book__authors': 1}, {'book__authors': 2}] #反向查找之多对多:
ret10=models.Author.objects.filter(book__title='Python').values('name')
print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}] #注意
#正向查找的book__title中的book是表名Book
#一对多和多对多在这里用法没区别

  注意:条件查询即与对象查询对应,是指在filter,values等方法中的通过__来明确查询条件

2、聚合查询和分组查询

<1> aggregate(*args,**kwargs):

通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。(对QuerySet整体进行计算,得到聚合函数的值的字典)

from django.db.models import Avg,Min,Sum,Max

从整个查询集生成统计值。比如,你想要计算所有在售书的平均价钱。Django的查询语法提供了一种方式描述所有
图书的集合。 >>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35} aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值 aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的
标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定
一个名称,可以向聚合子句提供它:
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35} 如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

  

<2> annotate(*args,**kwargs) 

 可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。(对查询结果按条件分组后的集合进行计算,返回每个分组的字典集合)

>>> Place.objects.filter(name='place1').aggregate(Sum('id'))
(0.001) SELECT SUM("blog_place"."id") AS "id__sum" FROM "blog_place" WHERE "blog
_place"."name" = 'place1'; args=('place1',)

{'id__sum': 5}
>>>

查询每个地址的id总和,这里就涉及到分组了(当然,这里不是很合理,一般是某个name对应多个id值时,才使用相加),分组条件是name

>>> Place.objects.values("name").annotate(Sum('id'));
(0.001) SELECT "blog_place"."name", SUM("blog_place"."id") AS "id__sum" FROM "bl
og_place" GROUP BY "blog_place"."name" LIMIT 21; args=()

<QuerySet [{'name': u'hourse1', 'id__sum': 7}, {'name': u'hourse2', 'id__sum': 8
}, {'name': u'hourse3', 'id__sum': 9}, {'name': u'place1', 'id__sum': 5}, {'name
': u'place2', 'id__sum': 6}]>
>>>

查询每个place,最小的id

>>> Place.objects.values('name').annotate(Min('id'));
(0.001) SELECT "blog_place"."name", MIN("blog_place"."id") AS "id__min" FROM "bl
og_place" GROUP BY "blog_place"."name" LIMIT 21; args=()

<QuerySet [{'id__min': 7, 'name': u'hourse1'}, {'id__min': 8, 'name': u'hourse2'
}, {'id__min': 9, 'name': u'hourse3'}, {'id__min': 5, 'name': u'place1'}, {'id__
min': 6, 'name': u'place2'}]>
>>>

  
注意:这里是因为settings里面设置了Logging选项,所以你会看到每次查询的raw SQL。

3、F查询和Q查询

仅仅靠单一的关键字参数查询已经很难满足查询要求。此时Django为我们提供了F和Q查询。

# F 使用查询条件的值,专门取对象中某列值的操作(每本书的价格提高20元)

    # from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1) # Q 构建搜索条件(与、或、非、条件,非常灵活!!!)
from django.db.models import Q #1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询
q1=models.Book.objects.filter(Q(title__startswith='P')).all()
print(q1)#[<Book: Python>, <Book: Perl>] # 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。
Q(title__startswith='P') | Q(title__startswith='J') # 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合
Q(title__startswith='P') | ~Q(pub_date__year=2005) # 4、应用范围: # Each lookup function that takes keyword-arguments (e.g. filter(),
# exclude(), get()) can also be passed one or more Q objects as
# positional (not-named) arguments. If you provide multiple Q object
# arguments to a lookup function, the arguments will be “AND”ed
# together. For example: Book.objects.get(
Q(title__startswith='P'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
) #sql:
# SELECT * from polls WHERE question LIKE 'P%'
# AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') # import datetime
# e=datetime.date(2005,5,6) #2005-05-06 # 5、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。
# 正确:
Book.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
title__startswith='P')
# 错误:和函数一样,关键字参数必须放后面!!
Book.objects.get(
question__startswith='P',
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

 

七、raw sql

django中models的操作,也是调用了ORM框架来实现的,pymysql 或者mysqldb,所以我们也可以使用原生的SQL语句来操作数据库!

九、Django之ORM的更多相关文章

  1. Django框架(九) Django之ORM常用字段和参数

    ORM字段 AutoField int自增列,必须填入参数 primary_key=True.当model中如果没有自增列,则自动会创建一个列名为id的列. IntegerField 一个整数类型,范 ...

  2. 第十九章 Django的ORM映射机制

    第十九章 Django的ORM映射机制 第一课 Django获取多个数据以及文件上传 1.获取多选的结果(checkbox,select/option)时: req.POST.getlist('fav ...

  3. Django之ORM操作

    Django之ORM操作 前言 Django框架功能齐全自带数据库操作功能,本文主要介绍Django的ORM框架 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计 ...

  4. python——Django(ORM连表操作)

    千呼万唤始出来~~~当当当,终于系统讲了django的ORM操作啦!!!这里记录的是django操作数据库表一对多.多对多的表创建及操作.对于操作,我们只记录连表相关的内容,介绍增加数据和查找数据,因 ...

  5. Python’s SQLAlchemy vs Other ORMs[转发 3]Django's ORM

    Django's ORM Django is a free and open source web application framework whose ORM is built tightly i ...

  6. 将SALT_STACK的JOB-CACHE放到数据库中,而建库用DJANGO的ORM完成

    下面包括了SALT_MASTER的配置,及DJANGO的ORM里更改默认表名称,更改默认字段名称(里面有个RETURN),更改默认ID索引... 一个下午有和它磨来磨去... 感谢鹏龙,感谢高远..: ...

  7. Django:之ORM、CMS和二维码生成

    Django ORM Django 的数据库接口非常好用,我们甚至不需要知道SQL语句如何书写,就可以轻松地查询,创建一些内容,所以有时候想,在其它的地方使用Django的 ORM呢?它有这么丰富的 ...

  8. Django的ORM实现数据库事务操作

    在Django中实现数据库的事务操作 在学习MySQL数据库时,MySQL数据库是支持原子操作的. 什么是数据库的原子操作呢??打个比方,一个消费者在一个商户里刷信用卡消费. 交易正常时,银行在消费者 ...

  9. Django之ORM基础

    ORM简介 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单的说,ORM是通过使用描述 ...

随机推荐

  1. hdu 1874 Dijkstra算法

    先贴个网上找的比较通俗易懂的教程: 2.1Dijkstra算法(非负权,使用于有向图和无向图) Dijkstra算法是典型最短路算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心 ...

  2. Level/levelup-1-简介

    https://github.com/Level/levelup A node.js wrapper for abstract-leveldown compliant stores 一个为实现抽象le ...

  3. Kubernetes 学习2 k8s基础概念

    一.架构描述 1.基本架构 2.pod ,有两类 a.自主式pod 自我管理的,创建之后,任然是需要提交给API Server,API Server接受之后然后由调度器调度到指定的 node节点,由n ...

  4. 《Python核心编程》第二版第三章答案

    本人python新手,答案自己做的,如果有问题,欢迎大家评论和讨论! 更新会在本随笔中直接更新. 我在Windows使用python版本是2.7.0 3–10. 异常.使用类似readTextFile ...

  5. PAT——1066. 图像过滤

    图像过滤是把图像中不重要的像素都染成背景色,使得重要部分被凸显出来.现给定一幅黑白图像,要求你将灰度值位于某指定区间内的所有像素颜色都用一种指定的颜色替换. 输入格式: 输入在第一行给出一幅图像的分辨 ...

  6. HomeKit 开发指南(中文版)

    转载自cocoachina 本文由CocoaChina翻译组成员iBenjamin_Go和浅夏@旧时光翻译自苹果开发文档:HomeKit Developer Guide,敬请勘误. 本文档内容包括 第 ...

  7. 精读 SBAR SDN flow-Based monitoring and Application Recognition

    目录 架构 测量模块 分类模块 具体实现 实验:最后接入巴塞罗那的校园网流量测试: SBAR: SDN flow-Based monitoring and Application Recognitio ...

  8. .net core 实践笔记(三)--封装底层

    前言: 有了前面的工作,简单的架子基本搭建起来了,因为条件有限,只能先测试SqlServer的了,源码放出来,也希望有兴趣的伙伴可以一起改善,相信可以成为未来进阶架构师的第一步,自己有小项目的时候可以 ...

  9. SFTP 服务搭建

    1. 介绍 sftp是Secure File Transfer Protocol的缩写,安全文件传送协议.可以为传输文件提供一种安全的加密方法.sftp 与 ftp 有着几乎一样的语法和功能.SFTP ...

  10. MySql多表关联,根据某列取前N条记录问题

    近来遇到一个问题:“MySql多表关联,根据某列取前N条记录”. 刚开始一直在想,SQL语句是否可以做到直接查询出来,但几经折磨,还是没能写出SQL语句,-------如果有大牛的话,望指点迷津.我把 ...