Django学习笔记一十二——建立多对多结构表的三种方式
我们在前面讲了多对多关联(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直接说生成的关系结构表,但是有些时候我们需要在第三张表里附加一些其他的字段,第一种方式就无法满足要求了。
比方我们要做一个婚恋网站,记录男性和女性的约会信息由于,男和女就是一个多对多的关系。约会的时间是不能放在男性表或女性表里的, 但是放在这个多对多的关系表里就可以。具体操作只说明一下思路,不放具体代码。