Django框架web开发实战:Model数据库详解(四)

Django框架web开发实战:Model数据库详解

一、关联关系

1、概述

关联关系指的是数据表之间的数据是相互依赖和影响关系,表之间有从属关系,比如,有一个用户表,用户又有订单表,则用户表与订单表之间就存在从属关系。

2、关联关系的种类
  • 一对一 一个人一本护照
  • 一对多 一个部门可以有多个员工
  • 多对多 一个学生有多门课,一门课有多个学生
3、Model中的关联关系
3.1 关系类型字段
  • ForeignKey(to=关系对方的类或类名或‘self’,on_delete=级联选项)
  • OneToOneField(to=关系对方的类或类名或‘self’,on_delete=级联选项)
  • ManyToManyField(to=关系对方的类或类名或‘self’)
4、关联关系Model搭建
4.1 一对多Model

1:*
class Category(models.Model):
    title = models.CharField(max_length=20)
    note = models.CharField(max_length=20)
    class Meta:
        db_table="t_category"

class Goods(models.Model):
    title = models.CharField(max_length=20,unique=True)
    price = models.FloatField()
    cate = models.ForeignKey(to=Category,on_delete=models.CASCADE) #关系属性
    class Meta:
        db_table = "t_goods"
4.2 一对一Model
1:1
class Passport(models.Model):
    note = models.CharField(max_length=20)
    person = models.OneToOneField(to="Person",on_delete=models.CASCADE,null=True) #关系属性
    class Meta:
        db_table="t_passport"

class Person(models.Model):
    name = models.CharField(max_length=20)
    age = models.IntegerField()
    class Meta:
        db_table="t_person"
4.3 多对多Model
*:*
class Student(models.Model):
    name = models.CharField(max_length=20)
    age = models.IntegerField()
    class Meta:
        db_table="t_student"
        
class Course(models.Model):
    title = models.CharField(max_length=30)
    expire = models.SmallIntegerField()
    stu = models.ManyToManyField(to=Student) #关系属性
    class Meta:
        db_table="t_course"
5、关联关系查询
5.1 一对一查询
pn = Person.objects.all()         # 所有的人
pt = Passport.objects.all()       # 所有的护照

pn[0].passport   # 通过对方类名小写,获得对方
pt[0].per        # 在护照类中,有Person属性per,所以通过per即可获取Person对象

pn[0].passport.country  # 继续获取属性值
pt[0].per.name          

# 1对1关系: 关联查询,会进行表连接
#注意:在查询条件中,如在filter中:
#	passport__country是passport的country字段
#	passport__id是passport的id字段
#	per__name是Person的name字段,
#护照的country中包含"中"的Person,会做表连接
Person.objects.filter(passport__country__contains="中")
Person.objects.filter(passport__country="中国") ##护照的country是"中国"的Person
Person.objects.filter(passport__id=1)#护照的id是1的Person

Passport.objects.filter(per__id=1)#id为1的person的Passport
Passport.objects.filter(per__id__gt=1)#id大于1的person的Passport
Passport.objects.filter(per__name__contains="z")#id大于1的person的Passport

# 保留双方数据
Person.objects.filter(passport__country='USA').values('id','name','age','passport__country')
5.2 一对多查询
# 1对多关系   Category(1):没有关系属性   Goods(*):其中有关系属性cate
#单独查一方
cs = Category.objects.all()
gs = Goods.objects.all()

cs[0].goods_set # 没有多方的关系属性,通过"多方类名_set"获得多方
                # 此时只是返回一个RelatedManger,不支持遍历和限制获取子集 而且并没有数据,需要如下
cs[0].goods_set.filter(price__gt=200) #第一个类别中价格大于200的所有商品 (如此才有值)
cs[0].goods_set.all() # 第一个类别中价格大于200的所有商品(如此才有值)

gs[0].cate #获得第一个商品的类别对象
gs[0].cate.title #获得对象后 自然可以继续获取属性

#关联查询
Goods.objects.filter(cate__gte=1) # 类别id大于1的商品
Goods.objects.filter(cate__title__contains="xx")
Goods.objects.filter(cate__pk__gte=1) # 类别id大于1的商品
Goods.objects.filter(cate__id__gte=1)
Goods.objects.filter(cate__title__contains="s")# 列名标题含有"s"的商品

Category.objects.filter(goods__gt=1)#id大于1的商品的类别
Category.objects.filter(goods__pk__gt=1)#id大于1的商品的类别
Category.objects.filter(goods__title__contains="a")#标题中含有"a"的商品的类别

#关联查询:保留双方数据
Goods.objects.filter(cate__title__contains="s").values("cate__title","pk","price")

#注意在进行联合查询时,可能会由重复数据出现,解决:
list(set(Category.objects.filter(goods__price__gt=200)))
5.3 多对多查询
# 多对多关系   Course:有对方关系属性students   Student:没有对方关系属性 
# 查询一方
stus = Student.objects.all()
cours = Course.objects.all()

stus[0].course_set #返回ManyRelatedManager,但并没有数据
stus[0].course_set.all() #获得对方数据
stus[0].course_set.filter(title__contains='s') #获得第一个学生的 标题中含有"s"的课程

cours[0].stu.all()
cours[0].stu.filter(age__gt=18)

#关联查询
Student.objects.filter(course__title__contains="h") #标题中含有"h"的课程中的学生
Course.objects.filter(stu__name="zzz") #姓名为zzz的学生参加的课程
Course.objects.filter(stu__age__gt=18) #年龄大于18的学生参加的课程
6、增加数据
  • 单独增加主表方(没有外键一方) 和单表增加无差异
c = Category(title="男装",...)
c.save()
  • 为已存在的主表方附加从表数据(常见的增加情形)
# 方式一: 先取某一个类别,再通过该类别创建一个商品
c = Category.objects.filter(pk=1)   # 获取类别一对象
c.goods_set.create(title = 'abc',price=139.99)   # 不需要再save

# 方式二:先取某个类别,单独创建商品并关联类别
c = Category.objects.filter(pk=1)
g = Goods.objects.create(title='abc',price=213,cate=c) #为商品的关系属性赋值

# 注意:如果是1:1,则只能使用方式二
  • 同时添加主从数据
g = Goods(title="zzz6",price=100)
c = Category(title="类别1",note="xx")
g.save() #此时,外键的值为null
c.save()
c.goods_set.add(g) #会同步数据库,补充外键值

或

c = Category(title="类别1",note="xx")
c.save()
g = Goods(title="zzz6",price=100,cate9=c)
g.save() #此时,good是有外键值的
#注意如果是1:1只能用第二种
7、删除数据
  • 单独删除从表方,和删除单表无差异
Goods.objects.get(pk=1).delete()
  • 删除主表方,此时要看从表方的级联设置,会影响到从表方

    级联选项 : 在关联关系中删除主表时,对于从表可以有级联操作

    • CASCADE 级联删除
    • SET_NULL 外键置空(如果允许空的话)
    • PROTECT 不允许直接删除主表
    • SET_DEFAULT 需要为外键列设置默认值,默认值也应该是合法的外键值,可以在表中预留一个id
    • SET 将外键设置为某个值,值应该是合法的外键值,可以在表中预留一个id
    • DO_NOTHING dango什么也不做,由数据库决定是否合法

*:* 不支持级联删除

 #如果要删除主,所有从的外键置null  (重要)
 per = models.OneToOneField(to=Person,on_delete=models.SET_NULL,null=True)
 #如果要删除主,所有从一起删除
 per = models.OneToOneField(to=Person,on_delete=models.CASCADE,null=True)
 #如果要删除主,django什么也不做,有数据库决定是否合法
 per = models.OneToOneField(to=Person,on_delete=models.DO_NOTHING,null=True)
 #如果要删除主,所有从的外键置为6  (重要)
 cate = models.ForeignKey(to="Category",on_delete=models.SET(6))
 #如果要删除主,所有从的外键置为默认值5  (重要)
 cate = models.ForeignKey(to="Category",on_delete=models.SET_DEFALUT,default="5")
 #如果要删除主,如果有从数据存在,不允许删除
 cate = models.ForeignKey(to="Category",on_delete=models.PROTECT)
8、修改数据

查询出数据,修改属性,然后save()即可

cs = Category.objects.all()
c = cs[0]
c.title="new_title"
c.save()

# 坑:
Category.objects.all()[0].title="new_title"  #查询一次,并修改
Category.objects.all()[0].save() #查询一次,并保存

cate1=Category.objects.all()[0]
cate1.title="new_title"

二、懒加载Lazy-load

QuerySet在查询数据时,是延迟执行,直到真正使用了数据,才发起查询

a = Person.objects.all() #未查询
b = Category.objects.all() #未查询
print(a) #查询了a
#如上代码只执行了一次查询

c = Category.objects.all() #未查询
print(c) #查询c
goods = c[0].goods_set.all() #未查询goods
print(goods) #查询了goods
  • 测试方式

    找到mysql的安装目录下的my.ini文件,添加配置:

# SERVER SECTION
[mysqld]
.....
log = "E:/mysql_log.log" #设置日志文件,记录sql语句执行
或
general-log=1
general_log_file=E:/mysql_log.log

#需要重启Mysql服务   net stop/start mysql   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BRYTLEVSON

打赏的都是天使,创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值