Django 中 ManyToMany 的关联方法
2013-04-07 20:04
302 查看
什么是 ManyToMany?
举个简单的例子,一本书可以有一个或多个作者,而一个作者可以写多本书,那么对于书和作者来说,他们的关系就不是一一对应的,而是多对多(也就是 ManyToMany)。在 Django 的 model 中,有个 ManyToManyField 专门来处理这种关系。
我写了个小应用来的管理 blog 的文章,因此我设计了这样的 model:
class Tag(models.Model): name = models.CharField(max_length=30) class Entry(models.Model): title = models.CharField(max_length=100) pub_date = models.DateField(blank=True, null=True) content = models.TextField() tags = models.ManyToManyField(Tag)[/code]
Entry 和 Tag 分别代表了 blog 的两大组件——文章和分类。Tag 很简单,只用一个 name 字段来存放 tag 的名字。而 Entry 则用了 title, pub_date, content 这 3 个字段来存放文章的标题、发布时间和文章内容。那么
tags = models.ManyToManyField(Tag)是干什么的?
和之前的例子一样,一篇文章会有好几个 tag,而 tag 也下辖很多篇文章。从 Entry 的角度看,它有很多 tag(s),于是通过 ManyToManyField 与 Tag 关联起来。打开数据库,你可以看到 Django 专门生成了名为 entry_tags 的表来保存文章与 tag 的对应关系。
model 是搞定了,但实际中如何使用 model 往数据库里添加文章和 tag? 可以用 Model 的 save 方法或者 Manager 提供的 create 方法向数据库写入数据,因此可以这样添加 tag:
t = Tag() t.name = '测试' t.save() # 或者 Tag.objects.create(name='测试')
文章也是一样:
e = Entry() e.title = '测试' e.pub_date = '2010-03-11' e.content = 'test' e.save()
但这样只能分别添加 tag 和文章,而且文章与 tag 的对应关系没有添加进去。要想在添加文章的时候顺便把 tag 和对应关系也一并存放,我们需要重载 save 方法:
class Entry(models.Model): title = models.CharField(max_length=100) pub_date = models.DateField(blank=True, null=True) content = models.TextField() tags = models.ManyToManyField(Tag) taglist = [] def save(self, *args, **kwargs): super(Entry, self).save() for i in self.taglist: p, created = Tag.objects.get_or_create(name=i) self.tags.add(p)
在 Entry 的属性里多了个 taglist,它用来储存文章的 tag。之后使用 save 的时候,会先调用 Entry 父类的 save 方法将 title, pub_date, content 写入 entry 表,然后取出 taglist 中的每一个 tag,调用 Tag.objects.get_or_create 方法获得 Tag 对象,再用 ManyToMany
的 add 方法添加 Tag 对象,最后二次调用 save 方法把数据真正存入数据库。
Manager 的 get_or_create 方法接受给定参数作为查询条件,如果找到结果就返回找到的对象,如果没找到就先创建对象再返回它,这样一来我们就不用担心会出现重复添加 tag 的问题了。
能否在添加的文章的时候也使用 get_or_create 方法来防止重复添加呢,答案是当然可以。自定义一个 Manager 重载 get_or_create 即可。
class EntryManager(models.Manager): def get_or_create(self, **kwargs): defaults = kwargs.pop('defaults', {}) taglist = defaults.pop('taglist', {}) Entry.taglist = taglist kwargs.update(defaults) super(EntryManager, self).get_or_create(**kwargs) class Entry(models.Model): title = models.CharField(max_length=100) pub_date = models.DateField(blank=True, null=True) content = models.TextField() tags = models.ManyToManyField(Tag) taglist = [] objects = EntryManager() def save(self, *args, **kwargs): super(Entry, self).save() for i in self.taglist: p, created = Tag.objects.get_or_create(name=i) self.tags.add(p) self.taglist = []
因为 get_or_create 实际上还是会最终调用 model (Entry) 的 save 方法,所以才会用
Entry.taglist = taglist在真正执行 get_or_create 之前先把 tag 放进去。
最后就可以这样储存文章:
title = '测试' data = {'taglist': tags, 'pub_date': date, 'content': output, } Entry.objects.get_or_create(title=title, defaults=data)
以上代码均来自 Antidote(https://github.com/Vayn/Antidote),这是一个为方便用 jekyll 写 blog 的人管理文章的应用,欢迎 clone。
转自:http://www.zijin5.com/2011/04/django-manytomany/
相关文章推荐
- Django 多表关联 存储 使用方法 ManyToManyField save
- Django 多表关联 存储 使用方法 ManyToManyField save
- 分享:Django学习笔记(4)---ManyToMany 添加、删除关联、查询
- django——ManyToManyField的使用方法
- Django学习笔记(4)---ManyToMany 添加、删除关联、查询
- django manytomany field修改关联数据库问题
- django+python+操作数据库多表关联-增删改查-many-to-many-many-to-one
- one-to-many双向关联映射
- hibernate many to one 非主键关联
- JPA 、 @ManyToOne、@OneToMany 外键 关联 设置关联项为NULL的问题完美解决
- 多对一关联映射(单向关联)见项目:me_many_to_one
- Hibernate单向一对多关联(Unidirectional One-To-Many)要点
- many-to-many 多对多关联
- Django学习笔记(4)---ManyToMany 添加、删除关联、查询 - Linktime的个人空间 - 开源中国社区
- Python Django admin many-to-many 多选框改成复选框
- Hibernate 中annotations(注解开发)的@OneToMany 一对多双向关联映射后,出现的jar包冲突问题
- hibernate学习5之one-to-many双向关联
- Django many to many MultipleObjectsReturned
- Django后台ManyToManyField显示成Object
- [Hibernate]基于xml和annotation的many-to-many单向关联【学习笔记】