Django之ORM

Posted banbosuiyue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django之ORM相关的知识,希望对你有一定的参考价值。

ORM对象关系映射,Django中用于操作数据库的Python语句,并且每一条ORM语句,都可以翻译成一条sql语句,而且是符合sql语句优化原则的语句

 技术图片

 

0 创建表之前的准备工作

(1) 先在终端下的mysql中创建一个数据库。

create database db_book charset=utf8mb4;

(2) pycharm界面中连接数据库

Database中的+-> Data Source -> MySQL -> 填写Name(@localhost), User(root), Password(xxx), Database(db_book) -> Test Connection -> 下面出现对勾,就代表连接成功

(3) 在主应用drfdemo中的__init__.py中设置使用pymysql作为数据库驱动,写下如下代码

import pymysql
pymysql.install_as_MySQLdb()

 

(4) settings.py配置文件中设置mysql的账号密码

DATABASES = {

    # ‘default‘: {

    #     ‘ENGINE‘: ‘django.db.backends.sqlite3‘,

    #     ‘NAME‘: os.path.join(BASE_DIR, ‘db.sqlite3‘),

    # }

    default: {

        ENGINE: django.db.backends.mysql,

        NAME: "db_book", #db_book是我们用的数据库的库名

        "HOST": "127.0.0.1",

        "PORT": 3306,

        "USER": "root",

        "PASSWORD": "xxx",

    },

}

 

下面进入正题

  针对单表的创建,增删改查

1.1 models.py创建单表

class Publish(models.Model):

    name = models.CharField(max_length=32)

    email = models.CharField(max_length=32)

    addr = models.CharField(max_length=32)

 

1.2

Python终端Terminal中执行命令

迁移数据库

python manage.py makemigration

在数据库中创建表

python manage.py migrate

这个时候就可以在pycharm中的Database中看到models.py中我们要创建的表了

 

添加数据

(1) 手动在数据库里插入就不说了,实际项目中一般是前端提交过来的数据调用views.py中的方法执行增删改查操作

(2) view.py文件中写插入表的语句

# 添加记录方式一 (实例化对象)  一条记录就对应一个对象

此方法分两步,第一步对Book模型类进行实例化得到book_obj模型类对象,第二步执行book_obj对象.save()方法进行数据保存

book_obj=Book(title="java",pub_data="2012-12-12",prict=120,publish="西瓜出版社")

book_obj.save()

# 添加记录方式二 (objects.create方法)

book=Book.objects.create(title="python",pub_data="2019-12-30",prict=666,publish="香蕉出版社")

# 这里的book就对应着一条记录的模型类对象, book都能点出来表中字段的值 就是对象 . 属性

 

 

QuerySet数据类型的概念

QuerySet数据类型是针对Django创建的一种序列数据类型,是类似列表的数据类型,不过列表里面可以放任意数据类型(包括放对象,函数等等),QuerySet数据类型有两种形式

第一种 类似列表里面装模型类对象的形式

所以这种QuerySet序列可索引取值(取出的值就是一个个的模型类对象),可切片,for i in遍历等等

(1)以下列出几种返回第一种QuerySet序列的查询方法

ret1=Book.objects.all()

print(ret1)

# 打印结果 <QuerySet [<Book: Book object (16)>, <Book: Book object (17)>,...

ret2=Book.objects.filter(title="小黄书")

print(ret2) # 注意就算filter过滤出的数据只有一条,这一条也是装在QuerySet序列里的

# 打印结果 <QuerySet [<Book: Book object (19)>, <Book: Book object (20)>]>

(2)下列几种返回模型类对象的查询方法

book_obj=Book.objects.get(title="红楼梦")

print(book_obj) # 打印结果Book object (22)

ret2=Book.objects.filter(title="小黄书").first()

# 上面一句话等同于Book.objects.filter(title="小黄书")[0]

print(ret2) # 打印结果Book object (19)

可以看出,查询多条数据的查询方式一般就返回的是QuerySet序列,而查询一条数据的查询方式,返回的就是模型类对象.

模型类就是models.py中建表的类,比如Book类中有title属性,对应的就是book表中有title字段,并且由模型类查询得到的模型类对象,都可以点出其中的属性,比如book_obj=Book.objects.get(id=16)

print(book_obj.title) # 打印出书名,异形

print(book_obj.price) # 打印出价格,32

...

 

第二种 类似列表里面套字典的形式

这种QuerySet序列是通过.values()方法查询出来的返回的数据类型,能够使用字典的相关操作方法

只有QuerySet序列才有.values()方法,用于取到自己想要的字段,模型类对象没有.values()方法

使用.values()方法,你需要传参字段,返回一个特殊的QuerySet序列,这个序列中装着的是字典,字典中的键值对是 字段:字段值的形式,比如说values(“name”,”age”)中的参数相当于sql语句select name,age from table1 中的nameage,所以这个QuerySet序列通过遍历之后,能通过键取出nameage,例如:

ret=Book.objects.filter(title="小黄书").values("pub_date","price")

print(ret)

# <QuerySet [{‘pub_date‘: datetime.date(2019, 12, 7), ‘price‘: Decimal(‘666.00‘)}, {‘pub_date‘: datetime.date(2019, 12, 28), ‘price‘: Decimal(‘666.00‘)}]>

for i in ret:

print(“%s,%s”%(i["price"],i[“pub_date”]) )

# 666.00, 2019-12-07

# 666.00, 2019-12-28

 

3 单表查询

除了添加表记录,删和改都得通过查询到这条表记录,再执行对应的删或改方法就行

3.1 直接查询

Book.objects.可以.出来以下一些方法(Book就是models.py中的模型类)

<1> all():                  查询所有结果

<2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象(其中**kwargs代表关键字形参,比如说有个字段名叫name,那么查询namexiaoming的参数就传name=”xiaoming”),可以传多个关键字形参,代表同时满足多个条件,用逗号隔开,返回QuerySet序列

<3> get(**kwargs):          返回与所给筛选条件相匹配的模型类对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误

<4> exclude(**kwargs):      它包含了与所给筛选条件不匹配的对象

<5> order_by(*field):       对查询结果排序

<6> reverse():              对查询结果反向排序

<8> count():                返回数据库中匹配查询(QuerySet)的对象数量。

<9> first():                返回第一条记录

<10> last():                返回最后一条记录

<11> exists():              如果QuerySet包含数据,就返回True,否则返回False

<12> values(*field):        返回一个特殊的QuerySet序列,详情见QuerySet数据类型的介绍

<13> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

<14> distinct():            从返回结果中剔除重复纪录

 

3.2 基于双下划线的模糊查询

Book.objects.filter(price__in=[100,200,300])

Book.objects.filter(price__gt=100)

Book.objects.filter(price__lt=100)

Book.objects.filter(price__range=[100,200])

Book.objects.filter(title__contains="python")

Book.objects.filter(title__icontains="python")

Book.objects.filter(title__startswith="py")

Book.objects.filter(pub_date__year=2012)

 

4 单表删除数据

book=Book.objects.get(pk=1)

book.delete()

合起来写就是Book.objects.get(pk=1).delete()

 delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Entry.objects.delete() 方法导致 所有的 记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用:

Entry.objects.all().delete()  

如果不想级联删除,可以设置为:

pubHouse = models.ForeignKey(to=Publisher, on_delete=models.SET_NULL, blank=True, null=True)

 

5 单表修改数据

Book.objects.filter(title__startswith="py").update(price=120)

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

 

针对多表的创建,增删改查

1.1 models.py文件中写建表语句 也就是写一个Book类 使用命令迁移后就是一个book,代码如下

from django.db import models

 

# Create your models here.

class Book(models.Model):

    id = models.AutoField(primary_key=True)

    title = models.CharField(max_length=32)

    pub_date = models.DateField()

    price = models.DecimalField(max_digits=8, decimal_places=2)

    publisher = models.ForeignKey(to="Publish",to_field="pid", on_delete=models.CASCADE)

    authors=models.ManyToManyField("Author")

    def __str__(self):

        return self.title

 

class Publish(models.Model):

pid=models.AutoField(primary_key=True)

    name = models.CharField(max_length=32)

    email = models.CharField(max_length=32)

    addr = models.CharField(max_length=32)

 

    def __str__(self):

        return self.name

 

class Author(models.Model):

    name=models.CharField(max_length=32)

    age=models.IntegerField()

    ad=models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)

 

class AuthorDetail(models.Model):

    tel=models.CharField(max_length=32)

    email=models.CharField(max_length=32)

 

class User(models.Model):

    name=models.CharField(max_length=32)

pwd=models.CharField(max_length=32)

其中

to_field="pid" 表示这个外键关联另一张表的pid,对应的,另一张表建表时需要将pid作为主键;如果不加这句话,表示这个外键将自动关联另一张表的主键id,默认自动创建,建表时不用写这个id字段

on_delete=models.CASCADE 联级删除

ManyToManyField 多对多关系,一个字段主动关联多对多关系后,Django自动生成一张多对多关系表.比如上述book表和author表为多对多关联,Django默认创建一张book_author,用来关联两张表的主键,形成多对多关系

OneToOneField 一对一关系

ForeignKey 一对多直接使用外键

AutoField 自动设置主键

booleanField 布尔型

DecimalField 浮点型

 

# 表格详情图(进行数据查询时看着图写比较清晰)

 技术图片

 

1.2 创建3类关联表

pycharm中每创建一张表都会有一个主键,如果你不加,他会默认加一个id字段作为主键

models中创建多张关系表

1) 多对一:(在多的那张表中设置外键进行多对一关联)

publisher=models.ForeignKey(to="Publish", on_delete=models.CASCADE)

#在多的那个表中的字段publisher,设置为外键关联Publish(也就是Publish)

# 级联删除:models.CASCADE 当关联表中的数据删除时,该外键也删除

# 置空:models.SET_NULL 当关联表中的数据删除时,该外键置空,当然,你的这个外键字段得允许为空,null=True

# 设置默认值:models.SET_DEFAULT 删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。

2) 一对一:(OneToOneField)

models中创建一对一外键关系表,就是在多对一的基础上,在原来多的那个表添加一个unique唯一约束,就是一对一了.models中简化了操作,OneToOneField方法

class Author(models.Model):

...

    ad=models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)

 

使用OneToOneField之后,会将此表中的ad字段自动设置成unique唯一约束,并作为外键(并改名为ad_id作为数据库中的字段)关联AuthorDetail这张表的主键,这样形成一对一关联

3) 多对多:(ManyToManyField)

在两张表里分别设置两个外键,然后分别关联(新创建的)第三张表的两个字段,ORM中自动创建第三张关系表,第三张表的表名是  子应用名_这个类名_这个字段名,也就是home_book_authors,这张表中的两个字段book_idauthor_id多对多一一对应

class Book(models.Model):

...

authors=models.ManyToManyField("Author")

# Author是多对多的另一张表

 

2 多表添加记录

2.1 一对多添加

方式1:

# 先在publish表里利用出版社id获取出版社对象

publish_obj=Publish.objects.get(nid=1)

# 再在book表中添加一条数据,book_obj.publishr正向查询按字段,按照book表中的publishr字段,也就是给publish表添加了一个模型类对象publish_obj,即添加了一条数据(因为book_obj.publishr可以点出来publish表中的各个字段)

book_obj=Book.objects.create(title="西游记,price=100,

publishDate="2012-12-12",publishr=publish_obj)

# 多个book对应一个publish(出版社)

 

方式2:(更简便)

   book_obj=Book.objects.create(title="西游记",publishDate="2012-12-12"

,price=100,publishr_id=1) 

# 直接给主动关联多对一的那个字段赋值,完成添加操作

 

2.2 一对一添加

同上一对多,一模一样

2.3 多对多添加

    # 当前生成的书籍对象

book_obj=Book.objects.create(title="追风筝的人",price=200,

publishDate="2012-11-12",publishr_id=1)

# 为书籍绑定的做作者对象

listvar1=[“yuan”,”alex”] #前端传过来的两个作者

listvar2=[]

for i in listvar1:

     j=Author.objects.filter(name=i).first() # 在Author表中作者叫yuan的纪录

listvar2.append(j)

    # 绑定多对多关系,即向关系表book_authors中添加纪录

book_obj.authors.add(*listvar2)    #  将某些特定的 model 对象添加到被关联对象集合中 book_obj.authors.add(*[])

这里好像也可以这样写

listvar2=Author.objects.filter(name_in=listvar1)

book_obj.authors.add(*listvar2)

 

(当然现在前端提交过来的是名字,如果你让前端提交过来的直接是作者对应的author_id),这样简单的操作就添加完成了)

authors_id = request.POST.getlist("authors") #前端传过来作者id列表

book=Book.objects.create(title=title, price=price, pub_date=pub_date, publisher_id=publish_id)

book.authors.add(*authors_id)

# *author_id解包多个普通形参列表,相当于传入多个普通形参

 

 

3 多表修改记录

# 先添加其他字段

Book.objects.filter(id=pk).update(title=title, price=price, pub_date=pub_date, publisher_id=publish_id)

# 再添加多对多字段

book_obj=Book.objects.filter(id=pk).first()

book_obj.authors.clear()

book_obj.authors.add(*authors_id)

# 上面两行相当于 book_obj.authors.set(authors_id) #set后面需要传参列表

 

4 多表查询

参考博客园:

cnblogs.com/yuanchenqi/articles/8963244.html

口诀:  正向查询按字段,反向查询按表名(的小写)

正反向查询都是在两张表是关联表的基础上应用的,models.py中可以看出,比如说A表和B表关联,A表为主动关联的一方(从建表语句就可以看出),则由A表查B表为正向查询,BA则为反向查询

  

图示:

                     正向查询

Book() ----------------------------> Publish()

         <----------------------------

                     反向查询

 

                     正向查询

Author() ---------------------------> AuthorDetail()

           <---------------------------

                     反向查询

 

                     正向查询

Book() -----------------------------> Author()

         <-----------------------------

                     反向查询

 

3.1 基于对象的跨表查询(子查询) 模型类对象.字段/表名.属性值

正向查询按字段 (publishr)

1 查询主键为1的书籍所对应的出版社名字

book_obj=Book.objects.filter(pk=1).first()

print(book_obj.publishr.name)  # book_obj.publishr 是主键为1的书籍对象关联的出版社对象,然后获取到publish表中的name字段对应的值

2 查出红楼梦的所有作者的名字和年龄

# 正向查询按字段,那应该是book表的哪个字段呢,看book表的建表语句authors=models.ManyToManyField("Author") # 这个就是Book类中主动多对多关联的字段,用的就是这个字段

book=Book.objects.filter(title="红楼梦").first()

print(book.authors.all().values("name","age")) # <QuerySet [{‘name‘: ‘alex‘, ‘age‘: 345}, {‘name‘: ‘egon‘, ‘age‘: 324}]>

 

反向查询按表名 (book)

查询苹果出版社出版的所有书籍的名称

publish=Publish.objects.get(name="苹果出版社")

# publish.book.all()  与苹果出版社关联的所有书籍对象集合

book_list=publish.book.all()

for book_obj in book_list:

       print(book_obj.title)

 

2 查询人民出版社出版社所有书籍的名称和价格

pub_obj=Publish.objects.filter(name="人民出版社").first()

print(pub_obj.book_set.all().values("title","price")) # <QuerySet [{‘title‘: ‘红楼梦‘, ‘price‘: Decimal(‘122.00‘)}, {‘title‘: ‘水浒传‘, ‘price‘: Decimal(‘333.00‘)}]>

# 反向查询应该按表名Book的小写也就是book,那为什么要用book_set,因为这里一个出版社对应多本书,所以由出版社查书,查询的时候会出现多个book对象符合条件,所以这里规定要用book_set,而不是book,当你这里非要写book的时候会报错

AttributeError: Author object has no attribute book

所以,当你通过一对一关系查询的时候,直接用表名/字段名;当你通过一对多或者多对多,查多的这个表时,要用表名/字段名加上”_set”

 

3 alex出版过得所有书籍的名称和价格

alex_obj=Author.objects.filter(name="alex").first()

print(alex_obj.book_set.all().values("title","price"))

 

3.2 基于双下划线的跨表查询 (jion联表查询),联表之后随便写,遵循规则就行

同样遵循正向查询按字段,反向查询按表名

例一 三国演义这本书籍的出版社的邮箱

方法一

# 查的是Book,然后正向查询按字段publisher,然后publish__email跨到publish表拿到email

ret=Book.objects.filter(title="三国演义").values("publisher__email")

print(ret) # <QuerySet [{‘publisher__email‘: ‘234‘}]>

方法二

# 查的是Publish,然后反向查询按表名book,然后book__title跨到book表拿title,将book__title="三国演义"作为筛选条件获取到email

# 实际就是查的时候,查自己表的字段,直接写字段,查别的表的字段,就要按正/反向查询写 字段/表名__字段,注意:作为values()的参数,必须要用引号包起来,也就是”字段/表名__字段”,当然这两张表不能是没关系的两张表,必须是关联的表,否则也谈不上正反向查询

ret=Publish.objects.filter(book__title="三国演义").values("email")

print(ret) # <QuerySet [{‘email‘: ‘234‘}]>

方法三(基于对象的查询,有点麻烦)

# 正向查询按字段,利用.publisher得到publish表记录(模型类对象),然后对象.email取到邮箱

ret = Book.objects.filter(title=三国演义).first().publisher.email

print(ret) # 直接打印出邮箱 234

 

例二 查询人民出版社出版社所有书籍的名称和价格

# 方法一

ret=Publish.objects.filter(name="人民出版社")

.values("book__title","book__price")

# 方法二

ret=Book.objects.filter(publisher__name="人民出版社")

.values("title","price")

 

例三 查询alex的手机号

ret=Author.objects.filter(name="alex").values("ad__tel")

ret=AuthorDetail.objects.filter(author__name="alex").values("tel")

例四 红楼梦的所有作者的名字和年龄

Book.objects.filter(title="红楼梦").values("authors__name",

"authors__age")

Author.objects.filter(book__title="红楼梦").values("name","age")

 

例五 连续跨表: 手机号以123开头的作者出版过的所有书籍名称以及出版社名称

# 连续跨表一般挑选关系表中最中间的表作为起点,跨表更容易

# 方法一,以Book表为起点

Book.objects.filter(authors__ad__tel__startswith="123").values("title", "publisher__name")

# 方法二,以Author表为起点

Author.objects.filter(ad__tel__startswith="123").values("book__title",

"book__publisher__name"))

 

 

 

 

以上是关于Django之ORM的主要内容,如果未能解决你的问题,请参考以下文章

Django之ORM

(Django)气流中的 ORM - 有可能吗?

Django之ORM框架的使用

Django 基础 之ORM简介与单表操作

九Django之ORM

Django(MTV)之 M:ORM