Django中的通用一对一关系

30 投票
2 回答
9340 浏览
提问于 2025-04-17 04:42

我需要建立一个一对一的关系,而且这个关系还得是通用的。也许你能给我一些更好的设计建议。目前我想到的模型如下:

class Event(models.Model):
    # skip event related fields...
    content_type      = models.ForeignKey(ContentType)
    object_id         = models.PositiveIntegerField()
    content_object    = generic.GenericForeignKey('content_type', 'object_id')

    class Meta:
        unique_together   = ('content_type', 'object_id')

class Action1(models.Model):
    # skip action1 related fields...
    events = generic.GenericRelation(Event, content_type_field='content_type', object_id_field='object_id')

    @property
    def event(self):
        return self.events.get() # <<<<<< Is this reasonable?

class Action2(models.Model):...

在Django的管理界面中,我想在事件列表里收集所有的操作,然后从那里跳转到操作的管理页面。有没有办法避免在操作模型中创建event这个属性?有没有更好的解决方案?如果能把events这个字段和event这个属性合并成一个定义就太好了。我现在正在做的项目使用的是Django 1.1。

2 个回答

11

GenericRelation 是一个用来表示通用的多对一关系的类,添加一个 first_event 属性可以用来表示通用的一对一关系。

class Event(models.Model):
    content_type      = models.ForeignKey(ContentType)
    object_id         = models.PositiveIntegerField()
    content_object    = generic.GenericForeignKey('content_type', 'object_id')

    class Meta:
        unique_together   = ('content_type', 'object_id') # Important

class Action1(models.Model):
    events = generic.GenericRelation(Event)

    @property
    def first_event(self):
        return self.events.first()

另外,我建议在 Action1.first_event 中使用 .first() 方法,而不是 .get()。因为如果事件不存在,.first() 会返回 None,而 .get() 如果找不到事件就会抛出一个异常,这种情况可能会让人不太想要。

21

我最近遇到了一个问题,具体可以查看这个链接。你所做的事情是没问题的,但你可以通过创建一个混合类来让这个关系反转得更通用一些,这样做会更方便:

class Event(models.Model):
    content_type      = models.ForeignKey(ContentType)
    object_id         = models.PositiveIntegerField()
    content_object    = generic.GenericForeignKey('content_type', 'object_id')

    class Meta:
        unique_together   = ('content_type', 'object_id')

class EventMixin(object):
     @property
     def get_event(self):
         ctype = ContentType.objects.get_for_model(self.__class__)
         try:
             event = Event.objects.get(content_type__pk = ctype.id, object_id=self.id)
         except:
            return None 
         return event

class Action1(EventMixin, models.Model):
    # Don't need to mess up the models fields (make sure the mixing it placed before models.Model)
    ...

还有

action = Action1.object.get(id=1)
event = action.get_event

你可能也想给反转的关系加上缓存,这样会更高效。

撰写回答