django models

在日常的编程中,我们需要建立数据库模型

而往往会用到表与表之间的关系,这就比单表取数据要复杂一些

在多表之间发生关系的情形下,我们如何利用models提供的API的特性获得需要的数据呢

我们先从对象和查询集说开去

查询结果有时是对象/有时是查询集

我们只需要知道 ,只有get方法或者对查询集合进行切片,比如objects.all()[0] 或者 objects.all().first()等得到的是对象,其他得到的都是queryset

我们举个例子看下

# -*- coding: utf-8 -*-
from __future__ import unicode_literals from django.db import models # Create your models here. class Province(models.Model):
name = models.CharField(max_length = 20) def __str__(self):
return self.name.decode("utf-8") def __unicode(self):
return self.name.decode("utf-8") class City(models.Model):
name = models.CharField(max_length = 20)
province = models.ForeignKey('Province', to_field = 'id') def __str__(self):
return self.name.decode("utf-8") def __unicode(self):
return self.name.decode("utf-8")

从上面的ORM类的建立可以看到,city通过外键和province表发生关系

BTW,我们通常(建议)都是把外键放在一对多的关系的多的那一方,因为这样是方便的。否则,放在少的那一方,因为少的一方是一对多的关系,数据库里要繁琐的建立这种关系:A省-B市,

A省-C市 A省-D市等等,这样会很麻烦

而放在多的一方,就只要建立一个 市-省的关系就行了

然后我们建立一些测试数据

我们可以通过django的shell,通过命令行的方式建立测试数据

python manage.py shell

这样就可以进入python 的django 的命令行用代码的方式建立测试数据

[root@khao test_models]# python manage.py  makemigrations
Migrations for 'app01':
app01/migrations/0001_initial.py
- Create model City
- Create model Province
- Add field province to city
[root@khao test_models]# python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying app01.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OKre
[root@khao test_models]# python manage.py shell
Python 2.7.5 (default, Aug 4 2017, 00:39:18)
Type "copyright", "credits" or "license" for more information. IPython 5.5.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details. In [1]: from app01 import models In [2]: models.Province.objects.create(name = u"河北")
Out[2]: <Province: 河北> In [3]: models.Province.objects.create(name = u"山东")
Out[3]: <Province: 山东> In [4]: h = models.Province.objects.get(name = u"河北") In [5]: s = models.Province.objects.get(name = u"山东")
In [8]: models.City.objects.create(name = u"石家庄",province = h)
Out[8]: <City: 石家庄> In [9]: models.City.objects.create(name = u"张家口",province = h)
Out[9]: <City: 张家口> In [10]: models.City.objects.create(name = u"邢台",province = h)
Out[10]: <City: 邢台> In [11]: models.City.objects.create(name = u"济南",province = s)
Out[11]: <City: 济南> In [12]: models.City.objects.create(name = u"青岛",province = s)
Out[12]: <City: 青岛>

然后,我们进入数据库的命令行,看看数据和表结构:

[root@khao ~]# mysql
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 32
Server version: 5.6.39 MySQL Community Server (GPL) Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> set names utf8;
Query OK, 0 rows affected (0.00 sec) mysql> use test_models_db;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A Database changed
mysql> select * from app01_province;
+----+--------+
| id | name |
+----+--------+
| 1 | 河北 |
| 2 | 山东 |
+----+--------+
2 rows in set (0.00 sec) mysql> select * from app01_city;
+----+-----------+-------------+
| id | name | province_id |
+----+-----------+-------------+
| 1 | 石家庄 | 1 |
| 2 | 张家口 | 1 |
| 3 | 邢台 | 1 |
| 4 | 济南 | 2 |
| 5 | 青岛 | 2 |
+----+-----------+-------------+
5 rows in set (0.00 sec)

我们可以看到,在有外键的表里,也就是我们创建了

province = models.ForeignKey('Province', to_field = 'id')

这个字段的表里,django会自动给我们创建一个外键关联字段province_id字段

正是通过这个字段,city这张表找到了每行即每个城市对应的省的id,也即每个城市和省进行了关联

正向查找

我们在很多讲述django的文章/资料/教程里面,都会看到正向查找和反向查找的概念

那么什么是正向查找呢

正向查找就是通过数据库就有的字段去查找,比如

我们可以通过city这张表找到省(province),因为city表里有province这个外键

In [13]: models.City.objects.get(name = u"石家庄").province
Out[13]: <Province: 河北> In [14]: models.City.objects.get(name = u"石家庄").province_id
Out[14]: 1L In [17]: models.City.objects.get(name = u"石家庄").province.id
Out[17]: 1L
> 在上面的查找中,都是通过city表本身就有的字段进行出发查找的,这就叫做正向查找

跨表查询

其实在上面的查询过程,已经发生了跨表查询

什么是跨表查询

models.City.objects.get(name = u"石家庄").province_id 不是跨表查询,因为这个字段在city表结构里本来就有(上面已经演示了表结构)

models.City.objects.get(name = u"石家庄").province.id 是跨表查询,这个字段在city 表结构里本来没有,是通过什么方式查到的呢?

models.City.objects.get(name = u"石家庄") 拿到了 石家庄这个城市对象

models.City.objects.get(name = u"石家庄").province 拿到了石家庄这个城市对应的省,即河北这个对象

models.City.objects.get(name = u"石家庄").province.id 从河北这个对象里,查到了河北这个省对象自身的属性id

如何观察和验证上述结论(如何观察ORM对应的SQL语句)

这里有个小技巧

我们可以在django 命令行里面导入

from django.db import connection

然后编写ORM语句执行

再 敲入 connection.queries

就会看到之前所有的ORM对应的sql语句

我们看下 上面的查询过程

In [1]: from app01 import models

In [2]: models.City.objects.get(name = u"石家庄").province_id
Out[2]: 1L In [3]: from django.db import connection In [4]: connection.queries
Out[4]:
[{u'sql': u'SELECT @@SQL_AUTO_IS_NULL', u'time': u'0.000'},
{u'sql': u'SELECT VERSION()', u'time': u'0.000'},
{u'sql': u"SELECT `app01_city`.`id`, `app01_city`.`name`, `app01_city`.`province_id` FROM `app01_city` WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84'",
u'time': u'0.000'}]

我们可以看到,models.City.objects.get(name = u"石家庄").province_id这个ORM操作,是从app01_city这张表查出来的,而且app01_city这张表有province_id这个字段,所以不是跨表查询

我们再继续看 models.City.objects.get(name = u"石家庄").province.id

{u'sql': u"SELECT `app01_city`.`id`, `app01_city`.`name`, `app01_city`.`province_id` FROM `app01_city` WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84'",
u'time': u'0.002'},
{u'sql': u'SELECT `app01_province`.`id`, `app01_province`.`name` FROM `app01_province` WHERE `app01_province`.`id` = 1',
u'time': u'0.000'}]

可以清楚的看到,由于city这张表没有province.id这个字段,为此,先拿到models.City.objects.get(name = u"石家庄").province这个对象,也就是到province这张表去查找

在app01_province这张表查到了对应的id这个字段

什么时候用双下划线查找

凡是通过queryset对象去查找的时候,就用双下划线查找

前面已经讲过,在django的ORM里面,除了get拿到的是对象,其他查到到都是queryset,除非对queryset进行切片/索引取值,比如,objects.all()[0]这样拿到的才是对象,objects.all()拿到的是queryset

凡是get查到的对象,都可以通过点号 . 的方式进行逐级查找,比如上面的跨表查询 models.City.objects.get(name = u"石家庄").province.id

我们举几个双下划线查找的例子

In [10]: models.City.objects.all().filter(name = "石家庄")
Out[10]: <QuerySet [<City: 石家庄>]>

首先我们看到上面拿到的是QuerySet类型的

我们先看下上面的ORM语句对应的sql语句

In [4]: connection.queries
Out[4]:
[{u'sql': u'SELECT @@SQL_AUTO_IS_NULL', u'time': u'0.000'},
{u'sql': u'SELECT VERSION()', u'time': u'0.000'},
{u'sql': u"SELECT `app01_city`.`id`, `app01_city`.`name`, `app01_city`.`province_id` FROM `app01_city` WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84' LIMIT 21",
u'time': u'0.000'}]

可以看到,这sql对应的显然是一个查询集合(QuerySet)。因为都用到了LIMIT 限定条件

我们再继续看

In [5]: models.City.objects.all().filter(name = "石家庄").values("province__name")
Out[5]: <QuerySet [{'province__name': u'\u6cb3\u5317'}]> In [6]: connection.queries
Out[6]:
[{u'sql': u'SELECT @@SQL_AUTO_IS_NULL', u'time': u'0.000'},
{u'sql': u'SELECT VERSION()', u'time': u'0.000'},
{u'sql': u"SELECT `app01_city`.`id`, `app01_city`.`name`, `app01_city`.`province_id` FROM `app01_city` WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84' LIMIT 21",
u'time': u'0.000'},
{u'sql': u"SELECT `app01_province`.`name` FROM `app01_city` INNER JOIN `app01_province` ON (`app01_city`.`province_id` = `app01_province`.`id`) WHERE `app01_city`.`name` = '\u77f3\u5bb6\u5e84' LIMIT 21",
u'time': u'0.002'}]

看最后的SQL 可以看到,这种双下划线查询一般对应的都是各种类型的join联表查询,即联合两张表进行联合查询

XXX_set跨表查询

除了上面说到的通过对象在正向查找里面的点查询方式的跨表查询和通过QuerySet进行双下划线的方式查询

我们再简单总结下上面两种方式

  • 对象方式的点查询进行跨表查询

简单来说,凡是试图进行跨表查询,总得要两个表有某种关系吧,才能进行跨表查询,在有外键关系的两个表里,一般是在一对多的多的那一方里,通过某个对象,拿到与其有外键关系的别的表的对象,在通过这个对象进行查询(其实就是通过拿到的对象---别的表的,进行在别的表的查询,就是点方式的跨表查询)

  • 双下划线方式的跨表查询

这个一般都是通过从QuerySet类型的查询集出发,通过双下划线的方式查到别的表的属性,并且一般都是把双下划线作为某种属性的过滤条件或者是取出值的限定字段来使用的,比如可以使用在filter过滤中或者使用在values过滤中

比如 models.City.objects.filter(name = "石家庄").values("province__name")

或者也可以直接在filter的条件里面使用双下划线过滤

In [13]: models.City.objects.filter(province__name = u"河北")
Out[13]: <QuerySet [<City: 石家庄>, <City: 邢台>, <City: 张家口>]>
In [39]: models.City.objects.filter(province__name = "河北").values("province__name")
Out[39]: <QuerySet [{'province__name': u'\u6cb3\u5317'}, {'province__name': u'\u6cb3\u5317'}, {'province__name': u'\u6cb3\u5317'}]>
  • XXX_set方式跨表查询

这个一般都用在一对多的一的那一方

查询这个一的那一方对应的多的集合

而且一般只有对象才可以使用XXX_set属性查找,不能从QuerySet类型进行XXX_set查找

In [46]: models.Province.objects.first().city_set.all()
Out[46]: <QuerySet [<City: 石家庄>, <City: 邢台>, <City: 张家口>]>

django models的点查询/跨表查询/双下划线查询的更多相关文章

  1. Python--day69--单表查询之神奇的双下划线

    单表查询之神奇的双下划线: 单表查询之神奇的双下划线 models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 models. ...

  2. django基础之day04,必知必会13条,双下划线查询,字段增删改查,对象的跨表查询,双下划线的跨表查询

    from django.test import TestCase # Create your tests here. import os import sys if __name__ == " ...

  3. Django学习——Django测试环境搭建、单表查询关键字、神奇的双下划线查询(范围查询)、图书管理系统表设计、外键字段操作、跨表查询理论、基于对象的跨表查询、基于双下划线的跨表查询

    Django测试环境搭建 ps: 1.pycharm连接数据库都需要提前下载对应的驱动 2.自带的sqlite3对日期格式数据不敏感 如果后续业务需要使用日期辅助筛选数据那么不推荐使用sqlite3 ...

  4. django ORM模型表的一对多、多对多关系、万能双下划线查询

    一.外键使用 在 MySQL 中,如果使用InnoDB引擎,则支持外键约束.(另一种常用的MyIsam引擎不支持外键) 定义外键的语法为fieldname=models.ForeignKey(to_c ...

  5. python-day71--django多表双下划线查询及分组聚合及F/Q查询

    #====================================双下划线的跨表查询===============# 前提 此时 related_name=bookList 属性查询: # 查 ...

  6. ORM( ORM查询13种方法3. 单表的双下划线的使用 4. 外键的方法 5. 多对多的方法 ,聚合,分组,F查询,Q查询,事务 )

    必知必会13条 <1> all(): 查询所有结果 <2> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或 ...

  7. 测试脚本配置、ORM必知必会13条、双下划线查询、一对多外键关系、多对多外键关系、多表查询

    测试脚本配置 ''' 当你只是想测试django中的某一个文件内容 那么你可以不用书写前后端交互的形式而是直接写一个测试脚本即可 脚本代码无论是写在应用下的test.py还是单独开设py文件都可以 ' ...

  8. 使用admin的步骤、必知必会13条、单表的双下划线、外键的操作、多对多的操作:

    MVC M: model 模型 与数据库交互 V: view 视图 HTML C:controller 控制器 流程 和 业务逻辑 MTV M:model ORM T:template 模板 HTML ...

  9. $Django 多对多-自定义第三张表 基于双下划线的跨表查询(补充)

    自定义第三张表的好处:可以定义多个字段, 缺点:查询不方便(有方法解决) 1.第三张表设置外键,联合唯一(查询不方便) class Books(models.Model): name=models.C ...

随机推荐

  1. Spring Boot 文件上传原理

    首先我们要知道什么是Spring Boot,这里简单说一下,Spring Boot可以看作是一个框架中的框架--->集成了各种框架,像security.jpa.data.cloud等等,它无须关 ...

  2. 魔改版ss-panel v3前端配置文件

    配置文件所在目录:网站根目录/config/.config.php <?php // ss-panel v3 配置 // // !!! 修改此key为随机字符串确保网站安全 !!! $Syste ...

  3. sql server 2008 sql prompt 自动提示

    sql server 2008 在编写SQL脚本的时候,总是希望能提示一些信息,或者自动提示需要查询的表的名字,或者表的基本信息,sql server默认会有一些提示的,如果没有可以设置工具--> ...

  4. html备战春招の一

    html不是一种编程语言,而是一种标记语言,通过使用标签来标记网页. 对于中文网页需要使用 <meta charset="utf-8"> 声明编码,否则会出现乱码.有些 ...

  5. 基于jenkins的go语言项目自动化发布遇到的坑

    之前我们研究dep,就是为了有一天可以实现go语言项目在我们系统里的CI. 之前联物科技的项目主要是使用java作为后端开发语言,基于jenkins的自动发布,使用了pipeline编写脚本,从拉取代 ...

  6. 关于Android SDK Manager无法更新的解决办法

    网上其实也提供了很多的解决方法,但是很多方法下载已经失效了,这里我提供一个解决方法: 1.打开SDK Manager,点击Tools-Options... 2.做如下修改: (这张图片是参考其他网站的 ...

  7. 【Unity与23种设计模式】访问者模式(Visitor)

    GoF中定义: "定义一个能够在一个对象结构中对于所有元素执行的操作.访问者让你可以定义一个新的操作,而不必更改到被操作元素的类接口." 暂时没有完全搞明白 直接上代码 //访问者 ...

  8. 3D模型展示以及体积、表面积计算

    本文原创 如转载请注明出处!!! 本博客地址http://www.cnblogs.com/we-jack 本文原创,如果有同样需求的小伙伴请第一时间联系我 或者在留言区留言 上次为大家提供了3D模型的 ...

  9. JWT实战:使用axios+PHP实现登录认证

    上一篇文中,我们学习了什么是JWT(Json Web Token),今天我们来结合实例给大家讲述JWT的实战应用,就是如何使用前端Axios与后端PHP实现用户登录鉴权认证的过程. 查看演示 下载源码 ...

  10. NET Core2.0 Memcached踩坑,基于EnyimMemcachedCore整理MemcachedHelper帮助类。

    DotNetCore2.0下使用memcached缓存. Memcached目前微软暂未支持,暂只支持Redis,由于项目历史原因,先用博客园开源项目EnyimMemcachedCore,后续用到的时 ...