Django模型需要两个相关字段中的一个:我应该使用oneToOneField吗?

时间:2012-01-03 22:59:02

标签: django database-design django-models django-templates

我正在Django编写一个科学网络应用程序,处理抗体Fab片段的氨基酸序列,每个片段由一条重链和一条轻链组成。这些链中的每一个都由一系列氨基酸残基组成。

  • Fab 1
    • 轻链
      • 残留物1
      • 残留2
      • ...
    • 重链
      • 残留物1
      • 残留2
      • ...
  • Fab 2
    • 等...

我的models.py基本上是这样的:

from django.db.models import *

class Fab(Model):
    name = CharField(max_length=30)
    ...
    def __unicode__(self):
        return self.name

class Chain(Model):
    fab = ForeignKey(Fab)
    TYPE_CHOICES = (
        ('L', 'light'),
        ('H', 'heavy'),
    )
    type = CharField(max_length=5)
    ...

class Residue(Model):
    ch = ForeignKey(Chain)
    ...

因此,在将Fab输入数据库的过程中,我创建了2个链,为每个链分配typefab个外键。然后,要在模板中使用这些,我使用以下视图,将每个链作为一个对象,并将其传递给独立于其Fab父对象的模板,这不是完全理想的。

def fab_detail(request, fab_id):

    f = get_object_or_404(Fab, pk=fab_id)
    h = get_object_or_404(Chain, fab=f, type='H')
    l = get_object_or_404(Chain, fab=f, type='L')

    return render_to_response('antibodies/fab_detail.html', {
        'fab': f,
        'light': l,
        'heavy': h,
    }, context_instance=RequestContext(request))

但是,我想:

  1. 有更好的方法来引用模板中的轻链或重链,例如用{% for r in fab.light_chain.residue_set.all %}循环链的残基。
  2. 确保每个Fab只有1个轻链和1个重链
  3. 我考虑过继承Chain,但不确定如何实现类似的结果。我想出了一些类似的东西:

    class Chain(Model):
        # same as before, but without the fab ForeignKey field
        ...
    
    class LightChain(Chain):
        pass
    
    class HeavyChain(Chain):
        pass
    
    class Fab(Model):
        name = CharField(max_length=30)
        light_chain = OneToOneField(LightChain)
        heavy_chain = OneToOneField(HeavyChain)
        ...
    
    class Residue(Model):
        ???
    

    我遇到的主要问题是如何让LightChainHeavyChain字段包含Residue数据。具体来说,我将如何替换Residue类中的ch = ForeignKey(Chain)

    非常感谢任何建议或参考。

3 个答案:

答案 0 :(得分:0)

首先,你可以有一个元类,使字段在类型和链类型的组合上是唯一的。

class Chain(Model):
    fab = ForeignKey(Fab)
    TYPE_CHOICES = (
        ('L', 'light'),
        ('H', 'heavy'),
    )
    type = CharField(max_length=5, choices=TYPE_CHOICES)

    class Meta:
        unique_together = (
            ('type', 'fab'),
        )

这样,你无法添加更多2,因为你只有两个选择。

class Residue(Model):
    ch = ForeignKey(Chain)

看起来已经很好用了。

答案 1 :(得分:0)

keni的解决方案是我即将撰写的解决方案。

但是,我不认为“choices = TYPE_CHOICES”约束在任何级别都是强制执行的,它只是告诉Django在表单和管理员中使用“选择”菜单。所以理论上你可以有type ='R','W'或任何东西。顺便说一句,我认为你(jared)意味着max_length = 1。

另一种解决方案是简单地使用多表继承,而不是抽象基类,它们是{的两种不同形式{ {3}}。在这种情况下,您可以只使用ch = ForeignKey(Chain)。但这可能是太多的开销:将创建三个表,一个用于Chain,一个用于Light,一个用于Heavy,后两个用于引用第一个,一个基本上不包含任何其他内容。如果您需要存储轻链或重链的特定信息,可能会很有趣。

第三种解决方案是:

class Fab(Model):
name = CharField(max_length=30)
light = OneToOneField(Chain, related_name="fab_as_light")
heavy = OneToOneField(Chain, related_name="fab_as_heavy")

通过这种方式,您可以非常轻松地完成fab.light和fab.heavy,并且可以实现唯一性。我很确定将两个OneToOneField用于同一型号是合法的。如果不是,您仍然可以使用外键并将其设置为“唯一”。 我认为第三个是你的解决方案。

为了完整起见,你需要:

class Residue(Model):
ch = ForeignKey(Chain)

Chain几乎是空的(只是id)。

答案 2 :(得分:0)

在尝试了几个不同的东西并且无法使用'my_chain.fab_as_light / heavy'语法之后,我目前的解决方案是在@Arthur的解决方案中使用变体,在那里我生成一些名为'type'和'的属性链模型中的fab',它是根据Fab对象的related_name值计算的。 (例如,在对Chain对象执行操作但不关心它是哪种类型的链接的函数中,这些将是有用的:my_chain.fab返回轻链或重链的Fab对象。)< / p>

class Chain(Model):

    # determine the type based on Fab related_name
    def _get_type(self):
        try:
            if self.fab_as_light:
                return 'L'
        except:
            try:
                if self.fab_as_heavy:
                    return 'H'
            except:
                return None
    type = property(_get_type)

    # consolidate fab_as_light and fab_as_heavy into one property
    def _get_fab(self):
        try:
            return self.fab_as_light
        except:
            try:
                return self.fab_as_heavy
            except:
                return None
    fab = property(_get_fab)

    def __unicode__(self):
        return "%s_%s" % (self.fab.name, self.type)

class Fab(Model):
    name = CharField(max_length=30)
    light = OneToOneField(Chain, related_name='fab_as_light')
    heavy = OneToOneField(Chain, related_name='fab_as_heavy')

这可能不是最好的路线(它不是很优雅!),但它对我有用,所以我现在就一直用它。

感谢大家的投入。