Django学习笔记一十二——建立多对多结构表的三种方式

ManytoManyField创建表的第一种方式

我们在前面讲了多对多关联(ManyToManyField)的使用,但是具体的使用方法都是用的直接通过ORM创建第三个表,

 

第二种方法——手动创建管理表

除了前面用ORM自动创建有关联关系的第三张表意外,我们还可以自己写,然后用外键分别关联作者和书籍

class Books(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)

def __str__(self): return self.title class Author(models.Model): name =models.CharField(max_length=8) #建立的第三张表 class Author2Book(models.Model): book = models.ForeignKey(to='Books',on_delete=models.CASCADE) author = models.ForeignKey(to='Author',on_delete=models.CASCADE)

这里主要讲多对多关系的建立,把多余的字段都删除了。

前面忘记讲了一个小知识点:我们在创建了ForeignKey关系以后,在数据库中的实际字段名称是会自动添加一个_id的,看一下这个第三张表,我们定义的名称是book和author,看看数据库里是怎么样的

 注意一点:其实这个表应该把book和author作为联合唯一约束的 ,代码如下,逻辑关系需求但是对操作表的方法没有影响。

class Author2Book(models.Model):
    book = models.ForeignKey(to='Books',on_delete=models.CASCADE)
    author = models.ForeignKey(to='Author',on_delete=models.CASCADE)

    class Meta:
        unique_together = ('author','book')

 

但是对于查询操作,因为我们是自己手动创建的关联表,就不能通过ORM自己查询author表或books表了,想要查谁写了什么书或者什么书的作者是谁必须从第三张表下手

ret = models.Author2Book.objects.filter(author_id=2)
print(ret.values())
for i in ret:
    print(i.book.title,i.author.name)

#########输出##########
<QuerySet [{'id': 3, 'book_id': 2, 'author_id': 2}, {'id': 5, 'book_id': 4, 'author_id': 2}]>
学习Linux 李四

如果想查询指定作者名字的书籍更加麻烦:

author_obj = models.Author.objects.get(name='张三')
book_id = models.Author2Book.objects.filter(author=author_obj).values_list('book_id')
#book_id为QuerySet对象,要获得一个list
books_id_list = [i[0] for i in book_id]
books = models.Books.objects.filter(id__in=books_id_list) 
print(books)

 所以可以看出来,这种方法要比第一种方式更麻烦一些

 第三种方法

第三种方式和上面的第二种方式差不多,都是自己手动创建第三个关系表。但是又避免了第二种方式不能用ORM提供的多对多的关系查询的弊端 

 

class Books(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    def __str__(self):
        return self.title

class Author(models.Model):
    name =models.CharField(max_length=8)
    #通过through=..来指定使用手动创建的第三张表来建立多对多的关系
    book = models.ManyToManyField(to='Books',through='Author2Book',through_fields=('author','book')) 

#建立的第三张表
class Author2Book(models.Model):
    book = models.ForeignKey(to='Books',on_delete=models.CASCADE)
    author = models.ForeignKey(to='Author',on_delete=models.CASCADE)

    class Meta:
        unique_together = ('author','book')

定义类的时候一定要注意through_fields里参数的顺序:第一个字段是多对多设置在那个表,第三张表通过什么字段找到他,就把这个字段(是第三个表里的字段)放在第一个。没有关系的字段不用写在这里

这样建立的表,在查询数据的时候跟第一种方式是一样的。

#正向查询
books = models.Author.objects.get(name='张三').book.all()
print(books)
#反向查询
book = models.Books.objects.get(title='学习Python')
print(book)
authors =book.author_set.all()
for author in authors:
    print(author.name)

注意

用这种方式创建的表,是不能用前一张里说过的关联管理器中的add(),remove()等方法的。如果要进行增减,必须取到要操作的对象进行修改或删除(也就是直接对第三张表进行修改)。

总结:什么时候用那种方式

 可以看出来第一种方式基本满足了所有的需求了,但是什么时候还要用到其他的两种方式呢?

第三种方式其实已经包含了第二种方式,可以比较一下第一种方式的区别

由于第一种方式是通过ORM直接说生成的关系结构表,但是有些时候我们需要在第三张表里附加一些其他的字段,第一种方式就无法满足要求了。

比方我们要做一个婚恋网站,记录男性和女性的约会信息由于,男和女就是一个多对多的关系。约会的时间是不能放在男性表或女性表里的, 但是放在这个多对多的关系表里就可以。具体操作只说明一下思路,不放具体代码。

posted @ 2020-04-18 13:39  银色的音色  阅读(282)  评论(0编辑  收藏  举报