Django的Many-to-Many(多对多)模型
参考:《DjangoBook2.0》 数据模型高级进阶
经典的例子:一本书有多个作者,一个作者有多本书,典型的多对多关系。
设计模型如下:
- from django.db import models
- class Author(models.Model):
- first_name = models.CharField(max_length=30)
- last_name = models.CharField(max_length=40)
- email = models.EmailField()
- def __unicode__(self):
- return self.name
- class Meta:
- db_table = "author"
- class Book(models.Model):
- title = models.CharField(max_length=200)
- authors = models.ManyToManyField(Author)
- def __unicode__(self):
- return self.title
- class Meta:
- db_table = "book"
python manage.py sqlall books(App名) 看一下Django帮我们生成的数据库表结构:
- CREATE TABLE `author` (
- `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
- `first_name` varchar(30) NOT NULL,
- `last_name` varchar(40) NOT NULL,
- `email` varchar(75) NOT NULL
- )
- ;
- CREATE TABLE `book_authors` (
- `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
- `book_id` integer NOT NULL,
- `author_id` integer NOT NULL,
- UNIQUE (`book_id`, `author_id`)
- )
- ;
- ALTER TABLE `book_authors` ADD CONSTRAINT `author_id_refs_id_22051734` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`);
- CREATE TABLE `book` (
- `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
- `title` varchar(200) NOT NULL
- )
- ;
- ALTER TABLE `book_authors` ADD CONSTRAINT `book_id_refs_id_77516490` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`);
book表中并没有authors字段,其中的book_authors表就是用来存放书和作者多对多映射关系的。
访问多值:
一本书的所有作者:
b = Book.objects.get(id=50)
b.authors.all()
b.authors.filter(first_name='Adam')
反向也可以,一个作者的所有书:
a = Author.objects.get(id=1)
a.book_set.all()
给多对多字段添加值(添加多对多关系):
a = Author.objects.get(id=1)
b = Book.objects.get(id=50)
b.authors.add(a)
从多对多字段中删除值(删除多对多关系):
a = Author.objects.get(id=1)
b = Book.objects.get(id=50)
b.authors.remove(a) 或者 b.authors.filter(id=1).delete()
我想自定义关系表!!!
为什么?
可能的原因:
1)我想添加一些额外的字段;
2)与现有系统集成,关系表已经存在。
定义方法如下:
- # coding: utf-8
- from django.db import models
- class Author(models.Model):
- first_name = models.CharField(max_length=30)
- last_name = models.CharField(max_length=40)
- email = models.EmailField()
- def __unicode__(self):
- return self.name
- class Meta:
- db_table = "author"
- class Book(models.Model):
- title = models.CharField(max_length=200)
- authors = models.ManyToManyField(Author, through='BookAuthor') # 注意多了through参数
- def __unicode__(self):
- return self.title
- class Meta:
- db_table = "book"
- class BookAuthor(models.Model):
- book = models.ForeignKey(Book)
- author = models.ForeignKey(Author)
- created_at = models.DateTimeField(auto_now_add=True) # 我要添加额外的字段
- class Meta:
- db_table = "book_author_relationship" # 我需要用自定义的名称
再看一下此时的数据库表结构:
- BEGIN;
- CREATE TABLE `author` (
- `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
- `first_name` varchar(30) NOT NULL,
- `last_name` varchar(40) NOT NULL,
- `email` varchar(75) NOT NULL
- )
- ;
- CREATE TABLE `book` (
- `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
- `title` varchar(200) NOT NULL
- )
- ;
- CREATE TABLE `book_author_relationship` (
- `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
- `book_id` integer NOT NULL,
- `author_id` integer NOT NULL,
- `created_at` datetime NOT NULL
- )
- ;
- ALTER TABLE `book_author_relationship` ADD CONSTRAINT `author_id_refs_id_6b774382` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`);
- ALTER TABLE `book_author_relationship` ADD CONSTRAINT `book_id_refs_id_7cf7763a` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`);
- CREATE INDEX `book_author_relationship_752eb95b` ON `book_author_relationship` (`book_id`);
- CREATE INDEX `book_author_relationship_337b96ff` ON `book_author_relationship` (`author_id`);
- COMMIT;
爽!
但是这样做也是有不便的地方:多对多字段的add()和remove()方法就不能用了。这里有很好的解释:http://stackoverflow.com/questions/1755591/many-to-many-relationships-with-additional-data-on-the-relationship
https://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships
那此时我们如何添加和删除映射关系呢?直接使用BookAuthor模型。
例如:
a = Author.objects.get(id=1)
b = Book.objects.get(id=50)
BookAuthor(book=b, author=a).save()