Django中的一对一、一对多、多对多

  • 一对一

创建两个模型IdCard和Person实现一对一的关系:

class IdCard(models.Model):
    card_number = models.IntegerField()

    class Meta:
        db_table = 'idcard'


class Person(models.Model):
    name = models.CharField(max_length=32)
    # 通过将属性设置为OneToOneFieldl类型实现一对一的关系
    person_idcard = models.OneToOneField(IdCard)

    class Meta:
        db_table = 'person

 idcard表结构:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDI3ODUxMg==,size_16,color_FFFFFF,t_70

person表结构:

 2019010918551734.png

 从表中可以看到Django根据person_idcard属性自动生成了外键字段person_idcard_id,使得两张表完成了一对一关系。

在视图中添加一个idcard直接添加就可以:

def add_idcard(request):
    cardnum = request.GET.get('cardnum')
    cards = IdCard.objects.filter(card_number=cardnum)
    if cards.exists():
        return HttpResponse('改卡已存在!')
    card = IdCard()
    card.card_number = cardnum
    card.save()
    return HttpResponse('添加卡成功!')

添加一个person时,因为在Person中定义了 person_idcard ,通过该字段完成了一对一的关系,所以在创建person时,需要指定该属性值,以完成一对一的关系

def add_person(request):
    name = request.GET.get('name')
    cardnum = request.GET.get('cardnum')

    card = IdCard.objects.get(card_number=cardnum)

    person = Person()
    person.name = name
    person.person_idcard = card

    person.save()
    return HttpResponse('添加人成功!')

通过请求添加一个Person,并指定对应IdCard

http://localhost:8001/fiveApp/addPerson/?name=成龙&cardnum=106

看看person表中数据:

 2019010919114426.png

idcard:

20190109191235153.png

可以看到关联成功了。

当我们在创建一个Person时,将它继续关联到卡号为106时,就会报一下错误:

django.db.utils.IntegrityError: (1062, "Duplicate entry '7' for key 'person_idcard_id'")

删除时:

删除卡号为106的卡:

 

def delete_idcard(request):
    cardnum = request.GET.get('cardnum')
    cards = IdCard.objects.filter(card_number=cardnum)
    if not cards.exists():
        return HttpResponse('该卡不存在!')
    card = IdCard.objects.get(card_number=cardnum)
    card.delete()
    return HttpResponse('以成功删除!')

删除后数据为:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDI3ODUxMg==,size_16,color_FFFFFF,t_70

20190109192355883.png

可以看到随着删除卡号106的卡,人也随之删除。

当删除人时:删除名为刘圆圆的人

def delete_person(request):
    name = request.GET.get('name')
    person = Person.objects.get(name=name)
    person.delete()
    return HttpResponse('删除成功')

person表数据:

20190109193822634.png

idcard表数据:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDI3ODUxMg==,size_16,color_FFFFFF,t_70

可以看到当删除名为刘圆圆的人时,人删除了,但是对应的卡则不会删除。

总结:一对一关系可以两个模型中任意一个指定属性为models.OneToOneField类型进行模型关系关联。实际上从表结构看出

一对一关系也是通过建立外键而关联的,实际上也有主从表之分,可以看做一种特殊的一对多关系。维持关系的一方(创建外检的一方)是从表,没有维持关系的一方是主表。当删除从表数据时并不会对从表的数据造成英雄,当删除主表数据时,会删除与之关联的从表数据。这种删除称之为级联删除。但当我们在从表中添加数据时,不能将外键指向已经关联数据的主表数据,否则会报错。

  • 一对多

创建teacher和student表,模型为:

class Teacher(models.Model):
    name = models.CharField(max_length=32)


class Student(models.Model):
    name = models.CharField(max_length=32)
    # 通过指定外键创建一对多关系
    stu_tea = models.ForeignKey(Teacher

表结构:

teacher表20190109195816428.png

student表 20190109195848954.png

 可以看到Django为我们成功创建了一对多的关系。

添加student需要指定关系(和一对一是进行关联一样):

def add_student(request):
    tname = request.GET.get('tname')
    sname = request.GET.get('sname')

    teachers = Teacher.objects.filter(name=tname)
    if not teachers.exists:
        return HttpResponse('该老师不存在!')
    teacher = Teacher.objects.get(name=tname)
    student = Student()
    student.name = sname
    student.stu_tea = teacher
    student.save()
    return HttpResponse('添加学生成功!')

添加一个学生:

 http://localhost:8000/fiveApp/addStudent/?tname=雷锋&sname=你

表中数据:

teacher表(数据时原有的): 

20190109202451683.png

student表:

20190109202522256.png

再添加多个学生:

20190109202645716.png

可以看到student表中的多条数据同时指向了主键为1的老师,这就形成了一对多的关系。

删除数据时:

删除student数据:删除学生 他

def delete_student(request):
    name = request.GET.get('name')
    students = Student.objects.filter(name=name)
    if not students.exists():
        return HttpResponse('该学生不存在!')
    student = Student.objects.get(name=name)
    student.delete()
    return '删除学生成功!'

表数据:

student:

20190109203609835.png

teacher:

20190109203634987.png

可以看到对主表teacher并没有产生影响,只是单单删除了从表中的数据。

删除teacher雷锋:

def delete_teacher(request):
    name = request.GET.get('name')
    teachers = Teacher.objects.filter(name=name)
    if not teachers.exists:
        return HttpResponse('该老师不存在!')
    teacher = Teacher.objects.get(name=name)
    teacher.delete()
    return HttpResponse('成功删除老师!')

看看表中数据:

teacher:

20190109204244253.png

student:

20190109204302728.png

可以看到都是空空如也。

总结:

Django中的一对多关系通过定义属性为ForeignKey类型而进行关系关联(比如:stu_tea = models.ForeignKey(Teacher)),

定义了这个属性的模型完成从表的映射,另一模型完成主表的映射。可以将从表中的多条数据同时关联到主表中的一条数据,形成一对多的关系。当删除主表数据时,会删除与数据关联的从表中的数据,进行级联删除。

  • 多对多:

比如一个男生可以有多个女性朋友,一个女生可以有多个男性朋友,形成了多对多的关系(*_*)。如何创建模型?如下:

class BoyFriend(models.Model):
    name = models.CharField(max_length=32)


class GirlFriend(models.Model):
    name = models.CharField(max_length=32)

    girl_boy = models.ManyToManyField(BoyFriend)
在GirlFriend中创建了关联属性girl_boy,使Django完成多对多的关系映射。看看所创建的表

20190109205954236.png

可以看到,Django为我们创建了中间表,完成了多对多的关系映射。这个关系映射也是需要我们自己维护的。当各自添加男生女生时,可以自行添加不进行关系关联。如果男生和女生是朋友则需要进行关联。如下:

先添加一些数据:

20190109213124413.png

20190109213135419.png

20190109213157725.png

此时没有进行关联,中间表是空的。接下来关联一条数据。

如果男生一和女生一是朋友就进行关联:

视图函数:

def add_boy_girl(request):
    bname = request.GET.get('bname')
    gname = request.GET.get('gname')

    boys = BoyFriend.objects.filter(name=bname)
    girls = GirlFriend.objects.filter(name=gname)
    if not boys.exists():
        return HttpResponse('该男生不存在!')
    if not girls.exists():
        return HttpResponse('该女生不存在!')

    boy = BoyFriend.objects.get(name=bname)
    gril = GirlFriend.objects.get(name=gname)
    # 通过关联属性的add()方法进行关联
    gril.girl_boy.add(boy)

    return HttpResponse('成功关联!')

添加关联:

localhost:8000/fiveApp/addBoyGirl/?bname=男生一&gname=女生一

看看表数据:

20190109213807841.png

可以看到在中间表中通过主键将两个对象关联到了一起。现在使他们都成为朋友:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDI3ODUxMg==,size_16,color_FFFFFF,t_70

可以看到中间表中形成了多对多的关系。一列维护着一方的主键,使之关联到了一起。仔细看,实际上是多对一对多的关系,或者称为两个一对多的关系。

当删除时:

删除一个男生:

 

def del_boyfriend(request):
    name = request.GET.get('name')
    boys = BoyFriend.objects.filter(name=name)
    if not boys.exists():
        return HttpResponse('该男生不存在!')
    boy = BoyFriend.objects.get(name=name)
    boy.delete()
    return HttpResponse('删除成功!')

删除男生一看看数据: 

boyfriend表:

20190109215038238.png

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDI3ODUxMg==,size_16,color_FFFFFF,t_70

可以看到中间表中关于男生一的数据都被删除了。

删除一个女生:

def del_girlfriend(request):
    name = request.GET.get('name')
    girls = GirlFriend.objects.filter(name=name)
    if not girls.exists():
        return HttpResponse('该男生不存在!')
    girl = GirlFriend.objects.get(name=name)
    girl.delete()
    return HttpResponse('删除成功!')

删除女生一:

 http://localhost:8000/fiveApp/delGirlFriend/?name=女生一

删除之后看看表中数据:

女生表:

20190109215502609.png

中间表:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDI3ODUxMg==,size_16,color_FFFFFF,t_70

可以看到,当删除女生时,与之对应的中间表的数据都没有了。

总结:多对多关系在定义模型时,可以在任何一方建立一个维持关系的属性(girl_boy = models.ManyToManyField(BoyFriend)),Django会自动根据模型完成多对多关系的映射,即创建出中间表。这个关系需要我们自己维护,当需要添加关系时,可以通过关系属性的add方法进行关系关联(gril.girl_boy.add(boy))。多对多没有主从的关系,是由中间表维护的多对多的关系,实际上可以看做是多对一对多,或者两个一对多。当删除数据时,任何一方删除数据,与之对应的中间表数据都会进行删除(相当于一对多的级联删除)。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Django 一对多查询优化主要包括两个方面:预先加载和选择关联查询。 1. 预先加载(Prefetch) 预先加载是 Django 对象关系映射(ORM)的一个重要特性,用于减少 SQL 查询次数。它可以在查询主模型时,同时查询其关联模型的数据,而不是在后续使用时频繁查询数据库。 例如,一个 `Book` 模型和一个 `Author` 模型之间是一对多的关系,如果要查询所有书籍以及每本书的作者,可以使用 `prefetch_related()` 方法预先加载所有作者的数据: ```python books = Book.objects.all().prefetch_related('authors') ``` 这将通过两个 SQL 查询来实现,第一个查询获取所有书籍的数据,第二个查询获取所有作者的数据,并将其缓存到内存以供后续使用。这样,当您需要访问每个书籍的作者时,Django 将从缓存获取数据,而不是执行更多的 SQL 查询。 2. 选择关联查询(Select_related) 选择关联查询是另一个优化一对多查询的方法。它适用于查询与主模型相关联的一个外键模型,例如,如果要查询每本书的出版社名称,可以使用 `select_related()` 方法: ```python books = Book.objects.all().select_related('publisher') ``` 这将通过一个 SQL 查询来实现,查询所有书籍和它们的出版社,并将它们缓存起来。当您需要访问每个书籍的出版社时,Django 将从缓存获取数据,而不是执行更多的 SQL 查询。 需要注意的是,`select_related()` 适用于外键关系,而 `prefetch_related()` 适用于多对多关系和反向关系。在使用这些方法时,需要考虑到查询的性能和内存消耗。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值