5.ORM

5.1ORM简介

  • MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动
  • ORM是“对象-关系-映射”的简称。(Object Relational Mapping,简称ORM)
  • ORM执行的过程
    • 类对象--->sql--->pymysql--->mysql服务端--->磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎

ORM与原生SQL对比

iShot2020-10-16 14.44.35

5.2ORM操作

5.2.1ORM连接mysql

这里先来一个orm连接mysql并创建一张表的示例

第一步、修改settings配置文件,配置mysql相关信息

#先注释默认项
# DATABASES = {
#     'default': {
#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
#     }
# }


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '数据库名',
        'HOST': '数据库IP',
        'PORT': '数据库端口',
        'USER': '用户',
        'PASSWORD': '密码'
    }
}

第二步、在项目目录下的同名目录下的__init__.py文件中写上以下内容,来指定pymysql作为连接客户端

import pymysql
pymysql.install_as_MySQLdb()

第三步、在应用程序目录下面的models.py文件中写对应的类,这里为创建一张表

from django.db import models 

#这里表示创建一个userinfo表,需要注意的是,django在创建表的时候会把表名小写并重命名为  应用程序_userinfo
class UserInfo(models.Model):
    id = models.AutoField(primary_key=True)
    username = models.CharField(max_length=10)
    password = models.CharField(max_length=32)

第四步: 执行数据库同步指令,在终端中执行,执行的路径是django项目下

#在migrations文件夹下面生成记录文件
python3 manage.py makemigrations  

#执行记录文件
python3 manage.py migrate

可以看到,我们自定义的表已经创建完成了,但是发现多了好多其他的表,那这些表是从哪来的呢,这些是django从settings文件中循环读取INSTALLED_APPS下的所有子项而默认创建的表

iShot2020-10-16 14.47.57

表结构

iShot2020-10-16 14.48.20

5.2.2单表操作

5.2.2.1增

应用程序的models.py文件中已经定义了创建一张表的语句

urls文件

from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
]

views文件

from django.shortcuts import render,HttpResponse
from app01 import models

#方式1
def index(request):
    obj = models.UserInfo(
        username='小明',
        password='123'
    )
    obj.save()
    return HttpResponse('ok')


#方式2
def index(request):
    models.UserInfo.objects.create(
        username='小颖',
        password='666'
    )
    return HttpResponse('ok')

接下来启动django项目,浏览器访问我们指定的127.0.0.1:8000/index

执行以上任意一种方式都可以插入数据

iShot2020-10-16 14.48.48

5.2.2.2删

views文件

def index(request):
      #删除UserInfo表中id值为1的值
    models.UserInfo.objects.filter(id=1).delete()
    return HttpResponse('ok')

浏览器访问127.0.0.1/index即可成功执行语句

iShot2020-10-16 14.49.22

可以看到,id为1的字段已经被删除

iShot2020-10-16 14.49.40

5.2.2.3改

views文件

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
    # 方式1
    models.UserInfo.objects.filter(id=2).update(
        username='abc',
        password='abc',
    )

    # 方式2
    # obj = models.UserInfo.objects.filter(id=2)[0]
    # obj.username = 'ggg'
    # obj.password = 'ggg'
    # obj.save()

    return HttpResponse('ok')

浏览器访问127.0.0.1/index

可以看到修改已经生效

iShot2020-10-16 14.50.44

批量创建

views文件

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
    list_obj = []
    for i in range(6):
        obj = models.UserInfo(
            username='name%s' %i,
            password='pwd%s' %i,
        )
        list_obj.append(obj)
    print(list_obj)
    models.UserInfo.objects.bulk_create(list_obj)

    return HttpResponse('ok')

print(list_obj)返回的结果如下

[<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]

浏览器访问127.0.0.1/index

可以看到已经批量插入了数据

iShot2020-10-16 14.51.19

update_or_create 有就更新,没有就创建

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
    a,b = models.UserInfo.objects.update_or_create(
        username='name0',
        defaults={
            'id': 20,
            'password': 'abcdef',
        }
    )

    print(a)  #当前更新后的model对象,或者是你新增的记录的model对象
    print(b)  #新增就是True,查询就False

    return HttpResponse('ok')


#a返回的结果
UserInfo object

#b返回的结果
True

iShot2020-10-16 14.51.47

5.2.2.4查

最简单的查询

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
    ret = models.UserInfo.objects.filter(id=1)
    print(ret)  # <QuerySet [<UserInfo: UserInfo object>]> -- []
    obj = ret[0]
    print(obj.id, obj.username)

    return HttpResponse('ok')

表app01_userinfo内容如下

iShot2020-10-16 14.52.34

print(ret)结果如下
<QuerySet [<UserInfo: UserInfo object>]>

print(obj.id, obj.username)执行后结果如下
1 aaa

查询非常重要的13种方法

1 all() 查询所有结果,结果是queryset类型

views文件

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
    ret = models.UserInfo.objects.all()
    print(ret)

    return HttpResponse('ok')

#print(ret)结果如下
<QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>

现在我们想看到的是数据库中的数据,需要在models文件中我们创建的UserInfo中写一个类的属性
class UserInfo(models.Model):
    id = models.AutoField(primary_key=True)
    username = models.CharField(max_length=10)
    password = models.CharField(max_length=32)

    #在models文件中写上这个属性后就能在all()方法中返回数据而不是对戏那个
    def __str__(self):
        return self.username


#结果如下
<QuerySet [<UserInfo: aaa>, <UserInfo: bbb>]>

2 get() 只能返回一个数据,没有或者数据多就报错,不是queryset类型,是行记录对象

例如,查询一个表中密码为aaa的数据,如果这个表中只有一个用户的密码是aaa,则可以查询成功,如果这个表中有多个用户的密码是aaa这个时候就会报错并且这个表中如果没有用户的密码是aaa同样会报错

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
    ret = models.UserInfo.objects.get(password='aaa')
    print(ret)
    return HttpResponse('ok')

表app01_userinfo内容如下

iShot2020-10-16 14.53.12

浏览器访问127.0.0.1/index

此时返回的结果是可以成功查询的

现在修改表内容如下

iShot2020-10-16 14.53.32

再次执行以上代码,查询表中密码为aaa的数据,此时会报错

iShot2020-10-16 14.53.53

修改代码为查询表中密码为bbb的数据,同样会报错

iShot2020-10-16 14.54.25

get两个报错,但是get请求返回的结果是model对象

#1.UserInfo matching query does not exist. 没有查到的报错
#2 get() returned more than one UserInfo -- it returned 11!  结果多了,不行!

3 filter() 查询数据,返回值是queryset类型

views文件

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
    ret = models.UserInfo.objects.filter(password='bbb')
    print(ret)
    return HttpResponse('ok')

filter查询表中密码为bbb的数据,虽然表中结果没有,但是不会像get报错

print(ret)结果如下

<QuerySet []>

filter查询表中密码为aaa的数据,表中密码为aaa的数据有2条,filter不会像get一样报错

<QuerySet [<UserInfo: aaa>, <UserInfo: bbb>]>

4 exclude() 排除,返回值是queryset类型

现有app01_book表如下

iShot2020-10-16 14.54.50

查询所有数据,排除bid为4的数据

urls文件

from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
]

views文件

from django.shortcuts import render,reverse,redirect,HttpResponse
from app01 import models

def index(request):
    #查询bid不为4的数据,这里还可以写多个排除条件,以逗号分隔
    ret = models.Book.objects.exclude(bid=4)
    print(ret)
    return HttpResponse('ok')

models文件

class Book(models.Model):
    bid = models.AutoField(primary_key=True)
    bname = models.CharField(max_length=20)
    price = models.DecimalField(max_digits=6,decimal_places=2)
    publish_date = models.DateField(auto_now_add=False)
    publish = models.CharField(max_length=20)

    def __str__(self):
        return self.bname

浏览器访问127.0.0.1/index,返回结果如下,bid为4的隔壁老王的故事没有返回

<QuerySet [<Book: 1-Linux从入门到放弃>, <Book: 2-python从入门到放弃>, <Book: 3-mysql之从删库到跑路>, <Book: 5-放开那个女孩>, <Book: 6-成为富翁的秘密>]>

5 order_by() queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值是queryset类型

按照bid倒序排序,在bid前加个-号即可

viwes文件

def index(request):
    ret = models.Book.objects.all().order_by('-bid')
    print(ret)
    return HttpResponse('ok')

浏览器反问127.0.0.1/index,返回结果如下,结果是倒序

<QuerySet [<Book: 6-成为富翁的秘密>, <Book: 5-放开那个女孩>, <Book: 4-隔壁老王的故事>, <Book: 3-mysql之从删库到跑路>, <Book: 2-python从入门到放弃>, <Book: 1-Linux从入门到放弃>]>

order_by()中加多个条件说明

#这段代码的意思是按照id倒叙排序,然后再按照id相同的数据进行价格升序排序
models.Test.objects.all().order_by('-id','money')

现有表app01_test如下

iShot2020-10-16 14.59.44

先按照id进行倒叙排序

ret = models.Test.objects.all().order_by('-id')
print(ret.values())

返回结果如下

<QuerySet [{'id': 3, 'name': '3-哈哈', 'money': 8}, {'id': 3, 'name': '3-大强', 'money': 80}, {'id': 3, 'name': '3-呵呵', 'money': 800}, {'id': 2, 'name': '2-小颖', 'money': 100}, {'id': 1, 'name': '1-小明', 'money': 10}]>

再加一个条件进行排序,先根据id相同的进行倒序排序,然后再根据money升序排序

ret = models.Test.objects.order_by('-id','money')
print(ret.values())

返回结果如下

<QuerySet [{'id': 3, 'name': '3-大强', 'money': 80}, {'id': 3, 'name': '3-呵呵', 'money': 800}, {'id': 3, 'name': '3-哈哈', 'money': 8}, {'id': 2, 'name': '2-小颖', 'money': 100}, {'id': 1, 'name': '1-小明', 'money': 10}]>

6 reverse() queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型

以例5中app01_test表为例

⚠️直接进行reverse进行反向排序是不生效的,必须先进行分组

ret = models.Test.objects.all().reverse()

返回结果如下,不分组进行反向排序是不生效的

<QuerySet [{'id': 1, 'name': '1-小明', 'money': 10}, {'id': 2, 'name': '2-小颖', 'money': 100}, {'id': 3, 'name': '3-大强', 'money': 80}, {'id': 3, 'name': '3-呵呵', 'money': 800}, {'id': 3, 'name': '3-哈哈', 'money': 8}]>

先进行分组,然后再进行反向排序才能生效

ret = models.Test.objects.all().order_by('id').reverse()

返回结果如下

<QuerySet [{'id': 3, 'name': '3-大强', 'money': 80}, {'id': 3, 'name': '3-呵呵', 'money': 800}, {'id': 3, 'name': '3-哈哈', 'money': 8}, {'id': 2, 'name': '2-小颖', 'money': 100}, {'id': 1, 'name': '1-小明', 'money': 10}]>

7 count() queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量

以例5中app01_test表为例

ret = models.Test.objects.all().count()
print(ret)

返回结果为5,因为app01_test表中有5条记录

8 first() queryset类型的数据来调用,返回第一条记录,得到的都是model对象,不是queryset

以例5中app01_test表为例

ret = models.Test.objects.all().first()
print(ret)


#models文件表的类中需要写如下代码
class Test(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=10)
    money = models.IntegerField()

    def __str__(self):
        return self.name

返回结果为1-小明

9 last() queryset类型的数据来调用,返回最后一条记录,得到的是model对象

以例5中app01_test表为例

ret = models.Test.objects.all().last()
print(ret)


#models文件表的类中需要写如下代码
class Test(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=10)
    money = models.IntegerField()

    def __str__(self):
        return self.name

查询时遇到的一个问题,使用last方法查询一个没有设置主键的表时,只能返回id相同的记录中的第一个id的记录

例如,一张表中id字段如下:1、2、3、3、3,查询的返回的结果是id=3的第一条记录,即2后边的第一个3

iShot2020-10-16 15.00.17

此表中没有设置主键,只能返回3-大强,而理论上是3-哈哈

10 exists() queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False

空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits 例:all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS a FROM app01_book LIMIT 1,就是通过limit 1,取一条来看看是不是有数据

以例5中app01_test表为例

test表中没有id=5的数据,因此会返回False

ret = models.Test.objects.filter(id=5).exists()

11 values() 用的比较多,queryset类型的数据来调用,返回一ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续连续调用queryset类型的其他的查找方法,其他方法也是一样的。

以例5中app01_test表为例

 ret = models.UserInfo.objects.all().values('id','name')  queryset调用          

ret = models.UserInfo.objects.values('id','name')   objects调用--对所有数据进行取值

返回结果如下

<QuerySet [{'id': 1, 'name': '1-小明'}, {'id': 2, 'name': '2-小颖'}, {'id': 3, 'name': '3-大强'}, {'id': 3, 'name': '3-呵呵'}, {'id': 3, 'name': '3-哈哈'}]>

12 values_list 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

以例5中app01_test表为例

ret = models.Test.objects.all().values_list('id','name')

返回结果如下

<QuerySet [(1, '1-小明'), (2, '2-小颖'), (3, '3-大强'), (3, '3-呵呵'), (3, '3-哈哈')]>

13 distinct() values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录

以例5中app01_test表为例

 ret = models.UserInfo.objects.all().values('id','name').distinct()               
 ret = models.UserInfo.objects.values('id','name').distinct()

基于双下划线的模糊查询

表app01_book内容如下

iShot2020-10-16 15.00.42

#price值等于这三个里面的任意一个的对象
Book.objects.filter(price__in=[10,33,9999]) 

#大于,大于等于是price__gte=100,别写price>100,这种参数不支持
Book.objects.filter(price__gt=100)

#小于,小于等于是price__lte=100,别写price<100,这种参数不支持
Book.objects.filter(price__lt=100)

#sql的between and,大于等于100,小于等于10000
Book.objects.filter(price__range=[100,10000])  

#bname值中包含python的
Book.objects.filter(bname__contains="python")  

#不区分大小写,在前边加一个i就是不区分大小写,其余用法相同
Book.objects.filter(bname__icontains="python") 

#以什么开头,istartswith  不区分大小写
Book.objects.filter(bname__startswith="py") 

#查询publish_date是2020年的
Book.objects.filter(publish_date__year=2020)

查询示例

1 查询某某出版社出版过的价格大于100的书籍
    ret = models.Book.objects.filter(price__gt=100,publish='老王出版社').values('bname')

2 查询201811月出版的所有以Li开头的书籍名称
  ret = models.Book.objects.filter(bname__startswith='Linux',publish_date__year=2018,publish_date__month=11).values('bname')

3 查询价格为88,100或者9999的所有书籍名称及其出版社名称
   ret = models.Book.objects.filter(price__in=[88,100,9999]).values('bname','publish')

4 查询价格在80100之间的所有书籍名称及其价格
 ret = models.Book.objects.filter(price__range=[80,100]).values('bname','price')

5 查询所有老王出版社出版的书籍的价格(从高到低排序,去重)
 ret = models.Book.objects.filter(publish='老王出版社').values('price').order_by('-price').distinct()

5.2.3多表操作

表关系及字段设计

iShot2020-10-16 15.05.53

urls文件

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^query/', views.query),
]

settings文件配置数据库连接

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'orm01',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': 'xxx'
    }
}

models文件

from django.db import models

# Create your models here.

class Author(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    ad=models.OneToOneField(to="AuthorDetail",to_field="id",on_delete=models.CASCADE)
    # to_field="id"  可以不写,默认找主键
    # to="AuthorDetail",  to=可以不用写
    # models.SET_NULL  置空
    # on_delete=models.CASCADE  默认是级联删除,想做级联更新,直接去数据库修改表结构

class AuthorDetail(models.Model):
    birthday=models.DateField()
    telephone=models.CharField(max_length=16)
    addr=models.CharField(max_length=64)


class Publish(models.Model):
    name=models.CharField(max_length=32)
    city=models.CharField(max_length=32)

class Book(models.Model):
    title = models.CharField(max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)

    #这行代码的意思是生成第3张表关联book表和author表
    authors=models.ManyToManyField(to='Author',)

⚠️如果AuthorDetail写在Author的上边,则ad=models.OneToOneField(to="AuthorDetail"中的AuthorDetail可以不加引号

⚠️django1.x版本on_delete=models.CASCADE可以不写,默认是级连删除,如果想做级连更新,直接到数据库中操作,但是django2.x必须写级连删除

⚠️OneToOneField是一对一 ForeignKey是一对多 ManyToManyField是多对多

django项目下同名目录__init__.py文件

import pymysql
pymysql.install_as_MySQLdb()

相关配置文件配置完成后开始同步数据库

⚠️makemigrations执行后必须执行migrate,否则会出问题

//在django项目下
#在migrations目录下面生成记录文件
python3 manage.py makemigrations  

#执行记录文件
python3 manage.py migrate

关于只执行makemigrations不执行migrate可能出现的问题

1.当执行makemigrations后,会在django项目下的makemigrations目录中创建一个0001_initial.py,这个文件记录的是models文件中的操作(创建表、删除表),当执行migrate后会执行数据库同步操作,同时在数据库中django会自动创建一张django_migrations表,这个表记录的是models文件中的操作

2.如果只执行了makemigrations而不执行migrate,当修改了models文件再次重复执行makemigrations,django会从数据库中的django_migrations查看0001_initial是否执行过,而此时0001_initial已经执行过了,因此会出现修改了models文件执行migrate提示没有改动的问题

同步后的数据库,可以看到app01_book_authors这张表是orm自动帮我们创建的

iShot2020-10-16 15.06.25

5.2.3.1增

views文件

一对一,作者表和作者详细信息表

//给作者详细信息表添加一条记录
 models.AuthorDetail.objects.create(
        birthday='2018-11-11',
        telephone='18033336789',
        addr='北京'
    )


//给作者表添加一条记录
 models.Author.objects.create(
        name='小明',
        age=1,

               #方式1
        #关联作者详细信息表中id为1的
        ad=models.AuthorDetail.objects.get(id=1)

               #方式2(常用)
        ad_id = 1
    )

iShot2020-10-16 15.07.58

一对多,出版社和书籍表

models.Book.objects.create(
        title='隔壁老王的故事',
        publishDate='2011-01-01',
        price='99',

              #方式1
        publishs=models.Publish.objects.get(id=1)

              #方式2
              publishs_id=2
    )

iShot2020-10-16 15.08.27

多对多,书籍表和作者表

//方式一
book_obj = models.Book.objects.get(id=1)
author1 =models.Author.objects.get(id=1)
author2 =models.Author.objects.get(id=2)

#向关联书籍表和作者表的第3张表中插入作者1和作者2,书籍id为1的书的作者有2个,分别为作者id为1的和作者id为2的
#方式1
book_obj.authors.add(author1,author2)
#方式2
book_obj.authors.add(*[author1,author2])


//方式二
book_obj = models.Book.objects.get(id=1)
#方式1
book_obj.authors.add(1,2)
#方式2
book.obj.authors.add(*[1,2])

查看关联作者表和书籍表的第3张表app01_book_authors,书籍id为1的书作者有2个

iShot2020-10-16 15.08.54

5.2.3.2删

一对一、一对多删除方式一样

models.Author.objects.get(id=1).delete()

删除了作者表中id1的记录,但是不会影响作者详细信息表中作者id1的记录

作者表的id作为外键关联作者详细信息表中的作者id,可以理解为我抱别人的大腿,删除我自己不影响别人,但是别人如果删除大腿会影响我

作者表是关联者,作者详细信息表是被关联者

多对多

//方式一    remove    移除
#找到书籍id为1的书
book_obj = models.Book.objects.get(id=1)

#删除书籍id为1的书的作者id为2和3
book_obj.authors.remove(2,3)


//方式二    clear    全部清空
#把书籍id为1的对应的作者全部删除
book_obj = models.Book.objects.get(id=1)
book_obj.authors.clear()

5.2.3.3改

一对多和一对一和单表是一样的

#将书籍表中id为1的数据数名修改为python从入门到放弃并且出版社修改为出版社id为3的
book_obj = models.Book.objects.filter(id=1).update(
    title='python从入门到放弃',

    #方式1
    publishs=models.Publish.objects.get(id=3)

    #方式2
    publishs_id=3

多对多

//set    先清空再追加,set中必须写字符串,删除多个在列表中填写多个数值
#把书籍id为1的作者全部改为2
book_obj = models.Book.objects.get(id=1)
book_obj.authors.set(['2',])

5.2.3.4查

基于对象的跨表查询

一对一

作者表和作者详细信息表

//正向查询
#查询作者小明的住址
author_obj = models.Author.objects.get(name='小明')
# author_obj.ad直接拿到authordetail表中的那个记录对
print(author_obj.ad.addr)


//反向查询
#查询作者详细信息表中电话号以170开头的作者名字
authordetail_ojb = models.AuthorDetail.objects.filter(
        telephone__startswith='170').first()



print(book_obj)  #不加first打印结果   <QuerySet [<Book: Book object>]>
print(book_obj)  #加first打印结果    Book object


#authordetail_ojb.author中的author是类名小写,直接定位到app01_author这张表
print(authordetail_ojb.author.name)

关于正向查询和反向查询的说明

iShot2020-10-16 15.09.27

一对多

出版社表和书籍表

//正向查询    
#查询python从入门到放弃这本书的出版社
book_obj = models.Book.objects.filter(title='python从入门到放弃').first()
print(book_obj.publishs.name)


//反向查询
pub_obj = models.Publish.objects.get(name='南京出版社')

#pub_obj.book_set.all()中book_set是结果可能为多个,最后的all是拿到的书籍对象所有信息
books = pub_obj.book_set.all().values('title')
print(books)

#查询南京出版社中数名包含linux的书
books = pub_obj.book_set.filter(title__contains='linux').values('title')

多对多

书籍表和作者表

//正向查询
#查询书籍表中书名为linux从入门到放弃的作者
book_obj = models.Book.objects.filter(title='linux从入门到放弃').first()
authors = book_obj.authors.all().values('name')
print(authors)


//反向查询    反向查询中用字段存在的表的小写类名,这里为book
#查询作者小明写的书
xiaoming_obj = models.Author.objects.get(name='小明')
ret = xiaoming_obj.book_set.all().values('title')
print(ret)

查询书籍表中书名为linux从入门到放弃的作者

iShot2020-10-16 15.09.49

基于双下划线的跨表查询 就是join连表查询

一对一

作者表与作者详细信息表

//查询作者小明的住址
#基于对象的跨表查询写法 正向查询
author_obj = models.Author.objects.get(name='小明')
#author_obj.ad直接拿到authordetail表中的那个记录对
print(author_obj.ad.addr)

#基于双下划线的跨表查询写法
#正向查询
ret = models.Author.objects.filter(name='小明').values('ad__addr')
print(ret)

#反向查询    
ret = models.AuthorDetail.objects.filter(author__name='小明').values('addr')
print(ret)



正向查询中    关联字段__字段 就获得了关联表的数据
反向查询中 表名__字段

一对多

书籍表和出版社表

//查询东京出版社出版的书
#原生sql语句
select app01_book.title from app01_publish inner join app01_book on app01_publish.id = app01_book.publishs_id where app01_publish.name='东京出版社';

#正向查询
ret = models.Book.objects.filter(publishs__name='东京出版社').values('title')


#反向查询
ret = models.Publish.objects.filter(name='东京出版社').values('book__title')

多对多

书籍表和作者表

关联字段在书籍表中,从书籍表查询是正向查询

#查询一下小明写了哪些书
#正向查询    关联字段在book表中,现在要查询小明写了哪些书,当前是book表,表中没有作者名字的字段,因此需要先连表然后在取值
ret = models.Book.objects.filter(authors__name='小明').values('title')
print(ret)

#反向查询    现在要查询小明写了哪些书,当前是author表,表中有作者名字的字段,因此不需要先连表,已知字段在本表的就可以先写条件最后在连表
ret = models.Author.objects.filter(name='小明').values('book__title')
print(ret)



关联的方式,正向查询和反向查询都可以使用,关键在于查询的条件
关联字段__字段 就获得了关联表的数据
小写类名__字段

聚合查询

aggregate聚合查询,结果是普通字典,queryset的结束符

#导入相关模块
from django.db.models import Avg,Max,Min,Count,Sum

//查询书籍表中价格最高的
obj = models.Book.objects.all().aggregate(a=Max('price'))
print(obj)

结果:
    {'a': Decimal('125.00')}

分组查询

//查询每个出版社出版的书的最高价格
#方式一
ret = models.Book.objects.values('publishs_id').annotate(m=Max('price'))
print(ret)

结果:
    <QuerySet [{'publishs_id': 2, 'm': Decimal('125.00')}, {'publishs_id': 3, 'm': Decimal('99.00')}]>

方式一写法说明
values写在annotate前面,意思是以values括号内的字段作为分组的依据,annotate里面是你要做的统计结果,这样,返回结果为queryset类型数据,里面是字典{'publishs_id':1,'m':100}



#方式二
#从出版社表中查询,需要连接book表,因为book表中才有书名和价格,这么写表示直接以publish这张表的id进行分组
ret = models.Publish.objects.annotate(m=Max('book__price')).values('m','name')
print(ret)

结果:
<QuerySet [{'name': '东京出版社', 'm': Decimal('99.00')}, {'name': '南京出版社', 'm': Decimal('125.00')}, {'name': '北京出版社', 'm': None}]>

方式二写法说明
annotate直接写在了objects后面,意思是按照前面表的所有的数据(默认是id)作为分组依据,结果返回的是前面这个表的所有models对象(model对象中包含了每个对象自己的统计结果),在通过values来取值,取值时可以直接写字段和统计结果的别名,也是queryset类型,里面是字典{'m':100,'name':'东京出版社'}

F和Q查询

在book表中添加连个字段:点赞(dianzan)和评论(pinglun)

class Book(models.Model):
    title = models.CharField(max_length=32)
    publishDate=models.DateField()
    dianzan = models.IntegerField(default=100)
    pinglun = models.IntegerField(default=100)
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)

app01_book表中内容

iShot2020-10-16 15.10.19

F查询 F查询用于一张表中的两个字段比较之后的符合条件的结果集

//查询book表中点赞数大于评论数的所有书籍
#low版写法
list1 = []
books = models.Book.objects.all()
for i in books:
    if i.dianzan > i.comment:
    list1.append(i)

#F查询写法
ret = models.Book.objects.filter(dianzan__gt=F('pinglun')).values('title')
print(ret)

结果:
<QuerySet [{'title': 'python从入门到放弃'}, {'title': 'linux从入门到放弃'}]>



//F还支持四则运算,例如将book表中的价格都上调50元models.Book.objects.all().update(
    price=F('price')+50
)

Q查询 Q查询用于多个条件有与、或、非时的综合查询

与:& 或:| 非:~

orm执行原生sql语句

//方式一
ret = models.Book.objects.raw('select * from app01_book')
for i in ret:
    print(i.title)

结果:
会把表中所有书名打印出来



//方式二    django自带的连接通道(在项目下同名目录__init__.py中配置的pymysql)
from django.db import connection
import pymysql
def query(request):
    conn = pymysql.connect()
    cursor = connection.cursor()
    cursor.execute('select * from app01_book;')
    print(cursor.fetchall())



//方式三
def query(request):
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='PAPAlichencan!1',
        database='orm02',
        charset='utf8'

    )
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    cursor.execute('select * from app01_book;')
    print(cursor.fetchall())

结果:
[{'id': 1, 'title': 'python从入门到放弃', 'publishDate': datetime.date(2011, 1, 1), 'price': Decimal('99.00'), 'publishs_id': 3, 'dianzan': 600, 'pinglun': 150}, {'id': 2, 'title': 'linux从入门到放弃', 'publishDate': datetime.date(2019, 12, 12), 'price': Decimal('99.90'), 'publishs_id': 2, 'dianzan': 500, 'pinglun': 300}, {'id': 3, 'title': '隔壁老王的故事', 'publishDate': datetime.date(2019, 11, 7), 'price': Decimal('125.00'), 'publishs_id': 2, 'dianzan': 100, 'pinglun': 100}, {'id': 4, 'title': 'linux安全指南', 'publishDate': datetime.date(2020, 11, 30), 'price': Decimal('66.00'), 'publishs_id': 2, 'dianzan': 300, 'pinglun': 2000}, {'id': 5, 'title': '奔跑的linux内核', 'publishDate': datetime.date(2019, 11, 1), 'price': Decimal('77.00'), 'publishs_id': 2, 'dianzan': 50, 'pinglun': 100}]

django外部脚本调用models数据库操作

使用场景:有的时候我们不想运行django项目只想执行一些视图中的逻辑,这个时候就用到了django外部脚本调用models数据库操作

操作方法:在django项目下新建一个xx.py文件,在xx.py文件中写入以下内容,然后直接运行这个xx.py文件就可以在不启动django项目的情况下而单独执行我们想执行的测试逻辑

#导入os模块
import os

if __name__ == '__main__':
    #最后一个参数要写项目名.settings
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm02.settings")

    #导入django
    import django
    django.setup()

    #导入应用程序的models
    from app01 import models
    ret = models.Book.objects.all().values('title')
    print(ret)

ORM事务和锁

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

事务

//方式1 全局配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mxshop',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': '123',
        "ATOMIC_REQUESTS": True, #全局开启事务,绑定的是http请求响应整个过程当中的sql
    }
}

方式1中,django项目中所有的视图逻辑全部加上了事物


//方式2: 视图函数加装饰器
from django.db import transaction
@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    逻辑语句

方式2中,我们只想给某一个视图函数加事物,这个时候就用到了上述的方法


方式3: 上下文加装饰器  
from django.db import transaction
def viewfunc(request):
    # This code executes in autocommit mode (Django's default).
    逻辑语句1

    with transaction.atomic():   #保存点
        # This code executes inside a transaction.
        逻辑语句2

    逻辑语句3


方式3中,我们只想给逻辑语句2添加事物,而其他语句不需要,这个时候就在逻辑语句2的上边写如上语句即可
泡泡吐肥皂o © gitbook.pptfz.top 2021 all right reserved,powered by Gitbook文件修订时间: 秃笔南波湾!!!

results matching ""

    No results matching ""