django models的点查询/跨表查询/双下划线查询
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的点查询/跨表查询/双下划线查询的更多相关文章
- Python--day69--单表查询之神奇的双下划线
单表查询之神奇的双下划线: 单表查询之神奇的双下划线 models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 models. ...
- django基础之day04,必知必会13条,双下划线查询,字段增删改查,对象的跨表查询,双下划线的跨表查询
from django.test import TestCase # Create your tests here. import os import sys if __name__ == " ...
- Django学习——Django测试环境搭建、单表查询关键字、神奇的双下划线查询(范围查询)、图书管理系统表设计、外键字段操作、跨表查询理论、基于对象的跨表查询、基于双下划线的跨表查询
Django测试环境搭建 ps: 1.pycharm连接数据库都需要提前下载对应的驱动 2.自带的sqlite3对日期格式数据不敏感 如果后续业务需要使用日期辅助筛选数据那么不推荐使用sqlite3 ...
- django ORM模型表的一对多、多对多关系、万能双下划线查询
一.外键使用 在 MySQL 中,如果使用InnoDB引擎,则支持外键约束.(另一种常用的MyIsam引擎不支持外键) 定义外键的语法为fieldname=models.ForeignKey(to_c ...
- python-day71--django多表双下划线查询及分组聚合及F/Q查询
#====================================双下划线的跨表查询===============# 前提 此时 related_name=bookList 属性查询: # 查 ...
- ORM( ORM查询13种方法3. 单表的双下划线的使用 4. 外键的方法 5. 多对多的方法 ,聚合,分组,F查询,Q查询,事务 )
必知必会13条 <1> all(): 查询所有结果 <2> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或 ...
- 测试脚本配置、ORM必知必会13条、双下划线查询、一对多外键关系、多对多外键关系、多表查询
测试脚本配置 ''' 当你只是想测试django中的某一个文件内容 那么你可以不用书写前后端交互的形式而是直接写一个测试脚本即可 脚本代码无论是写在应用下的test.py还是单独开设py文件都可以 ' ...
- 使用admin的步骤、必知必会13条、单表的双下划线、外键的操作、多对多的操作:
MVC M: model 模型 与数据库交互 V: view 视图 HTML C:controller 控制器 流程 和 业务逻辑 MTV M:model ORM T:template 模板 HTML ...
- $Django 多对多-自定义第三张表 基于双下划线的跨表查询(补充)
自定义第三张表的好处:可以定义多个字段, 缺点:查询不方便(有方法解决) 1.第三张表设置外键,联合唯一(查询不方便) class Books(models.Model): name=models.C ...
随机推荐
- 【经验随笔】Restlet Client发送XML格式参数消息
背景介绍 Restlet Client是一款模拟客户端发送http请求的chrome插件,可以作为驱动调试后台程序,个人觉得比POSTMAN好用.CXF是一款发布restful服务的开源组件. 编写r ...
- log4net应用实践(一)
1.背景 log4net库是Apache log4j框架在Microsoft .NET平台的实现,是一个帮助程序员将日志信息输出到各种目标(控制台.文件.数据库等)的工具.它是.Net下一 ...
- asp.net mvc 5 关闭xss过滤
在控制器方法的头部添加 [ValidateInput(false)] 如果向mvc服务端提交带html标签的内容就会导致校验失败异常,从而得不到想要的结果,关闭的方法是在相应方法头部添加 ...
- redis笔记总结之redis介绍
一.Redis介绍: redis的发展历史简单的理解为因为使用类似MySql这类关系型数据库不方便进而开发的开源的.轻量级的.非关系型的,直到现在一直不断完善的一款NoSql数据库.具体的介绍大家可以 ...
- 图之单源Dijkstra算法、带负权值最短路径算法
1.图类基本组成 存储在邻接表中的基本项 /** * Represents an edge in the graph * */ class Edge implements Comparable< ...
- 20165230 学习基础和C语言基础调查
20165230 学习基础和C语言基础调查 技能学习经验 我擅长弹钢琴.小时候我曾上过很多兴趣班,比如钢琴.跳舞.书法.绘画等等,唯一坚持至今的只有钢琴.仔细一算学习钢琴至今已有12年,不能说已经精通 ...
- 前端dom元素的操作优化建议
参考自:http://blog.csdn.net/xuexiaodong009/article/details/51810252 其实在web开发中,单纯因为js导致性能问题的很少,主要都是因为DOM ...
- 在react中引入下拉刷新和上拉加载
1. 首先引入插件 import ReactPullLoad, {STATS} from 'react-pullload' 2. 初始化: constructor(props) { super(pro ...
- 微信公众号支付JSAPI网页,total_fee错误不正确,header重定向参数丢失,无法获取订单号和金额解决
微信公众号支付官方demo错误, 公众号支付只能用在微信里,也就是微信内部浏览器. 1.到WxPayHubHelper.php文件 JsApi_pub()类下createOauthUrlForCode ...
- Oracle创建用户、角色、授权、建表
oracle数据库的权限系统分为系统权限与对象权限.系统权限( database system privilege )可以让用户执行特定的命令集.例如,create table权限允许用户创建表,gr ...