Django学习笔记(7)---多数据库中跨数据库使用多对多

原创
2013/02/14 01:03
阅读数 5.4K

刚才写了大半没保存,被老弟按了一下注销,瞬间化为乌有……-_-||

现在正在重新写,略蛋疼……


      昨天研究了半天,终于弄明白了如何在Django中使用多个应用,并且不同应用使用不同数据库,传送门 《Django学习笔记(6)---多应用、多数据库》

      但是存在的一点问题,就是当一个应用的模型多对多关联到另一个应用的模型(两个应用使用不同数据库)时,相互之间的查询就会出一些问题。

      以昨天文章为基础,作如下修改

#userApp的models.py中
from django.db import models
class User(models.Model):
    name = models.CharField(max_length=30)
#essayApp中的models.py中
from django.db import models
from userApp.models import User
class Essay(models.Model):
    #owner = models.ForeignKey(User,related_name="essay_owner")
    #我们假设一篇文章可以有多个作者
    owner = models.ManyToManyField(User,related_name="essay_owner")
    content = models.TextField()
     同时,在dbsettings.py中指明两个应用可以关联,虽然上一篇文章有提到,但还是再次说明一下,即:
    def allow_relation(self, obj1, obj2, **hints):
    #该方法用于判断传入的obj1和obj2是否允许关联,可用于多对多以及外键
    #同一个应用同一个数据库
    if obj1._meta.app_label == obj2._meta.app_label:
        return True
    #User和Essay是允许关联的
    elif obj1._meta.app_label in ('userApp','essayApp') and
    #接上一行  obj2._meta.app_label in ('userApp','essayApp'):
        return True
      我们再次假设,u1是一个User对象,e1是一个Essay对象,并且已经调用e1.owner.add(u1)添加关联。(原谅我比较懒再重新在shell里边写贴上来)

      在view中如果使用u1.essay_owner.all()逆向查询就会报错,因为essayApp_essay_owner这个表不存在于userApp所使用的数据库'userdb'中,此时需要指定所使用的数据库,即u1.essay_owner.using('essaydb').all()

      令人疑惑的是,如果使用e1.owner.all()进行查询,也会报错,原因是userApp_user这个表不存在于'essaydb'这个数据库中,但是当使用e1.owner.using('userdb').all()时,也会报错,原因是essayApp_essay_owner这个表不存在于'userdb'中,这就比较蛋疼了。Django官方文档中明确指出官方目前不支持跨数据库使用多对多关系和外键。

      因此,如果要实现跨数据库使用多对多,就要靠自己实现,我试了很多方法均失败,昨天想到一个方法是自己链接两个数据库并写相应的SQL语句来查询,但是这样就比较麻烦,而且到时候改变使用其他数据库也要重新写相应的SQL语句。

      经过今天测试,好在外键还是能正常使用,所以想了一个办法来实现跨数据库的多对多关系。

      这里,再次对essayApp的models.py进行更改

#essayApp中的models.py中
from django.db import models
from userApp.models import User
class Essay(models.Model):
    #owner = models.ForeignKey(User,related_name="essay_owner")
    #我们假设一篇文章可以有多个作者
    #owner = models.ManyToManyField(User,related_name="essay_owner")
    
    #通过使用through来指定一张表定义User与Essay之间的关系
    #PS:经过后来测试,发现如果有多个多对多指向User,这里related_name这个参数还是少不了的
    owner = models.ManyToManyField(User,related_name="essay_owner",through="ownerRelation")
    content = models.TextField()

class ownerRelation(models.Model):
    #需要指出,使用through参数指定关系,在ownerRelation中,必须包含至少两个外键
    #分别指向User与Essay
    user = ForeignKey(User,related_name="ownerRelation_owner")
    essay = ForeignKey(Essay,related_name="ownerRelation_essay")

此时创建一个关系对象
o1 = ownerRelation.objects.create(user=u1,essay=e1)
在view中通过
o1 = u1.ownerRelation_owner.using('essaydb').all()[0] #为方便说明直接使用第一个对象

逆向查询就可以获得这个ownerRelation对象,此时使用

o1.essay即可获得对应的essay,反过来用e1来查询也是如此

o1 = e1.ownerRelation_essay.all()[0] #这里不需要额外指定使用的数据库
o1.user即可获得对应的user
e1这篇文章所有的owner,即
O = e1.ownerRelation_essay.all()
for o in O:
    o.user
    #……

    需要指出的是,e1.owner.all()等一系列查询方法均不可用,因为多对多关联的模型不在同一数据库,如果是同一数据库,仍是可用的,另外,add和remove等方法被系统禁用,原因是使用了ownerRelation这样的表来指定多对多之间的关系,但是add方法并不能自动为你创建相应的ownerRelation对象。不过使用这种方法也不是没有好处的,使用through,能够使关系更加明了,并且你可以在关系模型中添加一些其他的属性,比如一个date = models.DateField(auto_now_add=True),可以记录关系建立的日期等

    笔记到此,虽然仍然麻烦了一点。

     希望有其他方法的朋友请不要吝惜,分享给我!

      同时,这个方法目前来说没发现有什么bug,但是由于官方本来就不提供跨数据库的多对多支持,因此可能存在潜在问题,不过也得遇到了才能说。

转载请注明出处,谢谢!

展开阅读全文
打赏
0
10 收藏
分享
加载中
Linktime博主

引用来自“Mr.6”的评论

继续,不错!

呵呵,近段应该还会继续更新
2013/02/14 18:05
回复
举报
继续,不错!
2013/02/14 16:46
回复
举报
更多评论
打赏
2 评论
10 收藏
0
分享
返回顶部
顶部