Python 框架学习 Django篇 (六) 数据表关联、ORM关联

在后端服务器开发中,特别是前后端分离的架构中数据库是非常重要的,后端主要就是负责管理数据,而我们经常使用的mysql、oracle 都是关系型数据库,什么是关系型数据库?就是建立在关系模型基础上的数据库,而最难处理的就是各个表之间的关联关系,一般这种关系分为三种: 一对一 、一对多、多对多

 一、数据表关联

1、一对多

表之间以对多的关系就是数据库中的 "外键"  ,下面我们举个例子,比如一个医药系统中肯定会有客户的信息吧,我们先定义一个客户的基本信息(客户名称、联系电话、居住地址)

  vi Django_demo/paas/models.py

class Customer(models.Model):# 客户名称name = models.CharField(max_length=200)# 联系电话phonenumber = models.CharField(max_length=200)# 地址address = models.CharField(max_length=200)

我们是一个医药系统,肯定存在很多不同的药品类型,同样也需要定义一个药品类型的表

Medicine药品表 ,包含一些(药品名称、编号、描述信息)

  vi Django_demo/paas/models.py

class Medicine(models.Model):# 药品名name = models.CharField(max_length=200)# 药品编号sn = models.CharField(max_length=200)# 描述desc = models.CharField(max_length=200)

有了药品信息、客户信息,那么只要存在销售的话就一定会有订单信息

想一想,我们订单的信息是不是和上面的两张表多少有一些关联,比如订单中需要用到客户信息和药品信息

在实际观察中,我们发现订单表里面会同时需要拿到上面两张表中的数据,在下图中我们可以看到一个客户同时可能会有多个订单,这种情况就是一对多,或者说多对一

 

像是这种一对多的关系,在数据库中是以外键形式表示的,如果说一个表中的字段是外键,那么他的值一定来源与其他表的主键

另外,我们定义表的 Model类的时候,如果没有指定主键字段,migrate 的时候 Django 会为该Model对应的数据库表自动生成一个id字段,作为主键

#导入数据库
python manage.py makemigrations
python manage.py migrate #查看
desc paas_customer;

现在我们要生成订单表,按照实际情况我们订单表的字段里面也会有客户的信息表示谁下的订单,而用户的信息需要使用外键去关联客户的主键,而客户表也就是customer表的主键就是id字段,Django中定义外键 的方法就是 Model类的该属性字段 值为(ForeignKey)

 vi Django_demo/paas/models.py

import datetime
class Order(models.Model):# 订单名name = models.CharField(max_length=200,null=True,blank=True)# 创建日期create_date = models.DateTimeField(default=datetime.datetime.now)# 客户customer = models.ForeignKey(Customer,on_delete=models.PROTECT)

上面定义的customer是外键,让他去找Customer表的主键获取数据,而这里设置了一个on_delete的参数,这个意思是当主键被删除了那么外键这个数据还要不要了

1、CASCADE: 跟随主机一起把外键数据删除

2、PROTECT   禁止删除,如果非要删除就先清除外键数据后,才能删除对应主键

3、SET_NULL   删除后外键数据修改为null

注意

      外键字段,实际在数据库表中的字段名是DjangoForeignKey定义字段名加上后缀"_id"

比如上面,在执行了 migrate 命令更新数据库后,customer 这个外键字段实际上在 数据库表中的字段名 是 customer_id

python manage.py makemigrations
python manage.py migrate #查看
desc paas_info;

返回

mysql> desc paas_order;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | bigint       | NO   | PRI | NULL    | auto_increment |
| name        | varchar(200) | YES  |     | NULL    |                |
| create_date | datetime(6)  | NO   |     | NULL    |                |
| customer_id | bigint       | NO   | MUL | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

2、一对一

上面的外键案例,可以说是一对多或者多对一,而有时候是一对一的情况

比如,某个学校的学生表 和学生的地址表,就形成一对一的关系,即 一条主键所在表的记录 只能对应一条 外键所在表的记录,而Django 中 用 OneToOneField 对象 实现 一对一 的关系

 vi Django_demo/paas/models.py

class Student(models.Model):# 姓名name = models.CharField(max_length=200)# 班级classname = models.CharField(max_length=200)# 描述desc = models.CharField(max_length=200)class ContactAddress(models.Model):# 一对一 对应学生 student = models.OneToOneField(Student, on_delete=models.PROTECT)# 家庭homeaddress = models.CharField(max_length=200)# 电话号码phone = models.CharField(max_length=200)

 Django发现这样一对一定定义,它会在migrate的时候,在数据库中定义该字段为外键的同时, 加上 unique=True 约束,表示在此表中,所有记录的该字段 取值必须唯一,不能重复

 3、多对多

数据库中还存在一种多对多的关系,在order订单表中

一个订单可以采购多种药品,就对应 Medicine表里面的多种药品;

而一种药品也可以被多个订单采购, 那么Order表 和 Medicine表 之间就形成了多对多的关系

 

 Django是通过 ManyToManyField 对象 表示 多对多的关系的

 vi Django_demo/paas/models.py

import datetime
class Order(models.Model):# 订单名name = models.CharField(max_length=200,null=True,blank=True)# 创建日期create_date = models.DateTimeField(default=datetime.datetime.now)# 客户customer = models.ForeignKey(Customer,on_delete=models.PROTECT)# 订单购买的药品,和Medicine表是多对多 的关系medicines = models.ManyToManyField(Medicine, through='OrderMedicine')class OrderMedicine(models.Model):#添加外键order = models.ForeignKey(Order, on_delete=models.PROTECT)medicine = models.ForeignKey(Medicine, on_delete=models.PROTECT)# 订单中药品的数量  一种特殊的类型,表示非负整数amount = models.PositiveIntegerField()  

我们上面通过medicines = models.ManyToManyField(Medicine, through='OrderMedicine')

去指定Order表和Medicine表的对应关系,其实不会在Order表上面创建medicines的字段

python manage.py makemigrations
python manage.py migrate #查看
desc paas_OrderMedicine;

 

4、管理药品实现

我们在 mgr 目录下面新建 medicine.py,处理 客户端发过来的 列出药品、添加药品、修改药品、删除药品 的请求,需要运用前面的数据库增删改查的方法

vi Django_demo/mgr/medicine.py

from django.http import JsonResponse# 导入 Medicine 对象定义(这块可能显示模块导入不正常,忽略)
from  paas.models import  Medicineimport jsondef Orderdispatcher(request):# 根据session判断用户是否是登录的管理员用户if 'usertype' not in request.session:return JsonResponse({'ret': 302,'msg': '未登录','redirect': '/mgr/sign.html'},status=302)if request.session['usertype'] != 'mgr':return JsonResponse({'ret': 302,'msg': '用户非mgr类型','redirect': '/mgr/sign.html'},status=302)# 将请求参数统一放入request 的 params 属性中,方便后续处理# GET请求 参数 在 request 对象的 GET属性中if request.method == 'GET':request.params = request.GET# POST/PUT/DELETE 请求 参数 从 request 对象的 body 属性中获取elif request.method in ['POST','PUT','DELETE']:# 根据接口,POST/PUT/DELETE 请求的消息体都是 json格式request.params = json.loads(request.body)# 根据不同的action分派给不同的函数进行处理action = request.params['action']if action == 'list_medicine':return listmedicine(request)elif action == 'add_medicine':return addmedicine(request)elif action == 'modify_medicine':return modifymedicine(request)elif action == 'del_medicine':return deletemedicine(request)else:return JsonResponse({'ret': 1, 'msg': '不支持该类型http请求'})def listmedicine(request):# 返回一个 QuerySet 对象 ,包含所有的表记录qs = Medicine.objects.values()# 将 QuerySet 对象 转化为 list 类型# 否则不能 被 转化为 JSON 字符串retlist = list(qs)return JsonResponse({'ret': 0, 'retlist': retlist})def addmedicine(request):info    = request.params['data']# 从请求消息中 获取要添加客户的信息# 并且插入到数据库中medicine = Medicine.objects.create(name=info['name'] ,sn=info['sn'] ,desc=info['desc'])return JsonResponse({'ret': 0, 'id':medicine.id})def modifymedicine(request):# 从请求消息中 获取修改客户的信息# 找到该客户,并且进行修改操作medicineid = request.params['id']newdata    = request.params['newdata']try:# 根据 id 从数据库中找到相应的客户记录medicine = Medicine.objects.get(id=medicineid)except Medicine.DoesNotExist:return  {'ret': 1,'msg': f'id 为`{medicineid}`的药品不存在'}if 'name' in  newdata:medicine.name = newdata['name']if 'sn' in  newdata:medicine.sn = newdata['sn']if 'desc' in  newdata:medicine.desc = newdata['desc']# 注意,一定要执行save才能将修改信息保存到数据库medicine.save()return JsonResponse({'ret': 0})def deletemedicine(request):medicineid = request.params['id']try:# 根据 id 从数据库中找到相应的药品记录medicine = Medicine.objects.get(id=medicineid)except Medicine.DoesNotExist:return  {'ret': 1,'msg': f'id 为`{medicineid}`的客户不存在'}# delete 方法就将该记录从数据库中删除了medicine.delete()return JsonResponse({'ret': 0})

添加路由

vi Django_demo/mgr/urls.py

from django.urls import pathfrom .k8s import dispatcher
from .sign_in_out import signin,signoutfrom .medicine import orderdispatcher  #添加
urlpatterns = [path('customers/', dispatcher),path('medicines/', orderdispatcher),  #添加 必须带斜杠path('signin', signin),path('signout', signout),]

5、添加药品

vi main.py

import  requests,pprint#添加认证
payload = {'username': 'root','password': '12345678'
}
#发送登录请求
response = requests.post('http://127.0.0.1:8000/api/mgr/signin',data=payload)
#拿到请求中的认证信息进行访问
set_cookie = response.headers.get('Set-Cookie')# 构建添加 客户信息的 消息体,是json格式
payload = {"action":"add_medicine","data":{"name":"板蓝根","sn":"133","desc":"感冒药"}
}
url='http://127.0.0.1:8000/api/mgr/medicines/'
if set_cookie:# 将Set-Cookie字段的值添加到请求头中headers = {'Cookie': set_cookie}# 发送请求给web服务response = requests.post(url,json=payload,headers=headers)pprint.pprint(response.json())

json类型说明

刚才上面我们使用了查询和添加数据,但是发现一个问题,两个请求传参的时候稍有不同

#data=payload  表示这个请求携带的参数是以表单的形式也就是字符串形式传输给后端的
requests.post(url,data=payload)#json=payload   表示参数是以json的形式传输给后端的
requests.post(url,json=payload)

在使用时要特别注意,我卡了半天才看到。。

6、查询药品

vi main.py

import  requests,pprintpayload = {'username': 'root','password': '12345678'
}
#发送登录请求
response = requests.post('http://127.0.0.1:8000/api/mgr/signin',data=payload)
#拿到请求中的认证信息进行访问
set_cookie = response.headers.get('Set-Cookie')
if set_cookie:# 将Set-Cookie字段的值添加到请求头中headers = {'Cookie': set_cookie}# 发送带有Cookie的新请求 修改url到新的路由response = requests.get('http://127.0.0.1:8000/api/mgr/medicines/?action=list_medicine',headers=headers)pprint.pprint(response.json())

返回

{'ret': 0,'retlist': [{'desc': '192.168.1.2', 'id': 1, 'name': 'abc', 'sn': '133'},{'desc': '感冒药', 'id': 2, 'name': '板蓝根', 'sn': '133'}]}

第一行是我写错了添加上的,一会当作删除的案例

遇到的问题

在访问url的时候,要确定url访问时是否需要带上/  如果定义的urls上有/,那边必须要带上斜杠不然会报错

7、修改药品

vi main.py

import  requests,pprint#添加认证
payload = {'username': 'root','password': '12345678'
}
#发送登录请求
response = requests.post('http://127.0.0.1:8000/api/mgr/signin',data=payload)
#拿到请求中的认证信息进行访问
set_cookie = response.headers.get('Set-Cookie')# 构建添加 客户信息的 消息体,是json格式
payload = {"action":"modify_medicine","id": "1","newdata":{"name":"诺氟沙星","sn":"141","desc":"无"}
}
url='http://127.0.0.1:8000/api/mgr/medicines/'if set_cookie:# 将Set-Cookie字段的值添加到请求头中headers = {'Cookie': set_cookie}# 发送请求给web服务response = requests.post(url,json=payload,headers=headers)pprint.pprint(response.json())

再次查询

{'ret': 0,'retlist': [{'desc': '无', 'id': 1, 'name': '诺氟沙星', 'sn': '141'},{'desc': '感冒药', 'id': 2, 'name': '板蓝根', 'sn': '133'}]}

8、删除药品

import  requests,pprint#添加认证
payload = {'username': 'root','password': '12345678'
}
#发送登录请求
response = requests.post('http://127.0.0.1:8000/api/mgr/signin',data=payload)
#拿到请求中的认证信息进行访问
set_cookie = response.headers.get('Set-Cookie')# 构建添加 客户信息的 消息体,是json格式
payload = {"action":"del_medicine","id":"1",
}
url='http://127.0.0.1:8000/api/mgr/medicines/'if set_cookie:# 将Set-Cookie字段的值添加到请求头中headers = {'Cookie': set_cookie}# 发送请求给web服务response = requests.post(url,json=payload,headers=headers)pprint.pprint(response.json())

 返回

{'ret': 0, 'retlist': [{'desc': '感冒药', 'id': 2, 'name': '板蓝根', 'sn': '133'}]}

可以看到除了查询以外,增加、修改、删除的操作基本是一致的,只需要修改携带参数中的动作以及传入的参数值即可

二、数据库关联操作(sql)

1、一对多

#先查询用户的id,然后基于id查询外键对应的订单
select * from paas_order where customer_id = (select id from paas_customer where name = "zhangsan");

一对多,我们查询指定的一个客户id,然后基于id去订单表中获取所以用户相关的订单

2、多对多

我们上面使用的时候添加过这么一个表  OrderMedicine 

class OrderMedicine(models.Model):#添加外键order = models.ForeignKey(Order, on_delete=models.PROTECT)medicine = models.ForeignKey(Medicine, on_delete=models.PROTECT)# 订单中药品的数量  一种特殊的类型,表示非负整数amount = models.PositiveIntegerField()  

多对多,表示什么意思呢,我们建立的上面的表是将order订单表 和Medicine药品表进行关联的一个中间表,我们在给前面两张表添加数据的时候,还需要单独去给中间表添加一次数据,用来声明订单和药品的关系,通过这张中间表,我们就能查询药品是那些客户进行购买,也可以查询那些客户购买了那些药品,我们做一个案例

 Django_demo/mgr/admin.py

from  paas.models import Customer,Medicine,Order,OrderMedicine
admin.site.register([Customer,Medicine,Order,OrderMedicine])

 访问admin页面,前面我们添加了很多的数据除了客户表信息以外都先删除一下

 

我们先给Medicines药品表  和orders 订单表 添加一条信息

 

上面给药品添加了感冒颗粒,订单表中给张三用户添加了一个test的订单 ,现在是没有任何关联机制的,这两张表也没有交互,我们在order medicines表中添加下两个订单的关联

 我们关联了第一条数据,将刚才的订单信息和药品信息以及数量一并放入到了中间表中

mysql> select * from paas_ordermedicine;
+----+--------+-------------+----------+
| id | amount | medicine_id | order_id |
+----+--------+-------------+----------+
|  1 |     10 |           6 |        5 |
+----+--------+-------------+----------+
1 row in set (0.00 sec)

 这里因为存放了药品的id和订单的id,我们就可以根据这个表来查询关联的数据信息,比如我想要看看订单表里面test订单对应下单了什么药品,买了多少

#找到订单中的id
select id from paas_order where name = "test";#基于id查询关系表中药品
select medicine_id,amount  from paas_ordermedicine where order_id = 5;#命令组合
mysql> select medicine_id,amount  from paas_ordermedicine where order_id = (select id from paas_order where name = "test");
+-------------+--------+
| medicine_id | amount |
+-------------+--------+
|           6 |     10 |
+-------------+--------+
1 row in set (0.00 sec)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/150799.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

p5.js画布操作实战:创建,绑定指定元素,动态调整大小,隐藏滚动条,删除画布

文章简介 之前在 《p5.js 光速入门》 里粗略讲过一下如何使用 p5.js 创建画布。 这次要介绍几个 p5.js 提供的画布相关的方法。 创建画布时的相关配置。让画布绑定指定元素。重置画布大小。删除画布。 学习本文前你需要具备一点 p5.js 的知识,想了解的请查看 《p…

Elasticsearch:使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation (一)

最近看了一个同事的几个视频。他总结的很好。在使用 LangChain 时,根据 LangChain 的官方文档 https://integrations.langchain.com/vectorstores,目前有三种方法可以进行使用:ElasticVectorSearch,ElasticsearchStore 及 Elastic…

洗衣行业在线预约小程序+前后端完整搭建教程

大家好哇,好久不见!今天源码师父来给大家推荐一款洗衣行业在线预约的小程序,带有前后端的完整搭建教程。 目前,人们对生活品质的追求不断提高,但生活节奏却也不断加快。对品质的追求遇到了忙碌的生活节奏,…

计算机网络【CN】子网划分与子网掩码

一个子网定义(X.X.X.X/n) 子网掩码为 n 个 1,32-n 个 0包含的 IP 地址数:232−n 主机号全 0 表示本网段主机号全 1 表示网段的广播地址可分配的 IP 地址数 :232−𝑛−2 子网划分原则 满足子网定义子网𝐴1…𝐴&#x…

Android Glide限定onlyRetrieveFromCache取内存缓存submit超时阻塞方式,Kotlin

Android Glide限定onlyRetrieveFromCache取内存缓存submit超时阻塞方式,Kotlin import android.os.Bundle import android.util.Log import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import com.b…

宏电5G RedCap工业智能网关获首个中国移动5G物联网开放实验室5G及轻量化产品能力认证

10月21日,2023世界物联网博览会——中国移动物联网开发者大会暨物联网产业论坛在无锡圆满举行。宏电股份参与中国移动5G物联网开放实验室5G及轻量化产品能力认证成果授牌仪式,并获得认证证书。 此次认证主要对产品功能、产品性能、RedCap网络兼容性进行测…

Echarts 实现 设备运行状态图(甘特图) 工业大数据展示

let option{tooltip: {formatter: function (params) {let startTime new Date(params.value[1])let endTime new Date(params.value[2]);//北京时间/时间戳转成日常时间function convert(date){var y date.getFullYear();var m date.getMonth() 1;m m < 10 ? "0…

Ocelot简易教程目录

Ocelot简易教程目录 这里写目录标题 Ocelot简易教程目录 Ocelot简易教程&#xff08;一&#xff09;之Ocelot是什么Ocelot简易教程&#xff08;二&#xff09;之快速开始1Ocelot简易教程&#xff08;二&#xff09;之快速开始2Ocelot简易教程&#xff08;三&#xff09;之主要特…

jvm摘要

第 2 章 Java 内存区域与内存溢出异常 2.2 运行时数据区域 程序计数器-线程私有:是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的字节码的行号指示器。 程序计数器是唯一一个没有规定任何OutOfMemoryError 情况的区域。 Java 虚拟机栈-线程私有:用于执行Java …

2016年亚太杯APMCM数学建模大赛A题基于光学信息数据的温度及关键元素含量预测求解全过程文档及程序

2016年亚太杯APMCM数学建模大赛 A题 基于光学信息数据的温度及关键元素含量预测 原题再现 光含有能量&#xff0c;在一定条件下可以转化为热。燃烧是一种常见的现象&#xff0c;既能发光又能发热。光和热通常是同时存在的&#xff0c;一般来说&#xff0c;光强度越高&#xf…

36基于matlab的对分解层数和惩罚因子进行优化

基于matlab的对分解层数和惩罚因子进行优化。蚁狮优化算法优化VMD,算术优化算法优化VMD&#xff0c;遗传优化算法优化VMD&#xff0c;灰狼优化算法优化VMD&#xff0c;海洋捕食者优化算法优化VMD&#xff0c;粒子群优化VMD&#xff0c;麻雀优化算法优化VMD&#xff0c;鲸鱼优化…

如何在Microsoft Visual Studio 中使用Cpp代码调用python代码

Microsoft Visual Studio中Cpp调用Python代码 本文介绍如何在Microsoft Visual Studio中&#xff0c;开发cpp项目时&#xff0c;调用python代码。 文章目录 Microsoft Visual Studio中Cpp调用Python代码前言一、Cpp生成exe文件1.1 安装python环境1.2 配置Microsoft Visual Stu…