django【第五篇】django orm

why

设计orm就是为了

1.简化操作数据的语法(原生sql语句真的有点复杂)

2.不同水平的人写的sql语句执行效率天差地别。

orm与表的关系

ORM的对应关系:
类 ---> 数据表
对象 ---> 数据行
属性 ---> 字段

ORM详细步骤

settings默认使用的数据库是sqlite3,其实orm一套语法可以翻译成很多种数据库的sql语句,你用什么数据库都是一套orm语法(方便吧)

我这里就使用生产中常规的mysql

1. 自己动手创建数据库
    create database 数据库名;
2. 在Django项目中设置连接数据库的相关配置(告诉Django连接哪一个数据库)
    # 数据库相关的配置
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',  # 连接的数据库类型
            'HOST': '127.0.0.1',  # 连接数据库的地址
            'PORT': 3306,  # 端口
            'NAME': "mingren01",  # 数据库名称
            'USER': 'root',  # 用户
            'PASSWORD': 'root'  # 密码
        }
    }
3. 告诉Django用pymysql代替默认的MySQLDB 连接MySQL数据库
    在项目/__init__.py文件中,写下面两句:
        import pymysql
        # 告诉Django用pymysql来代替默认的MySQLdb
        pymysql.install_as_MySQLdb()
4. 在app下面的models.py文件中定义一个类,这个类必须继承models.Model
    class 类名(models.Model):
        ...
5. 执行两个命令
    1. python3 manage.py makemigrations
    2. python3 manage.py migrate


默认生成表

 

同步model与数据库

新增表

makemigrations

可以看到,migrations目录下多了一个py文件

migrate

 mysql中生成一张表(每次migrate真正调用的的是最新的makemigrations目录下的脚本)

每次migrate后,django_migrations表会新增一条记录(用于记录每次migrate操作:app 名 、本次调用的脚本名、执行时间)

每次migrate前,都会先检测django_migrations表是否执行过makemigrations目录下的当前脚本,未执行过则执行

django就是通过makemigrations把类生成orm语言,migrate是去把orm语句翻译成sql语句

 

 删除表

注释关联的model

然后

makemigrations
migrate

修改表

makemigrations
migrate

 

补充

不要随意修改:

1.migrations目录下的orm文件,

2.django_migrations表下的数据记录

因为每次migrate(数据库同步操作),是根据migrations目录下的文件在django_migrations表是否有记录而去决定是否执行migrations目录下的文件

3.数据库只能改表数据,不要动表结构

pycharm数据库界面管理工具

 

 

 其实pycharm还集成了很多工具,这样你在做python开发的时候,就不需要打开一堆软件了

手动增加一条记录,然后查询

 

后台管理系统

模板可以从一些模板网站获取(如http://metronic.kp7.cn/   https://v3.bootcss.com/getting-started/#examples),然后修改下

目的:做这个功能,只是为了让大家熟悉orm,其实django自带的admin和更炫酷的xadmin比这个功能更强大,后台管理系统比数据库工具(如navicat)更方便

后台管理系统之单表操作

在app01目录下的models.py新增如下

from django.db import models

# Create your models here.

# 图书管理系统, 书  作者 出版社


# 出版社
class Publisher(models.Model):
    id = models.AutoField(primary_key=True)  # 自增的ID主键
    # 创建一个varchar(64)的唯一的不为空的字段
    name = models.CharField(max_length=64, null=False, unique=True)
    addr = models.CharField(max_length=128)

    def __str__(self):
        return "<Publisher Object: {}>".format(self.name)


#
class Book(models.Model):
    id = models.AutoField(primary_key=True)  # 自增的ID主键
    # 创建一个varchar(64)的唯一的不为空的字段
    title = models.CharField(max_length=64, null=False, unique=True)
    # 和出版社关联的外键字段
    publisher = models.ForeignKey(to="Publisher")

    def __str__(self):
        return "<Book Object: {}>".format(self.title)


# 作者表
class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16, null=False, unique=True)
    # 告诉ORM 我这张表和book表是多对多的关联关系,ORM自动帮我生成了第三张表
    book = models.ManyToManyField(to="Book")

    def __str__(self):
        return "<Author Object: {}>".format(self.name)
models
 makemigrations
 migrate

可以看到,新建了三个类(从前面的介绍可知,一个类对应一张表)

我们发现,多对多外键关系会多出来一张表,这张表存的是author和book表两条相关记录的id

出版社列表页面

添加数据

 

 

<!DOCTYPE html>
<!-- saved from url=(0042)https://v3.bootcss.com/examples/dashboard/ -->
<html lang="zh-CN">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="https://v3.bootcss.com/favicon.ico">

    <title>Dashboard</title>
    <!-- Bootstrap core CSS -->
    <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <!-- Custom styles for this template -->
    <link href="/static/dashboard.css" rel="stylesheet">
    <link rel="stylesheet" href="/static/fontawesome/css/font-awesome.min.css">
</head>

<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container-fluid">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                    aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="https://v3.bootcss.com/examples/dashboard/#">BMS-S10</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
                <li><a href="https://v3.bootcss.com/examples/dashboard/#">Dashboard</a></li>
                <li><a href="https://v3.bootcss.com/examples/dashboard/#">Settings</a></li>
                <li><a href="https://v3.bootcss.com/examples/dashboard/#">Profile</a></li>
                <li><a href="https://v3.bootcss.com/examples/dashboard/#">Help</a></li>
            </ul>
            <form class="navbar-form navbar-right">
                <input type="text" class="form-control" placeholder="Search...">
            </form>
        </div>
    </div>
</nav>

<div class="container-fluid">
    <div class="row">
        <div class="col-sm-3 col-md-2 sidebar">
            <ul class="nav nav-sidebar">
                <li class="active"><a href="/publisher_list/">出版社列表页</a></li>
                <li><a href="/book_list/">书籍列表</a></li>
                <li><a href="/author_list/">作者列表</a></li>
            </ul>

        </div>
        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
            <h1 class="page-header">出版社管理页面</h1>

            <div class="panel panel-primary">
                <!-- Default panel contents -->
                <div class="panel-heading">出版社列表 <i class="fa fa-thumb-tack pull-right"></i></div>
                <div class="panel-body">
                    <div class="row" style="margin-bottom: 15px">
                        <div class="col-md-4">
                            <div class="input-group">
                                <input type="text" class="form-control" placeholder="Search for...">
                                <span class="input-group-btn">
                                    <button class="btn btn-default" type="button">搜索</button>
                                </span>
                            </div><!-- /input-group -->
                        </div><!-- /.col-md-4 -->
                        <div class="col-md-1 pull-right">
                            <button class="btn btn-success" data-toggle="modal" data-target="#myModal">新增</button>
                        </div>

                    </div><!-- /.row -->

                    <table class="table table-bordered">
                        <thead>
                        <tr>
                            <th>#</th>
                            <th>id</th>
                            <th>出版社名称</th>
                            <th>操作</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% for publisher in publisher_list %}
                            <tr>
                                <td>{{ forloop.counter }}</td>
                                <td>{{ publisher.id }}</td>
                                <td>{{ publisher.name }}</td>
                                <td>
                                    <a class="btn btn-danger" href="/delete_publisher/?id={{ publisher.id }}">删除</a>
                                    <a class="btn btn-info" href="/edit_publisher/?id={{ publisher.id }}">编辑</a>
                                </td>
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>

                    <nav aria-label="Page navigation" class="text-right">
                        <ul class="pagination">
                            <li>
                                <a href="#" aria-label="Previous">
                                    <span aria-hidden="true">&laquo;</span>
                                </a>
                            </li>
                            <li><a href="#">1</a></li>
                            <li><a href="#">2</a></li>
                            <li><a href="#">3</a></li>
                            <li><a href="#">4</a></li>
                            <li><a href="#">5</a></li>
                            <li>
                                <a href="#" aria-label="Next">
                                    <span aria-hidden="true">&raquo;</span>
                                </a>
                            </li>
                        </ul>
                    </nav>
                </div>

            </div>
        </div>
    </div>
</div>


<div class="modal fade" tabindex="-1" role="dialog" id="myModal">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title">用户信息</h4>
            </div>
            <div class="modal-body">
                <form class="form-horizontal">
                    <div class="form-group">
                        <label for="inputEmail3" class="col-sm-2 control-label">邮箱</label>
                        <div class="col-sm-10">
                            <input type="email" class="form-control" id="inputEmail3" placeholder="Email">
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="inputPassword3" class="col-sm-2 control-label">密码</label>
                        <div class="col-sm-10">
                            <input type="password" class="form-control" id="inputPassword3" placeholder="Password">
                        </div>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                <button type="button" class="btn btn-primary">保存</button>
            </div>
        </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
</div><!-- /.modal -->

<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>

</body>
</html>
publisher_list.html

前端核心代码

配置url/view

效果图

 

 出版社删除页面

前端核心代码

配置url/view

 出版社编辑页面

前端核心代码

 

配置url/view

编辑页面

这里编辑在一个新页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>编辑出版社</title>
</head>
<body>

<h1>编辑出版社</h1>

<form action="/edit_publisher/" method="post">
    <input type="text" name="id" value="{{ publisher.id }}" style="display: none">
    <input type="text" name="publisher_name" value="{{ publisher.name }}">
    <input type="submit" value="提交">
    <p style="color: red">{{ error }}</p>
</form>


</body>
</html>
edit_publisher

效果图

点击编辑后跳到此页面

修改

 

提交后自动跳到列表页

 出版社添加页面

这里通过orm添加数据(也可以去编辑数据库,如果是单表或者数据简单,这两种方式都没有问题,如果复杂就得通过orm或者后台管理系统了

前端核心代码

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加出版社</title>
</head>
<body>

<h1>添加出版社</h1>

<form action="/add_publisher/" method="post">
    {% csrf_token %}
    <input type="text" name="publisher_name">
    <input type="submit" value="提交">
    <p style="color: red">{{ error }}</p>
</form>


</body>
</html>
add_publisher.html

 配置url/view

效果图

后台管理系统之跨表操作

后端平时做跨表操作的时候也很多

 跨表操作前,先回顾一下

业务是这样的

于是我们设计了三张表

from django.db import models

# Create your models here.

# 图书管理系统, 书  作者 出版社


# 出版社
class Publisher(models.Model):
    id = models.AutoField(primary_key=True)  # 自增的ID主键
    # 创建一个varchar(64)的唯一的不为空的字段
    name = models.CharField(max_length=64, null=False, unique=True)
    addr = models.CharField(max_length=128)

    def __str__(self):
        return "<Publisher Object: {}>".format(self.name)


#
class Book(models.Model):
    id = models.AutoField(primary_key=True)  # 自增的ID主键
    # 创建一个varchar(64)的唯一的不为空的字段
    title = models.CharField(max_length=64, null=False, unique=True)
    # 和出版社关联的外键字段
    publisher = models.ForeignKey(to="Publisher")

    def __str__(self):
        return "<Book Object: {}>".format(self.title)


# 作者表
class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16, null=False, unique=True)
    # 告诉ORM 我这张表和book表是多对多的关联关系,ORM自动帮我生成了第三张表
    book = models.ManyToManyField(to="Book")

    def __str__(self):
        return "<Author Object: {}>".format(self.name)
models

然后,有个ForeignKey和ManyToManyField,这两个就是用来和其它表产生联系的,前者表达的事一对多关系,后者表达的是多对多关系。

OK,那ForeignKey和ManyToManyField这个放在哪个表有要求吗?没有,只是跨表操作的时候语法有点不同。

OK,最后一点就直接告诉你了,你会发现,设计表其实很灵活:1.表只需要两两关联就能一直访问到没有之间关联的表(所以设计的表关联顺序自己定制);2.外键放在哪张表也是自定制

 数据准备

这里的书籍列表、作者列表数据就是通过url/view的orm来操作了,因为跨表添加数据有点麻烦

 

 

 

mysql表的数据存储形式

一对多关系的外键字段放在外键表里,多对多关系的外键字段放在新生成的一张表里(这是orm生成表时自动做的)

 

orm跨表操作

跨表有正向和反向操作,正向指的是从外键所在的表向另一张表操作

一对一就是特殊的一对多,因此不演示,用法和一对多一样。

查询数据

一对多关系正向跨表

  

  

多对多关系正向跨表(和一对多一样)

一对多关系反向跨表

如果外键字段加上related_name就可以省略_set

对象跨表

 

 

双下划线跨表

多对多关系反向跨表(和一对多一样)

 

新增数据

一对多

多对多 

语法一

 语法二

 

 

新增多个

 

修改数据

一对多

多对多

 

 

删除数据

级联删除

就是说,如果某张表的某些记录被删除了,那这些记录关联的其它表记录也会删除。django1.11版本的orm默认是均删除,有的版本可能是不级联删除

 一对多

一对多不存在跨表删除,只有级联删除

多对多

 

 

 关联的全部删除

Queryset方法

找到这个类的源码可得

##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
##################################################################

def all(self)
    # 获取所有的数据对象

def filter(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def select_related(self, *fields)
    性能相关:表之间进行join连表操作,一次性获取关联的数据。

    总结:
    1. select_related主要针一对一和多对一关系进行优化。
    2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。

def prefetch_related(self, *lookups)
    性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。

    总结:
    1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
    2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。

def annotate(self, *args, **kwargs)
    # 用于实现聚合group by查询

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

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
    # 用于distinct去重
    models.UserInfo.objects.values('nid').distinct()
    # select distinct nid from userinfo

    注:只有在PostgreSQL中才能使用distinct进行去重

def order_by(self, *field_names)
    # 用于排序
    models.UserInfo.objects.all().order_by('-id','age')

def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    # 构造额外的查询条件或者映射,如:子查询

    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

 def reverse(self):
    # 倒序
    models.UserInfo.objects.all().order_by('-nid').reverse()
    # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序


 def defer(self, *fields):
    models.UserInfo.objects.defer('username','id')
    或
    models.UserInfo.objects.filter(...).defer('username','id')
    #映射中排除某列数据

 def only(self, *fields):
    #仅取某个表中的数据
     models.UserInfo.objects.only('username','id')
     或
     models.UserInfo.objects.filter(...).only('username','id')

 def using(self, alias):
     指定使用的数据库,参数为别名(setting中的设置)


##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################

def raw(self, raw_query, params=None, translations=None, using=None):
    # 执行原生SQL
    models.UserInfo.objects.raw('select * from userinfo')

    # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
    models.UserInfo.objects.raw('select id as nid from 其他表')

    # 为原生SQL设置参数
    models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

    # 将获取的到列名转换为指定列名
    name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
    Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

    # 指定数据库
    models.UserInfo.objects.raw('select * from userinfo', using="default")

    ################### 原生SQL ###################
    from django.db import connection, connections
    cursor = connection.cursor()  # cursor = connections['default'].cursor()
    cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    row = cursor.fetchone() # fetchall()/fetchmany(..)


def values(self, *fields):
    # 获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
    # 获取每行数据为元祖

def dates(self, field_name, kind, order='ASC'):
    # 根据时间进行某一部分进行去重查找并截取指定内容
    # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
    # order只能是:"ASC"  "DESC"
    # 并获取转换后的时间
        - year : 年-01-01
        - month: 年-月-01
        - day  : 年-月-日

    models.DatePlus.objects.dates('ctime','day','DESC')

def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
    # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
    # kind只能是 "year", "month", "day", "hour", "minute", "second"
    # order只能是:"ASC"  "DESC"
    # tzinfo时区对象
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))

    """
    pip3 install pytz
    import pytz
    pytz.all_timezones
    pytz.timezone(‘Asia/Shanghai’)
    """

def none(self):
    # 空QuerySet对象


####################################
# METHODS THAT DO DATABASE QUERIES #
####################################

def aggregate(self, *args, **kwargs):
   # 聚合函数,获取字典类型聚合结果
   from django.db.models import Count, Avg, Max, Min, Sum
   result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
   ===> {'k': 3, 'n': 4}

def count(self):
   # 获取个数

def get(self, *args, **kwargs):
   # 获取单个对象

def create(self, **kwargs):
   # 创建对象

def bulk_create(self, objs, batch_size=None):
    # 批量插入
    # batch_size表示一次插入的个数
    objs = [
        models.DDD(name='r11'),
        models.DDD(name='r22')
    ]
    models.DDD.objects.bulk_create(objs, 10)

def get_or_create(self, defaults=None, **kwargs):
    # 如果存在,则获取,否则,创建
    # defaults 指定创建时,其他字段的值
    obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

def update_or_create(self, defaults=None, **kwargs):
    # 如果存在,则更新,否则,创建
    # defaults 指定创建时或更新时的其他字段
    obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

def first(self):
   # 获取第一个

def last(self):
   # 获取最后一个

def in_bulk(self, id_list=None):
   # 根据主键ID进行查找
   id_list = [11,21,31]
   models.DDD.objects.in_bulk(id_list)

def delete(self):
   # 删除

def update(self, **kwargs):
    # 更新

def exists(self):
   # 是否有结果

QuerySet方法大全

 

posted @ 2019-03-08 20:55  沐风先生  阅读(419)  评论(0编辑  收藏  举报