Django从入门到放弃二 -- ORM之单表查询与多表查询

一、ORM -- 数据库交互

  参考地址:

ORM常用字段和参数:https://www.cnblogs.com/liuqingzheng/articles/9627915.html
Django-model进阶:https://www.cnblogs.com/liuqingzheng/articles/9805991.html

8 Django 模型层(1) - Yuan先生 - 博客园

Django从入门到放弃 - 刘清政 - 博客园

  SQL中的表与ORM直观对比图:ORM在python中是以类的方式定义的,在执行python时,类会转换成对应的SQL语句。

   1.1、首先学习,在Python中创建数据库

     1.1.1).首先在DB里面创建库:create database book_orm;

     1.1.2).在Django项目里面,设置settings.py文件

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'orm_19',   # 要连接的数据库,连接前需要创建好
        'USER':'root',         # 连接数据库的用户名
        'PASSWORD':'123456',   # 连接数据库的密码
        'HOST':'127.0.0.1',    # 连接主机,默认本级
        'PORT':3306            # 端口 默认3306
    }
}

# 手动创建数据库
  create DATABASE `sudada` default charset utf8;
# 备注: 制定utf8字符集

    1.1.3).在Django项目里面,设置models.py文件 (数据表结构)

class Book(models.Model):                       # Book可以理解为要创建的表名(实际创建时会附带上Django应用的名称)
    id = models.AutoField(primary_key=True)     # 主键:主键字段不可修改,如果你给某个对象的主键赋个新值实际上是创建一个新对象,并不会修改原来的对象。
    title = models.CharField(max_length=32)     # CharField用来存字符串的,对应varchar
    pub_date = models.DateField()               # 存日期的
    price = models.DecimalField(max_digits=8, decimal_places=2) # 存价格的
    publish = models.CharField(max_length=32)   # CharField用来存字符串的,对应varchar

    1.1.4).在windows命令行使用:python3 manage.py makemigrations 命令(生成一条记录,并没有提交到数据库)  创建表:(步骤一)

    1.1.4.1) 这一步会报错,如下:这是由于使用了MySQL模块导致的,在python3中,一般都是使用pymysql的,那么我们就需要告诉Django要使用pymysql。

   1.1.4.2) 在app01目录下的__init__.py文件中或者在项目目录下的__init__.py文件中定义如下代码:告诉Django数据驱动使用pymysql

import pymysql

pymysql.install_as_MySQLdb()

  1.1.4.3) 最后在使用python3 manage.py makemigrations命令 (生成一条记录,并没有提交到数据库) 创建表即可

    1.1.5.在windows命令行使用:python3 manage.py migrate 命令 (把生成的命令提交到数据库执行) 创建表: (步骤二)

  1.1.6.数据表创建完毕后,查看:

  这些表里面除了 "app01_book" (app01:应用名,book:表名,2者组合为了方便区分) 这张表是自己写的,其他都是Django默认创建的。

  1.1.7. 开启Django项目,由于在models.py文件定义了创建表的数据类型,那么访问http://127.0.0.1:8000/即可创建数据类型(关系对象映射)

  1.2 对数据库内的表结构,做修改的操作

     1.2.1.新增一条表结构 -- 在models.py文件中,直接新增一行表结构 (必须输入默认内容及是否允许为空:default='egon',null=True ),然后执行 python3 manage.py makemigrations (生成一条记录,并没有提交到数据库)  命令:

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)
    publish = models.CharField(max_length=32,default='egon',null=True)  # default='egon':设置默认字符串,null=True:允许空值。

     执行 python3 manage.py makemigrations 命令之后,会在migtations下生成一个文件:这个文件里面记录了,修改后的表结构。

    operations = [
        migrations.AddField(
            model_name='book',
            name='publish',
            field=models.CharField(default='egon', max_length=32, null=True),
        ),
    ]

    需要再次执行  python3 manage.py migrate (把生成的命令提交到数据库执行) 创建表。

     1.2.2.删除一条表结构  -- 在models.py文件中,直接注释掉一行表结构

class Book(models.Model):
    id = models.AutoField(primary_key=True)     # 生成一个主键:主键字段不可修改,如果你给某个对象的主键赋个新值实际上是创建一个新对象,并不会修改原来的对象。
    title = models.CharField(max_length=32)     # CharField 对应的就是varchar,用来存放字符串的
    pub_date = models.DateField()               # 日期格式
    price = models.DecimalField(max_digits=8, decimal_places=2)
    # publish = models.CharField(max_length=32)

     然后执行 python3 manage.py makemigrations (生成一条记录,并没有提交到数据库) 命令,会在migrations目录下生产一个0001_initial.py文件,这个文件里面记录了,修改后的表结构。

    operations = [
        migrations.CreateModel(
            name='Book',
            fields=[
                ('id', models.AutoField(primary_key=True, serialize=False)),
                ('title', models.CharField(max_length=32)),
                ('pub_date', models.DateField()),
                ('price', models.DecimalField(decimal_places=2, max_digits=8)),
            ],
        ),
    ]

    需要再次执行  python3 manage.py migrate (把生成的命令提交到数据库执行) 创建表。

1.2、 如果想打印orm转换过程中的sql,需要在settings中进行如下配置:DBUG模式

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

# 输出的SQL预计如下:
    SELECT `app01_study`.`id`, `app01_study`.`title`, `app01_study`.`pub_date`, `app01_study`.`price`, `app01_study`.`publish` FROM `app01_study` WHERE `app01_study`.`title` = '红宝书'  LIMIT 21; args=('红宝书',)
[04/Mar/2019 22:46:39] "GET /select/ HTTP/1.1" 200 2

  1.3 、单表操作-针对数据库表的增、删、改、查      学习地址:Django模型层 - 刘清政 - 博客园

   1.3.1.添加数据  方式一:create方式

from app01.models import Book  # 引用models.py文件内的Book类

def orm(request):            # Book.objects表示"Book类"的一个管理器,通过这个管理器来实现"增,删,改,查"
    # 添加数据
    # Book类,即models.py文件里面定义的类
    # title,pub_date,price,publish 即为表的字段
    Book.objects.create(title='python',pub_date='2015-05-11',price=199,publish='人民出版社')
    return HttpResponse('OK')

   添加完毕后,查询数据库内容如下:

  

 方式二:save方式,有返回值,就是Book对象

from app01.models import Book

def add(request):

    book=Book(title='红宝书',price=100,pub_date='2017-8-17',publish='人民')
    book.save()

    return HttpResponse('ok')

添加完毕后,查询数据库内容如下:

   1.3.2.查询数据

 方式一:使用Book.objects.all()拿到所有对象,拿到的是一个QuerySet,里面包含的所有的对象

from app01.models import Book
def orm(request):
    # 查询数据
    book_list=Book.objects.all()   # Book.objects.all()拿到的是一个"QuerySet"对象,(数据库内的"app01_book"表,每一行数据都被实例化为一个对象)
    for book in book_list:         # 循环对象
        print(book.title)          # 打印对象的title,pub_date,price,publish等信息
        print(book.pub_date)
        print(book.price)
        print(book.publish)
        # linux
        # 2013 - 05 - 11
        # 299.00
        # 人民出版社
    return HttpResponse('OK')

方式二:查询单个或多个条件 Study.objects.filter(title='西游记',id=5),拿到的是一个QuerySet,里面包含的是查询到的对象

from app01.models import Study
def select(request):
    res=Study.objects.filter(title='西游记',id=5)
    print(res)   # res拿到的是"QuerySet"这个对象,<QuerySet [<Study: Study object (5)>]>

    res_obj=Study.objects.filter(title='西游记',id=5).first()  # 如果通过Study.objects.filter(title='西游记',id=5)取到多个值的话,那么使用.first()只取第一个值
    print(res_obj.title)   # res_obj拿到的是"一行数据"这个对象,西游记

    return HttpResponse('ok')

方式三:使用 Study.objects.get(title='西游记')  查询的结果有且仅有一个,多一个或少一个都会报错

from app01.models import Study
def select(request):
    res_get=Study.objects.get(title='西游记')
    print(res_get)

    return HttpResponse('ok')

   1.3.3.删除数据  

  方式一:Book.objects.filter(id=1).delete()  把id=1的数据给删掉

from app01.models import Study

def orm(request):
    # 删除数据
    Study.objects.filter(id=1).delete()   # filter为判断条件,查找数据表"app01_book"内,id=1的数据,delete()删除数据。
    return HttpResponse('OK')

方式二:Study.objects.filter(title='红宝书').first().delete() 如果Study.objects.filter(title='红宝书')拿到的值为多个数据,那么.first()就是只删除第一个数据

from app01.models import Study
def select(request):
    Study.objects.filter(title='红宝书').first().delete()

    return HttpResponse('ok')

   1.3.4.修改数据  

  Study.objects.filter(title='西游记',id=5).update(title='苏大大',id=5) 把title='西游记',id=5的数据修改为title='苏大大',id=5

from app01.models import Study

def select(request):
    Study.objects.filter(title='西游记',id=4).update(title='苏大大',id=4)  # 查找title='西游记',id=4的数据,然后修改为:title='苏大大',id=4

    return HttpResponse('ok')

1.3.5、ORM查询API

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

1.3.6、查看ORM转换对应的SQL执行过程(在setting.py文件内配置,然后查看请求日志就有对应的SQL)

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

1.3.7、基于双下划线的模糊查询

Book.objects.filter(price__in=[100,200,300])  # 字符串是否存在于这个列表内
Book.objects.filter(price__gt=100)    # 大于
Book.objects.filter(price__lt=100)    # 小于
Book.objects.filter(price__gte=100)   # 大于等于
Book.objects.filter(price__lte=100)   # 小于等于 
Book.objects.filter(price__range=[100,200])     # 字符串是否存在于这个range内
Book.objects.filter(title__contains="python")   # 字符串是否包含"python"
Book.objects.filter(title__icontains="python")  # 字符串"python"不区分大小写
Book.objects.filter(title__startswith="py")     # 字符串以"py"开头
Book.objects.filter(pub_date__year=2012)        # 时间段在2012年的字符

学习多表查询之前先查看下ORM的常用字段有哪些:常用字段和参数 - 刘清政 - 博客园

学习地址:Django模型层之多表操作 - 刘清政 - 博客园

二、多表操作 : 一对一,一对多,多对多(需要创建中间表才能描述多表之间的联系)   

  实例:首先来假定下面这些概念,字段和关系

   作者模型:一个作者有姓名和年龄。

   作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)

   出版商模型:出版商有名称,所在城市以及email。

   书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。

 2.1、实例(伪代码):根据多个表来分析,多表之间的对应关系

Publish        # 出版社
Book           # 书籍名称(一对多)
Author         # 书籍作者:Author与AuthorDetail表之间做了对应关系
AuthorDetail   # 作者信息:Author与AuthorDetail表之间做了对应关系
Book2Author    # 书籍与作者之间的对应关系(为了描述多对多之间的关系而创建的中间表)

CREATE TABLE publish(
                id INT PRIMARY KEY auto_increment ,
                name VARCHAR (20)
              );

CREATE TABLE book(
                id INT PRIMARY KEY auto_increment ,
                title VARCHAR (20),
                price DECIMAL (8,2),
                pub_date DATE ,
                publish_id INT ,
                FOREIGN KEY (publish_id) REFERENCES publish(id)
              );

CREATE TABLE authordetail(
                id INT PRIMARY KEY auto_increment ,
                tel VARCHAR (20)
              );

CREATE TABLE author(
                id INT PRIMARY KEY auto_increment ,
                name VARCHAR (20),
                age INT,
                authordetail_id INT UNIQUE ,
                FOREIGN KEY (authordetail_id) REFERENCES authordetail(id)
              );

CREATE  TABLE book2author(
       id INT PRIMARY KEY auto_increment ,
       book_id INT ,
       author_id INT ,
       FOREIGN KEY (book_id) REFERENCES book(id),
       FOREIGN KEY (author_id) REFERENCES author(id)
)

2.2、在Django中:models.py文件中 (这个文件主要是使用ORM时,定义的一些类)

from django.db import models

# Create your models here.
# 这个文件主要是使用ORM时,定义的一些类。

# 多表操作 :一对一,一对多,多对多之间的关联关系
# 书籍出版日期表
class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()


# 书籍名称表,与Publish表关联
class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    publish_date = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
    # auto_now_add:插入数据的当前时间,auto_now:修改数据时的时间。

    # "to='Publish'"表示和Publish表做关联,to_field='nid'表示和表里面的哪个字段做关联
    # on_delete=models.CASCADE:级联删除,只要是有(OneToOneField和ForeignKey)字段的,都得加。ManyToManyField不用加
    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)

    # 多对多的中间表,书籍表和作者表做关联
    authors = models.ManyToManyField(to='Author')
    # "ManyToManyField"(多对多):就相当于自动创建了中间表,真正到数据库执行的就是创建一个中间表
    """
        # 纯SQL的写法:
        CREATE  TABLE book2author(
           id INT PRIMARY KEY auto_increment ,  # book2author本身的主键ID
           book_id INT ,          # 字段 book_id
           author_id INT ,        # 字段 author_id
           FOREIGN KEY (book_id) REFERENCES book(id),    # 字段 book_id 关联book表下的id字段
           FOREIGN KEY (author_id) REFERENCES author(id) # 字段 author_id author表下的id字段
    )
        # Django中的写法:
         class Book2Author(models.Model):
             nid = models.AutoField(primary_key=True)
             author_id = models.ForeignKey(to='Author',to_field='nid')
             book_id = models.ForeignKey(to='Author',to_field='nid')
    """
    def __str__(self):        # 调用这个类的时候,会自动执行这个函数
        return self.name


# 书籍对应的作者信息表,与AuthorDetail关联(一对一)
class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)

    # 一对一之间关联:字段"authorDetail" 对应的就是'AuthorDetail'表的字段'nid',unique=True表示唯一约束,也就是说authorDetail只能对应一个值。
    # on_delete=models.CASCADE:级联删除,只要是有(OneToOneField和ForeignKey)字段的,都得加。ManyToManyField不用加
    # OneToOneField 内部实现的是一个FOREIGN KEY (authordetail_id) PEFERENCES authordetail(id)
    authorDetail = models.OneToOneField(to='AuthorDetail',to_field='nid',unique=True,on_delete=models.CASCADE)


# 作者本身的信息表
class AuthorDetail(models.Model):
    nid = models.AutoField(primary_key=True)
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=64)

生成的表如下:

注意事项:

  1、表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称  
  2、id 字段是自动添加的
  3、对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
  4、这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
  5、定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
  6、外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。

2.3、执行命令创建表:makemigrations --> migrate

2.4、多表的操作--添加表记录

  2.4.1、一对多的数据新增

def mui_query(request):
    # 一对多的数据新增

    # 方式一:指定"publish_id"的方式,传递出版社的信息
    book1 = Book.objects.create(name='红楼梦',price=199,publish_date='2016-09-08',publish_id=1)
    print(book1)

    # 方式二:通过获取出版社"对象",然后把该对象传值给"publish"
    pub=Publish.objects.filter(name='上海出版社').first()
    book=Book.objects.create(name='金瓶梅',price=299,publish_date='2018-09-08',publish=pub)
    print(book)

    return HttpResponse('ok')

2.4.2、一对一数据新增

def mui_query(request):
    # 一对一新增

    # 方式一: 指定"authorDetail_id"的方式,传递书籍作者的信息
    author=Author.objects.create(name='wsp',authorDetail_id=2)
    print(author)

    # 方式二:通过获取书籍作者的对象,然后把对象传值给“authorDetail”  注意:由于在models.py文件里面设置了unique=True,所以只能一个Name对应一个authorDetail_id。如果一个authorDetail_id对应多个Name的话,就会出现1062等错误。
    detail=AuthorDetail.objects.filter(addr='南京').first()
    author = Author.objects.create(name='wsp', authorDetail=detail)
    print(author)

    return HttpResponse('ok')

# 补充,数据表内的ForeignKey字段,可以拿到对应表的对象,然后查询数据
def index(request):
    book=Book.objects.filter(name="西游").first()
    print(book.publish)      # 这里拿到的是Publish表对象
    print(book.publish.name) # 通过Publish表对象就可以查到Publish表对应的值
    print(book.publish.city)

    return HttpResponse("index")

2.4.2、多对多数据新增

book.authors.remove()  将某个特定的对象从被关联对象集合中去除
book.authors.clear()    清空被关联对象集合
book.authors.set()       先清空在设置
book.authors.add()     将某个对象添加到表内,可添加多个,用逗号分隔

def mui_query(request):
    # 多对多新增

    szq=Author.objects.filter(name='szq').first()
    wsp=Author.objects.filter(name='wsp').first()
    book = Book.objects.create(name='西游记', price='298', publish_date='1991-09-01', publish_id=1)
    book.authors.add(szq)       # book.authors是多对多的中间表,有2个字段,分别是"book_id"和"author_id"。
                                  # book_id的值通过book.authors获取
                                  # author_id的值通过book.authors.add(szq)获取,其中szq=Author.objects.filter(name='szq').first()
    book.authors.add(szq,wsp)   # 也可以传递2个值(一本书对应2个作者)
    # book.authors.add(1,2)     # 也可以传对象对应的nid(主键数字)


    return HttpResponse('ok')

2.4.3、删除多表之间的关联关系

def mui_query(request):

    # 删除一个关联的数据
    szq = Author.objects.filter(name='szq').first()
    book = Book.objects.filter(name='西游记').first()
    book.authors.remove(szq)   # 把book.authors表里面,关于book_id的值为'西游记',author_id的值为"szq"的数据删除掉

    # 删除所有关联的数据
    szq = Author.objects.filter(name='szq').first()
    book = Book.objects.filter(name='西游记').first()
    book.authors.clear()   # 把book.authors表里面,关于book_id的值为'西游记'的数据全部删除掉

    # 修改一个关联的数据(执行的过程为:先执行clear,然后在执行add)
    book = Book.objects.filter(name='红宝书').first()
    book.authors.set([1,])  # 在book.authors表里面,把book_id的值为'红宝书'的这一条数据的author_id的值修改为1

    return HttpResponse('ok')

 2.4.4、核心:book.authors.all()是什么?  "book_authors" 表内,会根据"book_id"查找到对应的"author_id",然后根据"author_id"查找到"author"表内"author_id"对应的数据,并把这个数据封装成一个对象。

def mui_query(request): 
    # 核心:book.authors.all()是什么?   # 拿到的是book.authors表的所有数据。

    # 用法一:通过book.authors.all().values('name')直接获取数据
    book = Book.objects.filter(name='红宝书').first()
    ret=book.authors.all().values('name')
    # book.authors.all().values()  这里拿到的是一个字典: "{'nid': 1, 'name': 'sudada', 'authorDetail_id': 2}"
    print(ret)       # 获取到author表内作者的信息, 通过book_authors表的book_id拿到对应的author_id,然后去authors表内拿到author_id对应的"name"值。

    # 用法二:拿到一个个对象,依次取值
    book = Book.objects.filter(name='红宝书').first()
    ret = book.authors.all().first()   # 拿到的是Author这个对象
    print(ret.name)                    # 拿到的是Author这个对象下的name的值
    print(ret.authorDetail.phone)      # 拿到的是Author这个对象下的authorDetail对象,通过authorDetail对象拿到对应phone的值


    return HttpResponse('ok')

2.4、多表的操作--基于对象跨表查询(子查询--先查一个表,然后根据这个表查询另外一个表,2条SQL语句完成)

 2.4.1、基于对象多表查询 (常用)

例子一:

def mui_query(request):
    # 基于对象的多表查询
    # 查询红楼梦这本书作者的名字
    book=Book.objects.filter(name='红楼梦').first()
    res=book.authors.all()    
    # 根据book_authors这张表的book_id查询到对应的author_id字段,然后通过author_id找到对应的author表,然后取出对应的值。
    for auth in res:
        print(auth.name)

    return HttpResponse('ok')


def mui_query(request):
    # 基于对象的多表查询
    # 查询红楼梦这本书出版社的名字
    book=Book.objects.filter(name='红楼梦').first()
    print(book.publish.name)    # 北京出版社

    return HttpResponse('ok')

 2.4.2、什么是正向查询与反向查询?

正向查询:关联字段在A表中,通过A表查询B表叫做正向查询
反向查询:关联字段在A表中,通过B表查询A表叫做反向查询(反向查询按照表名小写"book_set.all()"拿到所有对象)

正向查询:通过表的字段.xxx
反向查询:通过表名+"_set"

def mui_query(request):

    '''
    正向查询:A表--查-->B表:关联字段在A表中(正向查询按照字段)
    反向查询:B表--查-->A表:B表中没有关联字段。(反向查询按照表名小写"book_set.all()"拿到所有对象)
    '''

    # 反向查询例子:查看"北京出版社"出版了几本书
    pub=Publish.objects.filter(name__contains='北京出版社').first()
    print(pub.book_set.all())   # 拿到对象:<QuerySet [<Book: 红楼梦>, <Book: 水浒传>, <Book: 西游记>, <Book: 三国>, <Book: 红楼梦>]>
    for pub_name in pub.book_set.all():  # 取出所有的值
        print(pub_name)
        # 红楼梦
        # 水浒传
        # 西游记
        # 三国
        # 红楼梦

    # 正向查询例子:查询红楼梦这本书出版社的名字
    book=Book.objects.filter(name='红楼梦').first()
    print(book.publish.name)    # 北京出版社

    return HttpResponse('ok')

2.4.3:一对一 正反向查询,一对一反向查询不需要"表名"+"_set",只需要写表名即可。

def mui_query(request):
    # 查询作者"szq"的手机号,Author表-->AuthorDetail表,一对一正向查询
    author = Author.objects.filter(name='szq').first()
    print(author.authorDetail.phone)

    # 查询手机号为"123456"对应的人的名字,AuthorDetail表-->Author表,一对一反向查询(按照表名小写)
    # 一对一反向查询不需要"表名"+"_set",只需要写表名即可。
    phone=AuthorDetail.objects.filter(phone='123456').first()
    print(phone.author.name)

    return HttpResponse('ok')

2.4.4:一对多 正反向查询

def mui_query(request):

    # 一对多的正向查询
    # 查询"红楼梦"这本书的出版社地址
    book=Book.objects.filter(name="红楼梦")
    for city in book:
        print(city.publish.city)
        # 北京
        # 北京
        # 上海

    # 一对多的反向查询:通过 "表名"+"_set"关键字,即:book_set

    # 北京出版社出版的书籍有哪些
    pub=Publish.objects.filter(name='北京出版社').first()
    for book in pub.book_set.all():
        print(book.name)
        # 红楼梦
        # 水浒传
        # 西游记
        # 三国
        # 红楼梦
    return HttpResponse('ok')

2.4.5::多对多 正反向查询

# 多对多 正反向查询

def mui_query(request):

    # 查询红楼梦这本书作者的名字,Book表-->Author表,多对多正向查询
    book=Book.objects.filter(name='红楼梦').first()
    res=book.authors.all()    
    # 根据book_authors这张表的book_id查询到对应的author_id字段,然后通过author_id找到对应的author表,然后取出对应的值。
    for auth in res:
        print(auth.name)


    # 查询作者名为"szq"写的所有书籍,Author表-->Book表,多对多反向查询
    author=Author.objects.filter(name='szq').first()
    print(author.book_set.all())  # 拿到对象: <QuerySet [<Book: 三国>, <Book: 红楼梦>]>
    for book_name in author.book_set.all():  # 取出所有的值
        print(book_name)
        # 三国
        # 红楼梦

    return HttpResponse('ok')

2.6、多表的操作--基于双下划线跨表查询join查询(一条SQL语句完成查询)  拿到的数据都是字典格式

2.6.1、一对一 正反向查询

# 一对一 正反向查询

def mui_query(request):
    # 查询作者"szq"的手机号,Author表 关联(联表) AuthorDetail表,一对一正向查询
    phone = Author.objects.filter(name='szq').values('authorDetail__phone')
    print(phone)        # <QuerySet [{'authorDetail__phone': 234567}]>
    # QuerySet通过for循环拿到具体的值。
    for n in phone:
        print(n.get("authorDetail__phone"))

    # 查询手机号为"123456"对应的人的名字,AuthorDetail表 关联(联表) Author表,一对一反向查询(按照表名小写)
    name = AuthorDetail.objects.filter(phone='123456').values('author__name')
    print(name)         # <QuerySet [{'author__name': 'sudada'}]>

    return HttpResponse('ok')

  2.6.2、一对多 正反向查询

# 一对多 正反向查询

def mui_query(request):
    # 基于双下划线的跨表查询,联表查询(一条SQL语句完成),正向查询按照字段,反向查询按照表名小写

    # 一对多的正向查询,按照__另一个表的字段:'publish__city'
    # 查询"红楼梦"这本书的出版社地址
    city=Book.objects.filter(name="红楼梦").values('publish__city')
    print(city)    # 拿到的数据都是一个个字典 <QuerySet [{'publish__city': '北京'}, {'publish__city': '北京'}, {'publish__city': '上海'}]>


    # 一对多的反向查询,
    # 上海出版社出版的书籍有哪些
    pub=Publish.objects.filter(name='北京出版社').values('book__name')
    for book in pub:
        '''
        拿到的都是一个个字典
        {'book__name': '水浒传'}
        {'book__name': '西游记'}
        {'book__name': '三国'}
        {'book__name': '红楼梦'}
        '''
        print(book['book__name'])

    return HttpResponse('ok')

  2.6.3、多对多 正反向查询

# 多对多 正反向查询

def mui_query(request):
    # 多对多的正向查询
    # 查询"红楼梦"这本书作者的名字
    # 通过Book表找到'红楼梦'这本书的id,然后通过中间表"Book_Author"找到id对应的作者author_id,再通过author_id找到作者的名字
    res=Book.objects.filter(name='红楼梦').values('authors__name')   # <QuerySet [{'authors__name': 'sudada'}, {'authors__name': 'szq'}, {'authors__name': None}, {'authors__name': 'wsp'}]>
    print(res)

    # 多对多的反向查询
    # 查询作者名为"szq"写的所有书籍,Author表 -->Book表,多对多反向查询
    res=Author.objects.filter(name='szq').values('book__name')
    print(res)   # <QuerySet [{'book__name': '三国'}, {'book__name': '红楼梦'}]>

    return HttpResponse('ok')

2.7、多表的操作--连续跨表查询

  理解:在表A的基础上,查询到表B,然后查询到表C并拿到表C的字段值(在表与表之间连续查询时会用到正向与反向查询)。values查询的数据也是在表A的基础上查询到的(在表与表之间连续查询时会用到正向与反向查询)。

正向查询:直接表字段
反向查询:表名+字段

def mui_query(request):
    # 需求:查询手机号以33开头的作者,出版过的书籍名称及书籍出版社的名字
    # 具体分析:可以通过以下4张表拿到想要的值
        # 手机号:Authordetail
        # 书名:Book
        # 竖版社名称:Publish
        # 作者:Author

    # 方法一:以AuthorDetail表为基准,查找到作者信息,那么后面的values也是以AuthorDetail表为基础查找"书籍名称及书籍出版社的名字"
    res=AuthorDetail.objects.filter(phone__startswith='33').values('author__book__name','author__book__publish__name')
    # 在AuthorDetail表里面,直接就可以通过匹配"phone"字段(正向查询)。values后面的值是以AuthorDetail表为基础,都是通过"反向查询拿到的"
    print(res)  # <QuerySet [{'author__book__name': '红楼梦', 'author__book__publish__name': '北京出版社'}]>

    # 方法二:以Author表为基准,查找到作者信息,那么后面的values也是以Author表为基础查找"书籍名称及书籍出版社的名字"
    res=Author.objects.filter(authorDetail__phone__startswith='33').values('book__name','book__publish__name')
    # 在Author表里面,需要通过authorDetail表拿到phone字段(正向查询)。values后面的值是以Author表为基础,都是通过"反向查询拿到的"
    print(res)  # <QuerySet [{'book__name': '红楼梦', 'book__publish__name': '北京出版社'}]>

    # 方法三:以Book表为基准,查找到作者信息,那么后面的values也是以Book表为基础查找"书籍名称及书籍出版社的名字"
    book=Book.objects.filter(authors__authorDetail__phone__startswith='33').values('name','publish__name')
    # 在Book表里面,需要通过authors表拿到authorDetail,然后拿到phone字段(正向查询)。values后面的值是以Book表为基础,都是通过"正向查询拿到的"
    print(book)  # <QuerySet [{'name': '红楼梦', 'publish__name': '北京出版社'}]>

    # 方法四:以Publish表为基准,查找到作者信息,那么后面的values也是以Publish表为基础查找"书籍名称及书籍出版社的名字"
    pub=Publish.objects.filter(book__authors__authorDetail__phone__startswith='33').values('book__name','name')
    # 在Publish表里面,需要通过book表拿到authors表,然后通过authors表拿到authorDetail表,然后拿到phone字段(反向查询)。values后面的值是以Publish表为基础,其中'book__name'通过"反向查询拿到的",'name'是通过"正向查询拿到的"
    print(pub)   # <QuerySet [{'book__name': '红楼梦', 'name': '北京出版社'}]>

    return HttpResponse('ok')

2.8、聚合查询 -- 使用 aggregate (聚合函数)

def mui_query(request):
    # 聚合查询 -- 使用aggregate:聚合函数
    # 需求:查询所有书籍的平均价格
    # 需要先导入常用的功能,加.减.乘.除等等~
    from django.db.models import Avg,Min,Max,Count

    # 需求:查询所有书籍的平均价格,书籍总数,书籍最高价与书籍最低价。
    res = Book.objects.all().aggregate(Avg('price'))    #书籍平均价格: {'price__avg': 221.375}
    res = Book.objects.all().aggregate(Count('name'))   #书籍总数: {'name__count': 8}
    res = Book.objects.all().aggregate(Max('price'))    #书籍最高价: {'price__max': Decimal('299.00')}
    res = Book.objects.all().aggregate(Min('price'))    #书籍最低价: {'price__min': Decimal('111.00')}

    # 一行代码实现:可以重命名
    res = Book.objects.all().aggregate(Avg('price'),Count('name'),Max('price'),Min('price'))
    print(res)   # {'price__avg': 221.375, 'name__count': 8, 'price__max': Decimal('299.00'), 'price__min': Decimal('111.00')}

    res = Book.objects.all().aggregate(pr=Avg('price'), co=Count('name'), ma=Max('price'), mi=Min('price'))
    print(res)   # {'pr': 221.375, 'co': 8, 'ma': Decimal('299.00'), 'mi': Decimal('111.00')}

    return HttpResponse('ok')

2.9、F查询与Q查询

   2.9.1、F查询 (数据表内的2个值做比较时用到)

def mui_query(request):
    # F查询
    # 需求:查询"评论数"大于"阅读数"的书籍
    from django.db.models import F
    book=Book.objects.filter(commit_num__gt=F('reat_num')).values('name')
    print(book)  # <QuerySet [{'name': '红楼梦'}, {'name': '金瓶梅'}, {'name': '水浒传'}, {'name': '红宝书'}, {'name': '三国'}, {'name': '红楼梦'}, {'name': '红楼梦'}]>

    # 需求:红宝书价格加100
    book = Book.objects.filter(name="红宝书").update(price=F('price')+100)
    print(book)

    return HttpResponse('ok')

    2.9.2、Q查询,描述一个"与,或,非"的关系。    "|"表示或者,"&"表示且,"~"表示非

def mui_query(request):
    # 需求:查询名字为红楼梦或者价格大于150的书籍
    from django.db.models import Q
    book=Book.objects.filter(Q(name="红楼梦")|Q(price__gt=200))
    print(book)  # <QuerySet [<Book: 红楼梦>, <Book: 金瓶梅>, <Book: 西游记>, <Book: 三国>, <Book: 红楼梦>, <Book: 红楼梦>]>

    # 需求:查询名字为红楼梦且价格大于190的书籍
    book = Book.objects.filter(Q(name="红楼梦") & Q(price__gt=190))
    print(book)     # <QuerySet [<Book: 红楼梦>]>

    # Q查询也可以Q套Q
    # 需求:查询名字为红楼梦且价格大于190,或者nid大于2的书籍
    book=Book.objects.filter((Q(name="红楼梦") & Q(price__gt=190)) | Q(nid__gt=5))
    print(book)  # <QuerySet [<Book: 红楼梦>, <Book: 三国>, <Book: 红楼梦>, <Book: 红楼梦>]>

    # 需求:查询名字不叫"红楼梦"的书籍
    book = Book.objects.filter(~Q(name="红楼梦"))
    print(book)  # <QuerySet [<Book: 金瓶梅>, <Book: 水浒传>, <Book: 红宝书>, <Book: 西游记>, <Book: 三国>]>

    return HttpResponse('ok')

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值