Python进阶29-ORM介绍

2022-09-26 12:50:06 浏览数 (1)

  • pycharm连接数据库
  • orm介绍
  • 使用orm
  • orm操作增删改查
  • 小练习:图书管理系统表设计
  • 单表操作基本流程
  • 执行数据库操作
  • 基于双下划线的模糊查询
  • 多表模型
  • 添加表记录
  • 基于对象的连表查询
    • 一对一查询
    • 一对多查询
  • 多对多查询
  • 连续跨表
  • 打印Django查询数据的SQL语句
  • 基于双下划线查询
  • 聚合查询
  • 分组查询
  • F查询
  • Q查询
  • ORM反向生成models
  • ORM常用和非常用字段
  • ORM字段参数
  • ORM关系字段
  • OneToOneField
  • ManyToManyField
  • 多对多关联关系的三种方式
  • 元信息
  • 自定义字段(了解)
  • defer和only
  • 事务操作

-曾老湿, 江湖人称曾老大。


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


pycharm连接数据库


连接MySQL

下载驱动

配置连接数据库

查看数据

添加数据


连接sqllite

复制路径

下载驱动,然后把路径填入

测试连接

orm介绍


简介

ORM即Object Relational Mapping,全称对象关系映射。 当我们需要对数据库进行操作时,势必需要通过连接数据、调用sql语句、执行sql语句等操作,ORM将数据库中的表,字段,行与我们面向对象编程的类及其方法,属性等一一对应,即将该部分操作封装起来,程序猿不需懂得sql语句即可完成对数据库的操作。

优点: 1.不用写sql,不会sql的人也可以写程序 2.开发效率高

缺点: 可能sql的效率低


Python中常用ORM框架

1.Django's ORM

代码语言:javascript复制
## 优点:
1.易用,学习曲线短 
2.和Django紧密集合,用Django时使用约定俗成的方法去操作数据库 

##缺点:
1.不好处理复杂的查询,强制开发者回到原生SQL 
2.紧密和Django集成,使得在Django环境外很难使用 

2.peewee

代码语言:javascript复制
##优点:
1.Django式的API,使其易用 
2.轻量实现,很容易和任意web框架集成 

## 缺点:
1.多对多查询写起来不直观 

3.SQLAlchemy

代码语言:javascript复制
## 优点:
1.企业级 API,使得代码有健壮性和适应性 
2.灵活的设计,使得能轻松写复杂查询 

## 缺点:
1.重量级 API,导致长学习曲线 

使用orm


修改配置

默认Django连接的是sqllite3

settings.py

代码语言:javascript复制
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '10.0.0.51',
        'PORT': 3306,
        'USER': 'zls',
        'PASSWORD': '123',
        'NAME': 'zls',
    }
}

修改app01模块中的__init__.py

代码语言:javascript复制
##  告诉Django,别用MySQLdb了,使用pymysql
import pymysql
pymysql.install_as_MySQLdb()

建立映射关系

注意: 1.orm不能创建数据库 2.可以创建数据表 3.可以创建字段

models.py

代码语言:javascript复制
from django.db import models


# Create your models here.
class User(models.Model):
    # 自增int类型,主键
    id = models.AutoField(primary_key=True)

    # varchar类型长度32
    name = models.CharField(max_length=32)

    # varchar类型长度32
    pwd = models.CharField(max_length=32)

使用命令创建表

代码语言:javascript复制
MacBook-pro:login driverzeng$ python3 manage.py makemigrations

表没有创建出来,但是创建了一个文件

代码语言:javascript复制
MacBook-pro:login driverzeng$ python3 manage.py migrate

数据库迁移:

代码语言:javascript复制
## 记录数据库的变化
MacBook-pro:login driverzeng$ python3 manage.py makemigrations

## 将变化同步到数据库
MacBook-pro:login driverzeng$ python3 manage.py migrate

orm操作增删改查


查数据

views.py

代码语言:javascript复制
from django.shortcuts import render, HttpResponse, redirect
import pymysql
from app01 import models


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    elif request.method == 'POST':
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')

        user = models.User.objects.filter(name=name, pwd=pwd).first()

        if user:
            return HttpResponse('登录成功')
        else:
            return HttpResponse('用户名密码错误')


添加字段

代码语言:javascript复制
from django.db import models


# Create your models here.
class User(models.Model):
    # 自增int类型,主键
    id = models.AutoField(primary_key=True)

    # varchar类型长度32
    name = models.CharField(max_length=32)

    # varchar类型长度32
    pwd = models.CharField(max_length=32)

    # varchar类型长度64,默认值shanghai
    addr = models.CharField(max_length=64, default='shanghai')
代码语言:javascript复制
MacBook-pro:login driverzeng$ python3 manage.py makemigrations
MacBook-pro:login driverzeng$ python3 manage.py migrate


查询用户

urls.py

代码语言:javascript复制
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url('login', views.login),
    url('userlist', views.userlist),
]

userlist.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>userlist</title>
</head>
<body>
    <table border="1">
        <thead>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>password</th>
            <th>addr</th>
        </tr>
        </thead>
        <tbody>
        {% for user in user_list %}
            <tr>
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <td>{{ user.pwd }}</td>
            <td>{{ user.addr }}</td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
</body>
</html>

models.py

代码语言:javascript复制
from django.db import models


# Create your models here.
class User(models.Model):
    # 自增int类型,主键
    id = models.AutoField(primary_key=True)

    # varchar类型长度32
    name = models.CharField(max_length=32)

    # varchar类型长度32
    pwd = models.CharField(max_length=32)

    # varchar类型长度64,默认值shanghai
    addr = models.CharField(max_length=64, default='shanghai')

views.py

代码语言:javascript复制
from django.shortcuts import render, HttpResponse, redirect
from app01 import models


def userlist(request):
    if request.method == 'GET':
        ## 相当于 select * from app01_user ,查询user表中所有数据
        data = models.User.objects.all()  ## 返回结果QuerySet对象(先当成列表) [user1,user2]
        return render(request, 'userlist.html', {'user_list': data})


删除用户

userlist.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>userlist</title>
</head>
<body>
    <table border="1">
        <thead>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>password</th>
            <th>addr</th>
            <th>删除</th>
        </tr>
        </thead>
        <tbody>
        {% for user in user_list %}
            <tr>
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <td>{{ user.pwd }}</td>
            <td>{{ user.addr }}</td>
            <td><a href="/deluser?id={{ user.id }}">删除</a></td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
</body>
</html>

添加路由urls.py

代码语言:javascript复制
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url('login', views.login),
    url('userlist', views.userlist),
    url('deluser', views.deluser),
]

添加视图views.py

代码语言:javascript复制
from django.shortcuts import render, HttpResponse, redirect
from app01 import models


def deluser(request):
    if request.method == 'GET':
        id = request.GET.get('id')
        data = models.User.objects.filter(id=id).delete()  ## 返回被修改的行数
        print(data)
        return redirect('/userlist/')


增加用户

修改前端userlist.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>userlist</title>
</head>
<body>
    <table border="1">
        <thead>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>password</th>
            <th>addr</th>
            <th>删除</th>
        </tr>
        </thead>
        <tbody>
        {% for user in user_list %}
            <tr>
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <td>{{ user.pwd }}</td>
            <td>{{ user.addr }}</td>
            <td><a href="/deluser?id={{ user.id }}">删除</a></td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
    <a href="/adduser/">新增用户</a>
</body>
</html>

添加路由urls.py

代码语言:javascript复制
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url('login', views.login),
    url('userlist', views.userlist),
    url('deluser', views.deluser),
    url('adduser', views.adduser),
]

添加页面adduser.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>useradd</title>
</head>
<body>
<form action="" method="post">
    <p>用户:<input type="text" name="name"></p>
    <p>密码:<input type="password" name="password"></p>
    <p>地址:<input type="text" name="addr"></p>
    <input type="submit" value="提交">
</form>

</body>
</html>

添加视图views.py

代码语言:javascript复制
from django.shortcuts import render, HttpResponse, redirect
from app01 import models


def adduser(request):
    if request.method == 'GET':
        return render(request, 'adduser.html')
    elif request.method == 'POST':
        name = request.POST.get('name')
        password = request.POST.get('password')
        addr = request.POST.get('addr')
        ## 方法一
        # data = models.User.objects.create(name=name, pwd=password, addr=addr)
        ## 方法二
        data = models.User(name=name, pwd=password, addr=addr)
        data.save()
        print(data.name)
        return redirect('/userlist/')


修改用户

修改前端userlist.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>userlist</title>
</head>
<body>
    <table border="1">
        <thead>
        <tr>
            <th>id</th>
            <th>name</th>
            <th>password</th>
            <th>addr</th>
            <th>删除</th>
            <th>编辑</th>
        </tr>
        </thead>
        <tbody>
        {% for user in user_list %}
            <tr>
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <td>{{ user.pwd }}</td>
            <td>{{ user.addr }}</td>
            <td><a href="/deluser?id={{ user.id }}">删除</a></td>
            <td><a href="/updateuser?id={{ user.id }}">编辑</a></td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
    <a href="/adduser/">新增用户</a>
</body>
</html>

添加路由urls.py

代码语言:javascript复制
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url('login', views.login),
    url('userlist', views.userlist),
    url('deluser', views.deluser),
    url('adduser', views.adduser),
    url('updateuser', views.updateuser),
]

添加页面update.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>updateuser</title>
</head>
<body>
<form action="" method="post">
    <p>用户:<input type="text" name="name" value="{{ data.name }}"></p>
    <p>密码:<input type="password" name="password" value="{{ data.pwd }}"></p>
    <p>地址:<input type="text" name="addr" value="{{ data.addr }}"></p>
    <input type="submit" value="提交">
</form>
</body>
</html>

添加视图views.py

代码语言:javascript复制
from django.shortcuts import render, HttpResponse, redirect
from app01 import models


def updateuser(request):
    if request.method == 'GET':
        id = request.GET.get('id')
        data = models.User.objects.filter(id=id).first()
        return render(request, 'updateuser.html', {'data': data})
    if request.method == 'POST':
        ## 取出id两种方式
        # 方法一:
        ## 隐藏输入框:<p><input type="hidden" name="id" value="{{ data.id }}"></p>
        id = request.POST.get('id')

        # 方法二:
        ## form表单:<form action="/updateuser?id={{ data.id }}" method="post">
        # id2 = request.GET.get('id')
        # print(id,id2)
        name = request.POST.get('name')
        password = request.POST.get('password')
        addr = request.POST.get('addr')
        models.User.objects.filter(id=id).update(name=name, pwd=password, addr=addr)
        return redirect('/userlist/')

总结

代码语言:javascript复制
## 数据的增删改查
        ****重点****:
        1 单表查询所有用户:models.User.objects.all()
            得到的是 queryset对象(当成列表),列表里面,一个一个的对象[user1,user2]
        2 render(request, 'userlist.html', {'user_list': ret})
        3 模板里:   {% for user in user_list %}
                        #要循环的内容 
                            {{user.name}}
                     {% endfor%}
        4 get请求携带参数:
            http://127.0.0.1:8080/deleteuser/?id=1
            后台取值:request.GET.get('id')
                    request.GET['id']
        5 orm删除记录 models.User.objects.filter(id=id).delete()
            返回值:影响的行数
        6 前台post提交的数据取值:name=request.POST.get('name')
        7 orm保存:
            两种方式:
            1 user=models.User.objects.create(name=name,password=pwd,address=addr)
            2 user=models.User(name=name,password=pwd,address=addr)
              user.save()
        8 orm查询单条数据:user=models.User.objects.filter(id=id).first()
        9 orm的修改 models.User.objects.filter(id=id).update(name=name,password=pwd,address=addr)

小练习:图书管理系统表设计


设计表

代码语言:javascript复制
图书管理系统多表设计:
        图书表--->出版社表---->一对多
            一对多的关系一旦确立,关联字段写在多的一方
        图书表--->作者表------>多对多
            多对多关系,需要创建第三张表

使用orm创建表

models.py

代码语言:javascript复制
from django.db import models

class Publish(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    email = models.EmailField()
    address = models.CharField(max_length=64)


class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    address = models.CharField(max_length=64)


class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    ## 可以表示出 21.88 价格,也可以用varchar类型 max_digits:最大长度  decimal_places:小数点后面几位
    price = models.DecimalField(max_digits=5, decimal_places=2)
    ## 一对多的关系确立,关联字段写在多的一方,orm自动在publish后面加id  (publish_id)
    publish = models.ForeignKey(to='Publish', to_field='id')
    ##  对对多的关系,orm会自动创建第三张表
    authors = models.ManyToManyField(to='Author')
代码语言:javascript复制
MacBook-pro:login driverzeng$ python3 manage.py makemigrations
MacBook-pro:login driverzeng$ python3 manage.py migrate

单表操作基本流程


基本配置

settings.py 修改连接数据库的信息

代码语言:javascript复制
DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # }
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'zls_db',
        'HOST': '10.0.0.51',
        'PORT': 3306,
        'USER': 'zls',
        'PASSWORD': '123'
    }

}

创建数据库和用户

代码语言:javascript复制
mysql> create database zls_db;
Query OK, 1 row affected (0.00 sec)

mysql> grant all on *.* to zls@'%' identified by '123';
Query OK, 0 rows affected (0.00 sec)

在__init__.py中配置使用pymysql

代码语言:javascript复制
## 因为Django默认连接mysql用的是MySQLdb模块,python3.0以后,不支持MySQLdb,需要用pymysql替换MySQLdb
import pymysql
pymysql.install_as_MySQLdb()

在models.py中建表

代码语言:javascript复制
from django.db import models

# Create your models here.
class Book(models.Model):
    id=models.AutoField(primary_key=True)
    name=models.CharField(max_length=32)
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publish=models.CharField(max_length=32)
    author=models.CharField(max_length=32)
    create_date=models.DateField(null=True)

开始创建数据

代码语言:javascript复制
## 记录变化
MacBook-pro:orm driverzeng$ python3 manage.py makemigrations

## 写入数据库
MacBook-pro:orm driverzeng$ python3 manage.py migrate

## 查看未执行的记录文件
MacBook-pro:orm driverzeng$ python3 manage.py showmigrations

查看数据库

代码语言:javascript复制
mysql> show databases;
 -------------------- 
| Database           |
 -------------------- 
| information_schema |
| mysql              |
| performance_schema |
| test               |
| zls_db             |
 -------------------- 

mysql> use zls_db

mysql> show tables;
 ---------------------------- 
| Tables_in_zls_db           |
 ---------------------------- 
| app01_book                 |
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
 ---------------------------- 
11 rows in set (0.00 sec)

在python脚本中调用Django环境

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled15.settings")
    import django
    django.setup()

    from app01 import models

    books = models.Book.objects.all()
    print(books)

执行数据库操作


增加数据

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models

    ## 插入数据的两种方式:
    # 方法一:返回结果是一个对象
    book_data=models.Book.objects.create(name='红楼梦',price=23.9,publish='人民出版社',author='曹雪芹',create_date='2018-09-17')
    print(book_data.name)

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models

    ## 插入数据的两种方式:
    # 方法一:返回结果是一个对象
    # book_data=models.Book.objects.create(name='红楼梦',price=23.9,publish='人民出版社',author='曹雪芹',create_date='2018-09-17')
    # print(book_data.name)
    # 方法二:先实例化产生一个对象 ,然后调用save方法保存
    book_data=models.Book(name='金瓶没',price=99.9,publish='曾老湿出版社',author='曾老湿',create_date='2018-09-20')
    book_data.save()
    print(book_data.name)

传日期格式

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models

    ## 插入数据的两种方式:
    # 方法一:返回结果是一个对象
    book_data=models.Book.objects.create(name='红楼梦',price=23.9,publish='人民出版社',author='曹雪芹',create_date='2018-09-17')
    # print(book_data.name)
    # 方法二:先实例化产生一个对象 ,然后调用save方法保存
    book_data=models.Book(name='金瓶没',price=99.9,publish='曾老湿出版社',author='曾老湿',create_date='2018-09-20')
    # book_data.save()
    # print(book_data.name)
    import datetime
    ctime=datetime.datetime.now()
    book_data = models.Book(name='水许传', price=19.9, publish='xx出版社', author='施奈淹', create_date=ctime)
    book_data.save()
    print(book_data.name)

查询数据

代码语言:javascript复制
mysql> select * from app01_book;
 ---- ----------- ------- -------------------- ----------- ------------- 
| id | name      | price | publish            | author    | create_date |
 ---- ----------- ------- -------------------- ----------- ------------- 
|  1 | 红楼梦    | 23.90 | 人民出版社         | 曹雪芹    | 2018-09-17  |
|  2 | 红楼梦    | 23.90 | 人民出版社         | 曹雪芹    | 2018-09-17  |
|  3 | 金瓶没    | 99.90 | 曾老湿出版社       | 曾老湿    | 2018-09-20  |
|  4 | 水许传    | 19.90 | xx出版社           | 施奈淹    | 2020-06-14  |
 ---- ----------- ------- -------------------- ----------- ------------- 
4 rows in set (0.00 sec)

删除数据

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    ## 删除名字叫水许传的书
    ## 两种方式
    # 方式一:
    res = models.Book.objects.filter(name='水许传').delete()
    # print(res)
    # 方式二:
    res = models.Book.objects.filter(name='水许传').first()
    res.delete()

修改数据

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    ## 修改数据的两种方式
    # 方法一:
    res=models.Book.objects.filter(name='红楼梦').update(price=1.9)
    # 方法二:
    book=models.Book.objects.filter(name='红楼梦').first()
    book.price=99
    book.save()

查询数据(重点)

查询API

代码语言:javascript复制
<1> all():                  查询所有结果
  
<2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象
  
<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():            从返回结果中剔除重复纪录

all

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.all()
    print(book_data)

如果想打印出来所有信息 ,就要去修改class

代码语言:javascript复制
from django.db import models


# Create your models here.
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.CharField(max_length=32)
    author = models.CharField(max_length=32)
    create_date = models.DateField(null=True)

    def __str__(self):
        return '书名:%s,价格:%s' % (self.name, self.price)

filter

类似SQL语句中的where条件

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.filter(name='金瓶没').first()
    print(book_data)

get

get的使用比较少,有且只有一个结果的时候,才能使用,通常用id查询

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.get(name='金瓶没')
    print(book_data)

exclude

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.exclude(name='金瓶没')
    print(book_data)
    
    
## 多条件查询
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.exclude(name='金瓶没',price=99.9)
    print(book_data)
    
    #  查看SQL语句
    print(book_data.query)

order by

代码语言:javascript复制
##  按照价格排,升序
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    ## 升序
    book_data = models.Book.objects.all().order_by('price')
    print(book_data)


##  按照价格排,倒序   
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    #  倒序1
    book_data = models.Book.objects.all().order_by('price').reverse()
    print(book_data)
    #  倒序2
    book_data = models.Book.objects.all().order_by('-price')
    print(book_data)
    
##  只要是QuerySet对象 就可以一直'点'下去
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.all().order_by('-price').filter(name='红楼梦')
    print(book_data)
    
## 多条件排序,按照价格 和 时间排序
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.all().order_by('-price','create_date')
    print(book_data)

*********** 先修改model.py里面的内容,打印出时间 ***********
from django.db import models


# Create your models here.
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.CharField(max_length=32)
    author = models.CharField(max_length=32)
    create_date = models.DateField(null=True)

    def __str__(self):
        return '书名:%s,价格:%s,出版时间:%s' % (self.name, self.price,self.create_date)

count

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.all().count()
    print(book_data)

values

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.all().values('name','price')
    print(book_data)
    
## 相当于 select name,price from book;

value_list

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.all().value_list('name','price')
    print(book_data)
    
## 返回元组

distinct

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm.settings")
    import django
    django.setup()

    from app01 import models
    book_data = models.Book.objects.all().values('name').distinct()
    print(book_data)

基于双下划线的模糊查询

代码语言:javascript复制
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])
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)
代码语言:javascript复制
#查询价格大于89 的书
ret=models.Book.objects.filter(price__gt='89')
print(ret)

#查询价格小于89 的书
ret=models.Book.objects.filter(price__lt='89')
print(ret)

#小于等于
ret=models.Book.objects.filter(price__lte='89')

#大于等于,
ret = models.Book.objects.filter(price__gte='89')
print(ret)

#in 在XX中
ret=models.Book.objects.filter(price__in=['23.8','89','100'])
print(ret)
print(ret.query)    

#range 在XX范围内 between and
ret=models.Book.objects.filter(price__range=[50,100])
print(ret.query)

#contains  查询名字有'%红%'的书
ret=models.Book.objects.filter(name__contains='红')
print(ret)
print(ret.query)

#icontains 查询名字带p的书,忽略大小写
ret=models.Book.objects.filter(name__icontains='P')
print(ret)
print(ret.query)

#startswith  以XX开头
ret=models.Book.objects.filter(name__startswith='红')
print(ret)
print(ret.query)

#endswith
ret=models.Book.objects.filter(name__endswith='梦')
print(ret)

#pub_date__year 按年查询
ret=models.Book.objects.filter(create_data__year='2017')
print(ret)

多表模型


建表

代码语言:javascript复制
from django.db import models

# Create your models here.
class Publish(models.Model):
    # id如果不写,会自动生成,名字叫nid并且自增
    id = models.AutoField(primary_key=True)
    name =  models.CharField(max_length=32)
    addr =  models.CharField(max_length=64)
    email =  models.EmailField()

class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    ## 数字类型
    sex = models.IntegerField()
    ## 一对一,外键 ,并且有唯一性约束
    # authordetail = models.ForeignKey(unique=True)
    ## Django内置了一对一的方法
    authordetail = models.OneToOneField(to='AuthorDetail',to_field='id')

class AuthorDetail(models.Model):
    id = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5,decimal_places=2)
    ## 一对多的关系,关联字段创建在多的地方
    publish =  models.ForeignKey(to='Publish',to_field='id')
    ## 多对多的关系 ,创建在哪里 都可以
    authors = models.ManyToManyField(to='Author')

数据库迁移操作

代码语言:javascript复制
MacBook-pro:orm2 driverzeng$ python3 manage.py makemigrations
MacBook-pro:orm2 driverzeng$ python3 manage.py migrate

使用脚本运行Django操作数据库

代码语言:javascript复制
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django
    django.setup()

    from app01.models import *

添加表记录


一对多新增数据

书跟出版社是一对多

先单表给出版社(publish)添加数据,这里我就直接用SQL语句添加了。

代码语言:javascript复制
## 添加一本北京出版社出版的书
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django
    django.setup()

    from app01.models import *

    # 第一种方式
    ret = Book.objects.create(name='红楼梦',price=34.5,publish_id=1)
    print(ret.name)

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *

    ## 添加一本北京出版社出版的书
    # 第一种方式
    # ret = Book.objects.create(name='红楼梦',price=34.5,publish_id=1)
    # 第二种方式:存对象,publish=出版社对象,pk 是 primary key 主键
    publish = Publish.objects.get(pk=2)
    ret = Book.objects.create(name='西游记', price=34.5, publish=publish)
    print(ret.name)


一对多修改数据

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *

    ## 添加一本北京出版社出版的书
    # 第一种方式
    # ret = Book.objects.create(name='红楼梦',price=34.5,publish_id=1)
    # 第二种方式:存对象,publish=出版社对象,pk 是 primary key 主键
    # publish = Publish.objects.get(pk=2)
    # ret = Book.objects.create(name='西游记', price=34.5, publish=publish)

    ## 方法一:
    book = Book.objects.get(pk=1)
    # book.publish=对象
    book.publish_id=2
    book.save()
代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *

    ## 添加一本北京出版社出版的书
    # 第一种方式
    # ret = Book.objects.create(name='红楼梦',price=34.5,publish_id=1)
    # 第二种方式:存对象,publish=出版社对象,pk 是 primary key 主键
    # publish = Publish.objects.get(pk=2)
    # ret = Book.objects.create(name='西游记', price=34.5, publish=publish)

    ## 方法一:
    # book = Book.objects.get(pk=1)
    # book.publish=对象
    # book.publish_id=2
    # book.save()

    ## 方法二:
    # book = Book.objects.filter(pk=1).update(publish=出版社对象)
    book = Book.objects.filter(pk=1).update(publish_id=3)

多对多新增数据

多对多新增的API

代码语言:javascript复制
book_obj.authors.add()         # 添加
book_obj.authors.remove()      # 将某个特定的对象从被关联对象集合中去除。    ======   book_obj.authors.remove(*[])
book_obj.authors.clear()       #清空被关联对象集合
book_obj.authors.set()         #先清空再设置 

首先先给作者详情(authordetail)表和作者(author)表增加数据,单表添加就不演示了

新增

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *

    ## 给红楼梦的书添加一个作者
    zls = Author.objects.filter(name='zls').first()  ## 拿到zls对象
    lls = Author.objects.filter(name='lls').first()  ## 拿到lls对象
    book = Book.objects.filter(name='红楼梦').first()
    ## add添加多个对象,方法一:
    book.authors.add(zls, lls)

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 给红楼梦的书添加一个作者
    ## add添加作者id:方法二
    book = Book.objects.filter(name='红楼梦').first()
    book.authors.add(1,2)


多对多删除数据

remove

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 删除作者,使用对象方式
    book = Book.objects.filter(name='红楼梦').first()
    zls = Author.objects.filter(name='zls').first()  ## 拿到zls对象
    book.authors.remove(zls)

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 删除作者,使用id方式
    book = Book.objects.filter(name='红楼梦').first()
    book.authors.remove(2)

删除多个,先把数据添加回来

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 删除作者,使用id方式
    book = Book.objects.filter(name='红楼梦').first()
    book.authors.remove(1,2)
    ## 删除多个作者,使用对象方式
    book = Book.objects.filter(name='红楼梦').first()
    zls = Author.objects.filter(name='zls').first()  ## 拿到zls对象
    lls = Author.objects.filter(name='lls').first()  ## 拿到lls对象
    book.authors.remove(zls,lls)   

clear清空所有

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    # clear清空所有
    book = Book.objects.filter(name='红楼梦').first()
    book.authors.clear()

set,先清空再添加

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    book = Book.objects.filter(name='红楼梦').first()
    ## 必须添加一个可迭代对象
    book.authors.set([2,])
    book.authors.set([zls,])
    
    ## 错误写法
    book.authors.set(*[zls,])

基于对象的连表查询


前戏知识点

正向查询:author表里面有跟authordetail表的关联字段,从author表查询到authordetail表就叫做正向查询

反向查询:反过来,从authordetail表查询到author表中,就是反向查询

一对一查询

正向查询按字段,反向查询按表名小写


需求:查询zls手机号

代码语言:javascript复制
## 正向查询
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    author = Author.objects.filter(name='zls').first()
    ## author.authordetail是作者详情的对象
    authordetail = author.authordetail
    print(author.name,authordetail.phone)


需求:查询地址是哈尔滨的作者名字

代码语言:javascript复制
## 反向查询
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *

    authordetail = AuthorDetail.objects.filter(addr='哈尔滨').first()
    ## 作者对象authordetail.author
    author = authordetail.author
    print(authordetail.addr, author.name)

一对多查询

正向查询:boook表里面有跟publish表的关联字段,从book表查询到publish表就叫做正向查询

反向查询:反过来,从publish表查询到book表中,就是反向查询

正向查询按字段,反向查询按表名小写_set.all()


需求:查询红楼梦这本书出版社邮箱

正向查询

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    book = Book.objects.filter(name='红楼梦').first()
    ## book.publish就是出版社对象
    publish = book.publish
    print(publish.email)


需求:查询地址是北京的出版社出版的图书

反向查询

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    publish = Publish.objects.filter(addr='北京').first()
    ## 拿出所有的图书
    books = publish.book_set.all()
    print(books)

多对多查询

正向查询:boook表里面有跟author表的关联字段,从book表查询到author表就叫做正向查询

反向查询:反过来,从author表查询到book表中,就是反向查询

正向查询按字段,反向查询按表名小写_set.all()


需求:查询红楼梦书所有的作者

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    book = Book.objects.filter(name='红楼梦').first()
    ## 是queryset对象,可以一直点 
    print(book.authors.all())
    ## 查看查询语句
    print(book.authors.all().query)


需求:查询zls写的所有书

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    zls = Author.objects.filter(name='zls').first()
    books =  zls.book_set.all()
    ## 查看所有数据
    print(books)
    ## 打印SQL语句
    print(books.query)

连续跨表


需求:查询红楼梦所有作者的手机号

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    book =  Book.objects.filter(name='红楼梦').first()
    authors = book.authors.all()
    for  author in authors:
        authordetail = author.authordetail
        print(authordetail.phone)

总结:

代码语言:javascript复制
1 一对一
            正向:正向查询按字段
            反向:反向查询按表名小写
2 一对多
            正向:正向查询按字段
            反向:反向按表名小写_set.all()
3 多对多
            正向:正向查询按字段
            反向查询:反向按表名小写_set.all()
            
4******基于对象的查询,多次查询(子查询)

打印Django查询数据的SQL语句

将如下代码添加到settings.py文件中

代码语言:javascript复制
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

基于双下划线查询

双下划线查询就是连表查询。


一对一查询

查询zls作者的手机号

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 方法一:以author作为基表
    ret = Author.objects.filter(name='zls').values('authordetail__phone')
    print(ret)

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 方法二:以authordetail作为基表
    ret = AuthorDetail.objects.filter(author__name='zls').values('phone')
    print(ret)

查询zls作者的性别和手机号

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ret = Author.objects.filter(name='zls').values('sex','authordetail__phone')
    print(ret)

总结:

代码语言:javascript复制
# 基于双下划线的跨表查询   
        - 连表查询
        - 一对一双下划线查询
            - 正向:按字段,跨表可以在filter,也可以在values中
            - 反向:按表名小写,跨表可以在filter,也可以在values中

一对多查询

查看出版社为北京出版社的所有图书的名字和价格

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 方法一:左外连接查询
    ret = Publish.objects.filter(name='北京出版社').values('book__name','book__price')
    print(ret)

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ret = Book.objects.filter(publish__name='北京出版社').values('name','price')
    print(ret)

查询北京出版社出版并且价格大于30的书

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ret = Publish.objects.filter(name='北京出版社',book__price__gt=30).values('book__name','book__price')
    print(ret)

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ret = Book.objects.filter(publish__name='北京出版社',publish__book__price__gt=19).values('name','price')
    print(ret)


多对多查询

查询红楼梦所有作者的名字

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *

    ret = Book.objects.filter(name='红楼梦').values('authors__name')
    print(ret)

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ret = Author.objects.filter(book__name='红楼梦').values('name')
    print(ret)

查询图书价格大于30的所有作者名字

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ret = Book.objects.filter(price__gt=30).values('authors__name')
    print(ret)

    ret2 = Author.objects.filter(book__price__gt=30).values('name')
    print(ret2)


进阶练习:连续跨表

查询北京出版社出版过的所有书籍以及作者的姓名

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ret = Publish.objects.filter(name='北京出版社').values('book__name','book__authors__name')
    print(ret)

    ret2 = Book.objects.filter(publish__name='北京出版社').values('name','authors__name')
    print(ret2)
    
    ret3 = Author.objects.filter(book__publish__name='北京出版社').values('book__name','name')
    print(ret3)

三种方法 :

手机号以130开头的作者出版过的所有书籍以及出版社的名称

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ret =  AuthorDetail.objects.filter(phone__startswith=130).values('author__name','author__book__name','author__book__publish__name')
    print(ret)

聚合查询


导入聚合函数

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum

需求:计算所有图书的平均价格

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    ret = Book.objects.all().aggregate(Avg('price'))
    print(ret)


需求:计算所有图书的最高价格

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    ret = Book.objects.all().aggregate(Max('price'))
    print(ret)

如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    ret = Book.objects.all().aggregate(Max('price'),Min('price'),Avg('price'),Sum('price'))
    print(ret)

分组查询

在MySQL的SQL语句章节中,我吟过一个group by的诗。

annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。

总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。 


需求:统计每一本书作者个数

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    ret = Book.objects.all().annotate(c=Count('authors'))
    for r in ret:
        print(r.name,'====>',r.c)

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    
    ret =  Book.objects.all().annotate(c=Count('authors')).values('name','c')
    print(ret)


需求:统计每一个出版社最便宜的书

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    ret =  Publish.objects.all().annotate(n=Min('book__price')).values('name','n')
    print(ret)


需求:统计以红开头的书籍作者个数

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    ret = Book.objects.filter(name__startswith='红').annotate(c=Count('authors')).values('name','c')
    print(ret)

总结: 1.values在前,表示group by,在后表示取值 2.filter在前,表示where条件,在后表示having


需求:查询名字叫zls出书的总价

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    ret = Author.objects.all().values('pk').filter(name='zls').annotate(s=Sum('book__price')).values('name','s')
    print(ret)

需求:查询名所有作者出书的总价大于30

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    ret = Author.objects.all().values('pk').annotate(s=Sum('book__price')).filter(s__gt=30).values('name','s')
    print(ret)


需求:统计不止一个作者的书名

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    from django.db.models import Avg,Count,Max,Min,Sum
    ret = Book.objects.all().values('pk').annotate(c=Count('authors')).filter(c__gt=1).values('name','c')
    print(ret)

F查询

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。


首先添加两个字段

将下面两个字段,添加到书的表中

代码语言:javascript复制
# 阅读数
reat_num=models.IntegerField(default=0)

# 评论数
commit_num=models.IntegerField(default=0)

models.py

代码语言:javascript复制
from django.db import models


# Create your models here.
class Publish(models.Model):
    # id如果不写,会自动生成,名字叫nid并且自增
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    email = models.EmailField()


class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    ## 数字类型
    sex = models.IntegerField()
    ## 一对一,外键 ,并且有唯一性约束
    # authordetail = models.ForeignKey(unique=True)
    ## Django内置了一对一的方法
    authordetail = models.OneToOneField(to='AuthorDetail', to_field='id')

    def __str__(self):
        return self.name


class AuthorDetail(models.Model):
    id = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)


class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    ## 一对多的关系,关联字段创建在多的地方
    publish = models.ForeignKey(to='Publish', to_field='id')
    ## 多对多的关系 ,创建在哪里 都可以
    authors = models.ManyToManyField(to='Author')
    # 阅读数
    reat_num = models.IntegerField(default=0)
    # 评论数
    commit_num = models.IntegerField(default=0)
    def __str__(self):
        return self.name

数据库迁移

代码语言:javascript复制
MacBook-pro:orm2 driverzeng$ python3 manage.py makemigrations app01
MacBook-pro:orm2 driverzeng$ python3 manage.py migrate

自己随便添加点数据


需求:查询评论数大于阅读数的书

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 导入F函数
    from django.db.models import F
    #ret =  Book.objects.filter(commit_num__gt=reat_num)  ## 这是错误写法
    ## 使用F函数包裹一下
    ret1 = Book.objects.filter(commit_num__gt=F('reat_num'))
    print(ret1)


需求:把所有书的评论数加1

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 导入F函数
    from django.db.models import F

    ## 把所有书的评论数加1
    # ret = Book.objects.all().update(commit_num =1) ## 错误写法
    ret =  Book.objects.all().update(commit_num=F('commit_num') 1)


需求:把红楼梦书的阅读数减5

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 导入F函数
    from django.db.models import F
    ret = Book.objects.all().update(reat_num=F('reat_num')-5)

Q查询

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。


导入Q函数

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 导入Q函数
    from django.db.models import Q

需求:查询作者名字是zls或者名字是cls的书/font>

代码语言:javascript复制
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm2.settings")
    import django

    django.setup()

    from app01.models import *
    ## 导入Q函数
    from django.db.models import Q
    # 方法一:
    ret = Author.objects.filter(Q(name='zls')|Q(name='cls')).values('book__name')
    print(ret)
    # 方法二:
    ret2 = Book.objects.filter(Q(authors__name='zls')|Q(authors__name='cls')).values('name')
    print(ret2)

代码语言:javascript复制
## 支持下面三种写法
与:&
filter(Q(authors__name='zls')&Q(authors__name='cls'))

或:|
filter(Q(authors__name='zls')|Q(authors__name='cls'))

非:~
filter(Q(authors__name='zls')~Q(authors__name='cls'))

ORM反向生成models

在企业中,我们的表基本上一句存在了,那么我们需要反向把他们从数据库中,导成orm的对象

settings.py配置

代码语言:javascript复制
DATABASES = {
    'zls_orm': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'zls_orm',
        'USER': 'zls',
        'PASSWORD': '123',
        'HOST': '10.0.0.51',
        'PORT': '3306',
    },
}

执行反向导出命令

代码语言:javascript复制
## 执行manage.py文件
MacBook-pro:orm2 driverzeng$ python3 manage.py inspectdb --database zls_orm > app01/models.py

inspectdb:反向导出
--datatabase:指定数据库(库名一定是在settings.py文件中配置的)
app01:项目名

ORM常用和非常用字段


字段介绍

AutoField int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

IntegerField 一个整数类型,范围在 -2147483648 to 2147483647。

CharField 字符类型,必须提供max_length参数, max_length表示字符长度。

DateField 日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。

DateTimeField 日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例

代码语言:javascript复制
AutoField
int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

IntegerField
一个整数类型,范围在 -2147483648 to 2147483647。

CharField
字符类型,必须提供max_length参数, max_length表示字符长度。

DateField
日期字段,日期格式  YYYY-MM-DD,相当于Python中的datetime.date()实例。

DateTimeField
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例

ORM字段参数

代码语言:javascript复制
null
用于表示某个字段可以为空。

unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。

db_index
如果db_index=True 则代表着为此字段设置索引。

default
为该字段设置默认值。

# DateField和DateTimeField
auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段。
代码语言:javascript复制
null                数据库中字段是否可以为空
    db_column           数据库中字段的列名
    db_tablespace
    default             数据库中字段的默认值
    primary_key         数据库中字段是否为主键
    db_index            数据库中字段是否可以建立索引
    unique              数据库中字段是否可以建立唯一索引
    unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
    unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
    unique_for_year     数据库中字段【年】部分是否可以建立唯一索引

    verbose_name        Admin中显示的字段名称
    blank               Admin中是否允许用户输入为空
    editable            Admin中是否可以编辑
    help_text           Admin中该字段的提示信息
    choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                        如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)

    error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                        字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                        如:{'null': "不能为空.", 'invalid': '格式错误'}

    validators          自定义错误验证(列表类型),从而定制想要的验证规则
                        from django.core.validators import RegexValidator
                        from django.core.validators import EmailValidator,URLValidator,DecimalValidator,
                        MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                        如:
                            test = models.CharField(
                                max_length=32,
                                error_messages={
                                    'c1': '优先错信息1',
                                    'c2': '优先错信息2',
                                    'c3': '优先错信息3',
                                },
                                validators=[
                                    RegexValidator(regex='root_d ', message='错误了', code='c1'),
                                    RegexValidator(regex='root_112233d ', message='又错误了', code='c2'),
                                    EmailValidator(message='又错误了', code='c3'), ]
                            )

ORM关系字段

代码语言:javascript复制
**ForeignKey**
外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。

ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。


to
设置要关联的表

to_field
设置要关联的表的字段

related_name
反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。
代码语言:javascript复制
class Classes(models.Model):
    name = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes")

当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:

代码语言:javascript复制
models.Classes.objects.first().student_set.all()

当我们在ForeignKey字段中添加了参数 related_name 后,

代码语言:javascript复制
class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes", related_name="students")

当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:

代码语言:javascript复制
models.Classes.objects.first().students.all()
代码语言:javascript复制
related_query_name
反向查询操作时,使用的连接前缀,用于替换表名。

on_delete
  当删除关联表中的数据时,当前表与其关联的行的行为。

  models.CASCADE
  删除关联数据,与之关联也删除


  models.DO_NOTHING
  删除关联数据,引发错误IntegrityError


  models.PROTECT
  删除关联数据,引发错误ProtectedError


  models.SET_NULL
  删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)


  models.SET_DEFAULT
  删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)


  models.SET

  删除关联数据,
  a. 与之关联的值设置为指定值,设置:models.SET(值)
  b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

def func():
    return 10

class MyModel(models.Model):
    user = models.ForeignKey(
        to="User",
        to_field="id",
        on_delete=models.SET(func)
    )


# db_constraint
是否在数据库中创建外键约束,默认为True。

OneToOneField

一对一字段。

通常一对一字段用来扩展已有字段。

一对一的关联关系多用在当一张表的不同字段查询频次差距过大的情况下,将本可以存储在一张表的字段拆开放置在两张表中,然后将两张表建立一对一的关联关系。

代码语言:javascript复制
class Author(models.Model):
    name = models.CharField(max_length=32)
    info = models.OneToOneField(to='AuthorInfo')
    

class AuthorInfo(models.Model):
    phone = models.CharField(max_length=11)
    email = models.EmailField()
代码语言:javascript复制
to
设置要关联的表。

to_field
设置要关联的字段。

on_delete
同ForeignKey字段。

ManyToManyField

代码语言:javascript复制
用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系

to
设置要关联的表

related_name
同ForeignKey字段。

related_query_name
同ForeignKey字段。

symmetrical
仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。

举个例子:

代码语言:javascript复制
class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self")

此时,person对象就没有person_set属性。

代码语言:javascript复制
class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self", symmetrical=False)

此时,person对象现在就可以使用person_set属性进行反向查询。

代码语言:javascript复制
through
在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。

但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。

through_fields
设置关联的字段。

db_table
默认创建第三张表时,数据库中表的名称。

多对多关联关系的三种方式


自己创建第三张表

代码语言:javascript复制
class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")


# 自己创建第三张表,分别通过外键关联书和作者
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")

    class Meta:
        unique_together = ("author", "book")

通过ManyToManyField自动创建第三张表

代码语言:javascript复制
class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")


# 通过ORM自带的ManyToManyField自动创建第三张表
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")
    books = models.ManyToManyField(to="Book", related_name="authors")

设置ManyTomanyField并指定自行创建的第三张表

代码语言:javascript复制
class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")


# 自己创建第三张表,并通过ManyToManyField指定关联
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")
    books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
    # through_fields接受一个2元组('field1','field2'):
    # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。


class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")

    class Meta:
        unique_together = ("author", "book")

注意:

当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式。

但是当我们使用第三种方式创建多对多关联关系时,就无法使用set、add、remove、clear方法来管理多对多的关系了,需要通过第三张表的model来管理多对多关系。

元信息

代码语言:javascript复制
ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。主要字段如下:

db_table
ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名。

index_together
联合索引。

unique_together
联合唯一索引。

ordering
指定默认按什么字段排序。

只有设置了该属性,我们查询到的结果才可以被reverse()。
代码语言:javascript复制
class UserInfo(models.Model):
        nid = models.AutoField(primary_key=True)
        username = models.CharField(max_length=32)

        class Meta:
            # 数据库中生成的表名称 默认 app名称   下划线   类名
            db_table = "table_name"

            # 联合索引
            index_together = [
                ("pub_date", "deadline"),
            ]

            # 联合唯一索引
            unique_together = (("driver", "restaurant"),)
            
            ordering = ('name',)
            
            # admin中显示的表名称
            verbose_name='哈哈'

            # verbose_name加s
            verbose_name_plural=verbose_name

自定义字段(了解)

自定义char类型字段:

代码语言:javascript复制
class FixedCharField(models.Field):
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs)

    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为max_length指定的值
        """
        return 'char(%s)' % self.max_length


class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用自定义的char类型的字段
    cname = FixedCharField(max_length=25)

defer和only

defer('id','name'):取出对象,字段除了id和name都有 only('id','name'):取的对象,只有id和name 如果点,依然能点出其它列,但是不要点了,因为取没有的列,会再次查询数据库

代码语言:javascript复制
ret=models.Author.objects.only('nid')
    for i in ret:
        # 查询不在的字段,会再次查询数据库,造成数据库压力大
        print(i.name)

事务操作

代码语言:javascript复制
# 事务操作
    from django.db import transaction
    with transaction.atomic():

0 人点赞