Django框架进阶4 模型层 ORM查询, 单表查询(必知避讳16条,双下划线查询), 多表查询(外键字段增删改查), 跨表查询(基于对象/双下划线的跨表查询),聚合查询, 分组查询, F与Q查询

准备工作

创建表

配置mysql数据库,即连接模块

图书管理系统表创建
一对多和一对一字段 外键字段会自动在后面加_id后缀

day53/settings.py

"""
Django settings for day53 project.

Generated by 'django-admin startproject' using Django 1.11.11.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '4y^p3gx^jfur_xe&6f_0-mtxatiimjb)16fjm2&%^i)2+rh-+_'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'day53.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'day53.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'day53',
        'USER':'root',
        "PASSWORD":'123',
        "HOST":'127.0.0.1',
        "PORT":3306,
        "CHARSET":'utf8'
    }
}


# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = '/static/'
View Code

day53/__init__.py

import pymysql
pymysql.install_as_MySQLdb()
View Code

app01/models.py

from django.db import models

# Create your models here.
class Movie(models.Model):
    title = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_time = models.DateField()  # 年月日
    """
    auto_now:每次修改数据的时候 都会自动将最新的更新时间记录下来
    auto_now_add:只在创建数据的时候将创建时间自动记录下来 之后不会自动改变
    """
    # publish_time = models.DateTimeField()  # 年月日 时分秒

    def __str__(self):
        return self.title


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_time = models.DateField(auto_now_add=True)  # 该字段新增数据自动添加 无需考虑

    # 出版社   一对多    外键字段建在多的一方
    publish = models.ForeignKey(to='Publish')
    # 作者     多对多    外键字段建在任意一方均可 推荐你建在查询频率较高的表
    authors = models.ManyToManyField(to='Author')

    # 库存数
    kucun = models.BigIntegerField(default=1000)
    # 卖出数
    maichu = models.BigIntegerField(default=1000)

    def __str__(self):
        return self.title


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


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    # 作者详情   一对一    外键字段建在任意一方均可 推荐你建在查询频率较高的表
    author_detail = models.OneToOneField(to='AuthorDetail')


class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=32)
View Code

测试用test.py文件准备

在任意一个py文件中书写以下代码
应用下的tests
或者自己新建一个

import os


if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings")   # 到这里从manage.py开头拷贝
    import django
    django.setup()  # 导入django框架配置

单表查询之必知必会16条

# 1.create()  返回值就是当前被创建数据的对象本身
# 日期可以手动给
# models.Movie.objects.create(title='3D肉蒲团s',price=999.23,publish_time='2016-1-1')
# 还可以直接传日期对象
# from datetime import date
# ctime = date.today()
# models.Movie.objects.create(title='西游记', price=666.23, publish_time=ctime)

# 2.all()            queryset对象
# res = models.Movie.objects.all()
# print(res)

# 3.filter()         queryset对象
# res = models.Movie.objects.filter(id=1)
# res = models.Movie.objects.filter(pk=1)  # pk指代的就是当前表的主键字段名  自动查找非常方便
# res = models.Movie.objects.filter(pk=1,title='python入门')  # 括号内可以放多个条件默认是AND关系

# # 只要是queryset对象  就可以通过 点query 的方式查看到获取到当前对象内部sql语句
# print(res.query)

# 4.get()      直接获取对象本身  不推荐使用  当查询条件不存在的时候直接报错
# res = models.Movie.objects.get(pk=1)
# print(res)
# print(res.title)
# print(res.price)
# res = models.Movie.objects.get(pk=1000)
# res = models.Movie.objects.filter(pk=1000)
# print(res)

# 5.values()   QuerySet对象  [{},{},{}]     获取指定字段对的数据
# res = models.Movie.objects.values('title','publish_time')
# print(res)
# print(res.query)

# 6.values_list()  QuerySet对象  [(),(),()]  获取指定字段对的数据
# res = models.Movie.objects.values_list('title','price')
# print(res)

# 7.first()    数据对象        取第一个元素对象
# res = models.Movie.objects.filter().first()
# print(res)

# 8.last()    数据对象          取最后一个元素对象
# res = models.Movie.objects.last()
# print(res)

# 9.update()  更新数据      返回值是受影响的行数
# res = models.Movie.objects.filter(pk=1).update(title='金瓶mei2')
# res = models.Movie.objects.filter(pk=1).update(title='金瓶mei2',price=666)
# print(res)

# 10.delete()  删除数据    返回值(1, {'app01.Movie': 1})  受影响的表及行数
# res = models.Movie.objects.filter(pk=3).delete()
# print(res)

# 11.count()  统计数据条数
# res = models.Movie.objects.count()
# print(res)

# 12.order_by  按照指定字段排序
# res = models.Movie.objects.order_by('price')  # 默认是升序
# res = models.Movie.objects.order_by('-price')  # 减号就是降序
# print(res)

# 13.exclude()  排除什么什么之外  少选条件
# res = models.Movie.objects.exclude(pk=1)
# print(res)

# 14.exists()   返回的是布尔值  判断前面的对象是否有数据  了解即可
# res = models.Movie.objects.filter(pk=1000).exists()
# print(res)

# 15.reverse()  反转
# res = models.Movie.objects.order_by('price').reverse()
# print(res)

# 16.distinct()  去重:去重的前提 必须是由完全一样的数据的才可以
# res = models.Movie.objects.values('title','price').distinct()
# print(res)

神奇的双下划线查询 

# 1.查询价格大于200的电影
# res = models.Movie.objects.filter(price__gt=200)
# print(res)
# 2.查询价格小于500的电影
# res = models.Movie.objects.filter(price__lt=500)
# print(res)
# 3.查询价格大于等于876.23的电影   # 由于python精确度的问题,实际是大于876.23000000000001818989403545856475830078125
# res = models.Movie.objects.filter(price__gte=876.23)
# print(res.query)
# 4.查询价格小于等于876.23的电影
# res = models.Movie.objects.filter(price__lte=500)
# print(res)
# 5.查询价格是123 或666 或876
# res = models.Movie.objects.filter(price__in=[123,666,876])
# print(res)
# 6.查询价格在200到900之间的电影  顾头也顾尾
# res = models.Movie.objects.filter(price__range=(200,900))
# print(res)
# 7.查询电影名中包含字母p的电影
"""
模糊查询:
    关键字 like
    关键符号
        %
        _
"""
# res = models.Movie.objects.filter(title__contains='p')  # 默认是区分大小写
# res = models.Movie.objects.filter(title__icontains='p')  # i忽略大小写
# print(res)

# 8.查询2014年出版的电影
# res = models.Movie.objects.filter(publish_time__year=2014)
# print(res)
# 9.查询是1月份出版的电影
# res = models.Movie.objects.filter(publish_time__month=1)
# print(res)

外键字段的增删改查

###########外键字段的增删改查############
    # 一对多  publish
    # 1.增  直接写实际的表字段 publish_id
    # models.Book.objects.create(title='三国演义',price=123.23,publish_id=2)
    # 2.增
    # publish_obj = models.Publish.objects.get(pk=1)
    # models.Book.objects.create(title='金瓶mei',price=66.66,publish=publish_obj)

    #

    #
    # models.Book.objects.filter(pk=1).update(publish_id=3)
    # publish_obj = models.Publish.objects.get(pk=4)
    # models.Book.objects.filter(pk=1).update(publish=publish_obj)

    #
    """
    外键字段在1.X版本中默认就是级联更新级联删除的
    2.X版本中 则需要你自己手动指定
        百度一大堆
    """


    # 多对多
    # 1 给书籍绑定作者关系   add
    # book_obj = models.Book.objects.filter(pk=1).first()
    # 书籍和作者的关系是由第三张表绝对 也就意味着你需要操作第三张表
    # print(book_obj.authors)  # 书籍对象点虚拟字段authors就类似于已经跨到书籍和作者的第三张关系表中
    # book_obj.authors.add(1)  # 给书籍绑定一个主键为1的作者
    # book_obj.authors.add(2,3)

    # author_obj = models.Author.objects.get(pk=1)
    # author_obj1 = models.Author.objects.get(pk=3)
    # book_obj.authors.add(author_obj)
    # book_obj.authors.add(author_obj,author_obj1)

    """
    add专门给第三张关系表添加数据
        括号内即可以传数字也可以传对象  并且都支持传多个
    """
    # 2 移除书籍与作者的绑定关系  remove
    # book_obj = models.Book.objects.filter(pk=1).first()
    # book_obj.authors.remove(2)
    # book_obj.authors.remove(1,3)
    # author_obj = models.Author.objects.get(pk=2)
    # author_obj1 = models.Author.objects.get(pk=3)
    # book_obj.authors.remove(author_obj)
    # book_obj.authors.remove(author_obj,author_obj1)
    """
    remove专门给第三张关系表移除数据
        括号内即可以传数字也可以传对象  并且都支持传多个
    """
    # 3 修改书籍与作者的关系   set
    # book_obj = models.Book.objects.filter(pk=1).first()
    # # book_obj.authors.set((3,))
    # # book_obj.authors.set((2,3))
    # author_obj = models.Author.objects.get(pk=2)
    # author_obj1 = models.Author.objects.get(pk=3)
    # # book_obj.authors.set((author_obj,))
    # book_obj.authors.set([author_obj,author_obj1])

    """
    set 修改书籍与作者的关系 
        括号内支持传数字和对象 但是需要是可迭代对象
    """
    # 4 清空书籍与作者关系
    # book_obj = models.Book.objects.filter(pk=1).first()
    # book_obj.authors.clear()  # 去第三张表中清空书籍为1的所有数据
    """
    clear()  清空关系
    不需要任何的参数
    """

跨表查询

 """
    跨表查询的方式
        1.子查询  将一张表的查询结果当做另外一张表的查询条件
            正常解决问题的思路 分步操作
        2.链表查询
            inner join
            left join
            right join
            union
    建议:在写sql语句或者orm语句的时候 千万不要想着一次性将语句写完 一定要写一点查一点再写一点
    
    
    正反向的概念
        正向
            跨表查询的时候 外键字段是否在当前数据对象中 如果在
            查询另外一张关系表  叫正向
            
        反向
            如果不在叫反向
        
        口诀
            正向查询按外键字段
            反向查询按表名小写
                                _set
    """
    # 基于对象的跨表查询(子查询)
    # 1.查询书籍pk为1的出版社名称
    # book_obj = models.Book.objects.filter(pk=1).first()
    # print(book_obj.publish)
    # print(book_obj.publish.name)
    # print(book_obj.publish.addr)

    # 2.查询书籍pk为2的所有作者的姓名
    # book_obj = models.Book.objects.filter(pk=2).first()
    # # print(book_obj.authors)  # app01.Author.None
    # print(book_obj.authors.all())
    # author_list = book_obj.authors.all()
    # for author_obj in author_list:
    #     print(author_obj.name)

    # 3.查询作者pk为1的电话号码
    # author_obj = models.Author.objects.filter(pk=1).first()
    # print(author_obj.author_detail)
    # print(author_obj.author_detail.phone)
    # print(author_obj.author_detail.addr)

    """
    正向查询的时候 当外键字段对应的数据可以有多个的时候需要加.all()
    否则点外键字典即可获取到对应的数据对象
    """
    # 4.查询出版社名称为东方出版社出版过的书籍
    # publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    # print(publish_obj.book_set)  # app01.Book.None
    # print(publish_obj.book_set.all())

    # 5.查询作者为jason写过的书
    # author_obj = models.Author.objects.filter(name='jason').first()
    # print(author_obj.book_set)  # app01.Book.None
    # print(author_obj.book_set.all())

    # 6.查询手机号为120的作者姓名
    # author_detail_obj = models.AuthorDetail.objects.filter(phone=120).first()
    # print(author_detail_obj.author)
    # print(author_detail_obj.author.name)
    # print(author_detail_obj.author.age)

    """
    基于对象的反向查询 表名小写是否需要加_set.all()
        一对多和多对多的时候需要加
        一对一不需要  
    """
    # 基于双下划线跨表查询(链表查询)
    # 1.查询书籍pk为1的出版社名称
    # 正向
    # res = models.Book.objects.filter(pk=1).values('publish__name')  # 写外键字段 就意味着你已经在外键字段管理的那张表中
    # print(res)
    # 反向
    # res = models.Publish.objects.filter(book__pk=1)  # 拿出版过pk为1的书籍对应的出版社
    # res = models.Publish.objects.filter(book__pk=1).values('name')
    # print(res)


    # 2.查询书籍pk为1的作者姓名和年龄
    # 正向
    # res = models.Book.objects.filter(pk=1).values('title','authors__name','authors__age')
    # print(res)
    # 反向
    # res = models.Author.objects.filter(book__pk=1)  # 拿出出版过书籍pk为1的作者
    # res = models.Author.objects.filter(book__pk=1).values('name','age','book__title')
    # print(res)

    # 3.查询作者是jason的年龄和手机号
    # 正向
    # res = models.Author.objects.filter(name='jason').values('age','author_detail__phone')
    # print(res)
    # 反向
    # res = models.AuthorDetail.objects.filter(author__name='jason')  # 拿到jason的个人详情
    # res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age')
    # print(res)

    # 查询书籍pk为的1的作者的手机号
    # res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
    # print(res)

    # res = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
    # print(res)

    """
    只要表之间有关系  你就可以通过正向的外键字段或者反向的表名小写 连续跨表操作
    """

聚合查询
关键字
aggregate

from django.db.models import Max,Min,Avg,Count,Sum
# 查询所有书的平均价格
# res = models.Book.objects.aggregate(avg_num=Avg('price'))
# print(res)
# 查询价格最贵的书
# res = models.Book.objects.aggregate(max_num=Max('price'))
# print(res)
# 全部使用一遍
# res = models.Book.objects.aggregate(Avg("price"), Max("price"), Min("price"),Count("pk"),Sum('price'))
# print(res)


分组查询
关键字
annotate

    # 1.统计每一本书的作者个数
    # res = models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num')
    # print(res)

    # 2.统计出每个出版社卖的最便宜的书的价格
    # res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price','book__title')
    # print(res)

    # 3.统计不止一个作者的图书
    # res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title')
    # print(res)

    # 4.查询各个作者出的书的总价格
    # res = models.Author.objects.annotate(price_sum=Sum('book__price')).values('name','price_sum')
    # print(res)

    # 5.如何按照表中的某一个指定字段分组
    """
    res = models.Book.objects.values('price').annotate()  就是以价格分组
    """


F与Q查询

    from django.db.models import F,Q
    # 1.查询库存数大于卖出数的书籍
    # res = models.Book.objects.filter(kucun__gt=F('maichu'))
    # print(res)

    # 2.将所有书的价格提高100
    # res = models.Book.objects.update(price=F('price') + 100)
    """
    帮你获取到表中某个字段对应的值
    """
    # Q能够改变查询的条件关系  and or not
    # 1.查询书的名字是python入门或者价格是1000的书籍
    # res = models.Book.objects.filter(title='python入门',price=1000)  # and关系
    # res = models.Book.objects.filter(Q(title='python入门'),Q(price=1000))  # 逗号是and关系
    # res = models.Book.objects.filter(Q(title='python入门')|Q(price=1000))  # |是or关系
    # res = models.Book.objects.filter(~Q(title='python入门')|Q(price=1000))  # ~是not关系
    # print(res.query)

    # Q的高阶用法
    # res = models.Book.objects.filter('title'='python入门')

    q = Q()
    q.connector = 'or'  # q对象默认也是and关系  可以通过connector改变or
    q.children.append(('title','python入门'))
    q.children.append(('price',1000))

    res = models.Book.objects.filter(q)
    print(res)

猜你喜欢

转载自www.cnblogs.com/ludingchao/p/12168804.html