07.模型详解 -- 多表查询

通过对象进行多表查询

  • 由一到多查询:一类模型类对象名.小写多类模型类类名_set.查询函数()
    通过对象查询分成2步,先查到某本图书,再通过该图书对象查询该图书中的人物对象
    实例演练: 查询id为1的图书中,所有人物的信息
    修改views.py文件
def index(request):
    book = BookInfo.objects.get(id=1)
    persons = book.personinfo_set.all()

    ret = ''
    for p in persons:
        ret += str(p.id) + ", " + p.pname + ", " + str(p.pgender) \
               + ", " + p.pcomment + ", " + str(p.hbook_id)
        ret += '<br>'

    return HttpResponse(ret)
1, 曹操, True, 字孟德, 1
2, 刘备, True, 字玄德, 1
3, 诸葛亮, True, 字孔明, 1
4, 孙权, True, 字仲谋, 1
  • 由多到一查询:多类模型类对象名.多类模型类中外键对应的属性名
    通过对象查询分成2步,先查到某个人物,再通过该人物对象查询其所在的图书对象
    实例演练: 查询id为1的人物所在的图书信息
    修改views.py文件
def index(request):
    person = PersonInfo.objects.get(id=1)
    book = person.hbook

    ret = book.btitle + ", " + str(book.bpub_date) + ", " \
          + str(book.bread) + ", " + str(book.bcomment)

    return HttpResponse(ret)
三国演义, 1980-05-01, 12, 34

通过模型类进行多表查询

  • 由一到多查询:一类模型类名.objects.filter(小写多类模型类名__属性名__条件运算符 = 值)
    实例演练: 查询图书,要求图书中人物描述包含'德'字
    修改views.py文件
def index(request):
    books = BookInfo.objects.filter(personinfo__pcomment__contains='德')

    ret = ''
    for book in books:
        ret += book.btitle + ", " + str(book.bpub_date) + ", " \
               + str(book.bread) + ", " + str(book.bcomment)
        ret += '<br>'
    return HttpResponse(ret)
三国演义, 1980-05-01, 12, 34
三国演义, 1980-05-01, 12, 34

如果没有__运算符部分,表示等于

  • 由多到一查询:多类模型类名.objects.filter(多类模型类外键的属性名__一类模型类属性名__条件运算符 = 值)
    实例演练: 查询“西游记”中的所有人物
    修改views.py文件
def index(request):
    persons = PersonInfo.objects.filter(hbook__btitle='西游记')

    ret = ''
    for p in persons:
        ret += str(p.id) + ", " + p.pname + ", " + str(p.pgender) \
               + ", " + p.pcomment + ", " + str(p.hbook_id)
        ret += '<br>'

   return HttpResponse(ret)
14, 孙悟空, True, 唐僧的大徒弟, 4
15, 唐僧, True, 玄奘, 4
16, 猪八戒, True, 悟能, 4
17, 沙僧, True, 沙悟净, 4

多对多查询

  • 对于复杂的多对多查询,可以使用原生SQL来处理,参考文档
  • 语法格式:模型类名.objects.raw('SQL语句', params=None, translations=None)
  • 实例演练: 查询所有新闻标题、内容及其类型
    Python Console中执行
list = NewsInfo.objects.raw('''SELECT * 
                               FROM app_newsinfo n, app_typeinfo t, app_newsinfo_ntype nt 
                               WHERE n.id = nt.newsinfo_id 
                               AND t.id = nt.typeinfo_id''')
  • 用来查询的模型类,使用NewsInfoTypeInfo都可以
  • 返回值的类型为RawQuerySet
for l in list:
    print(l.ntitle, l.ncontent, l.tname)
互联网科技 马云已退出阿里旗下5家公司:官方称没这个打算 科技
宇宙探索 平行时空、多元宇宙真的存在?令人细思极恐 科技
中国军情 中国海军万吨巨舰的起点 原型就是这艘民船 科技
国际军情 美国国会议员:前总统吉米·卡特请缨亲赴朝鲜 军事
欧洲 欧盟高官警告:特朗普不要搞垮了世贸体系 军事
数码产品 苹果官方科普来了:全面认识Apple ID 国际
国际军情 美国国会议员:前总统吉米·卡特请缨亲赴朝鲜 国际
欧洲 欧盟高官警告:特朗普不要搞垮了世贸体系 国际
美国 伊拉克北部发生汽车炸弹袭击致1死7伤 国际

自连接

对于地区信息数据表,表结构非常相似,可以设计成一张表,通过自连接产生相关信息

上图中,通过查询地区表(AreaInfo),可以产生省表和市区表
还可以通过自连接产生省市区对照表

通过Django实现自连接

  • 修改models.py文件,添加AreaInfo模型类。
    外键关连表使用self指向本类,nullblank参数允许为空,因为一级数据没有父数据
定义地区模型类AreaInfo,存储省、市、区县信息

class AreaInfo(models.Model):
    atitle = models.CharField(max_length=30)  # 地区名称
  
    # 上级地区
    aParent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE) 
  • 进行数据迁移,生成数据表
python manage.py makemigrations
python manage.py migrate

生成数据表app_areainfo,并将外键aParent_id关联到本身的主键id

  • 添加测试数据
    SQLyog中执行插入语句
INSERT INTO app_areainfo VALUES
('210000', '辽宁省', NULL),
('210100', '沈阳市', '210000'),
('210102', '和平区', '210100'),
('210103', '沈河区', '210100'),
('210104', '大东区', '210100'),
('210105', '皇姑区', '210100'),
('210106', '铁西区', '210100'),
('210200', '大连市', '210000'),
('210202', '中山区', '210200'),
('210203', '西岗区', '210200'),
('210204', '沙河口区', '210200'),
('210211', '甘井子区', '210200'),
('210300', '鞍山市', '210000'),
('210302', '铁东区', '210300'),
('210303', '铁西区', '210300'),
('210304', '立山区', '210300'),
('210311', '千山区', '210300'),
('220000', '吉林省', NULL),
('220100', '长春市', '220000'),
('230000', '黑龙江省', NULL),
('230100', '哈尔滨市', '230000')
  • 实例演练: 使用SQL语句分别查询省表、市区表、省市对照表
SELECT * FROM app_areainfo WHERE aParent_id IS NULL
SELECT * FROM app_areainfo WHERE aParent_id IS NOT NULL
SELECT p.atitle, c.atitle 
FROM app_areainfo p, app_areainfo c
WHERE p.id = c.aParent_id

通过Django实现查询
语法格式:查询上级,area.aParent,查询下级,area.areainfo_set.all()

  • 实例演练: 查询沈阳市的上级地区
    修改views.py文件
def area(request):
    area = AreaInfo.objects.get(pk=210100)  # 获得沈阳市的对象

    ret = area.atitle + ", " + area.aParent.atitle  # 通过对象查询上级
    return HttpResponse(ret)
沈阳市, 辽宁省
  • 实例演练: 查询辽宁省的下级地区
    修改views.py文件
def area(request):
    area = AreaInfo.objects.get(pk=210000)  # 获得辽宁省的对象

    ret = ""
    for area in area.areainfo_set.all():  # 通过对象查询下级
        ret += area.atitle
        ret += '<br>'

    return HttpResponse(ret)
沈阳市
大连市
鞍山市

课堂练习:

  • 创建empdept表,表结构如下图:
  • 添加测试数据
INSERT  INTO app_dept VALUES 
(10,'ACCOUNTING','NEW YORK'),
(20,'RESEARCH','DALLAS'),
(30,'SALES','CHICAGO'),
(40,'OPERATIONS','BOSTON');
INSERT INTO app_emp VALUES 
(7369,'SMITH','CLERK',7902,'1980-12-17','800.00',NULL,20),
(7499,'ALLEN','SALESMAN',7698,'1981-02-20','1600.00','300.00',30),
(7521,'WARD','SALESMAN',7698,'1981-02-22','1250.00','500.00',30),
(7566,'JONES','MANAGER',7839,'1981-04-02','2975.00',NULL,20),
(7654,'MARTIN','SALESMAN',7698,'1981-09-28','1250.00','1400.00',30),
(7698,'BLAKE','MANAGER',7839,'1981-05-01','2850.00',NULL,30),
(7782,'CLARK','MANAGER',7839,'1981-06-09','2450.00',NULL,10),
(7788,'SCOTT','ANALYST',7566,'1987-04-19','3000.00',NULL,20),
(7839,'KING','PRESIDENT',NULL,'1981-11-17','5000.00',NULL,10),
(7844,'TURNER','SALESMAN',7698,'1981-09-08','1500.00','0.00',30),
(7876,'ADAMS','CLERK',7788,'1987-05-23','1100.00',NULL,20),
(7900,'JAMES','CLERK',7698,'1981-12-03','950.00',NULL,30),
(7902,'FORD','ANALYST',7566,'1981-12-03','3000.00',NULL,20),
(7934,'MILLER','CLERK',7782,'1982-01-23','1300.00',NULL,10);
  • 使用Django完成如下查询操作
    查询dept表中的所有列信息
    查询emp表中的员工姓名、月收入及部门编号
    查询emp表中的部门编号及工种,并去掉重复行
    查询emp表中的员工姓名及全年的收入
    查询月收入大于2000的员工姓名及月收入
    查询月收入在1000元到2000元的员工姓名、月收入及雇佣时间。
    查询以S开头的员工姓名及月收入。
    查询员工姓名中的第三个字符是A的员工姓名及月收入。
    查询emp表中月收入是800的或是1250的员工姓名及部门编号
    查询在部门20中岗位CLERK的所有雇员信息
    查询工资高于2500或岗位为MANAGER的所有雇员信息
    查询有奖金 (COMM不为空,且不为0) 的员工姓名,按工资排序
    查询不带有'R'的雇员姓名




- end -

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342