[英]Django model needs one each of two related fields: should I use oneToOneField?
我正在Django中編寫一個科學的Web應用程序,處理抗體Fab片段的氨基酸序列,每個片段都恰好由一條重鏈和一條輕鏈組成。 這些鏈中的每條鏈均由一系列氨基酸殘基組成。
我的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條鏈,分別分配了一個type
和一個fab
外鍵。 然后,要在模板中使用它們,我使用以下視圖,將每條鏈作為一個對象,並將其傳遞到獨立於其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))
但是,我想:
{% for r in fab.light_chain.residue_set.all %}
環回鏈的殘基。 我已經考慮過將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):
???
我遇到的主要問題是如何獲取LightChain
和HeavyChain
字段以包含Residue
數據。 具體來說,在Residue類中,用什么替換ch = ForeignKey(Chain)
?
任何建議或參考將不勝感激。
例如,您可以具有一個meta類,以使字段在類型和鏈類型的組合上唯一。
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)
看起來和上面使用的一樣好。
keni的解決方案就是我要寫的解決方案。
但是,我認為“ choices = TYPE_CHOICES”約束不會在任何級別強制執行,它只是告訴Django在表單和管理中使用“選擇”菜單。 因此,理論上您可以輸入type ='R','W'或其他任何內容。 順便說一句,我認為您(受刺激)的意思是max_length = 1。
另一個解決方案是像您一樣簡單地使用多表繼承 ,而不是抽象基類 ,后者是模型繼承的兩種不同形式。 在這種情況下,您只需擁有ch = ForeignKey(Chain)
。 但這可能會產生太多開銷:將創建三個表,一個用於鏈表,一個用於輕表,一個用於重表,后兩個表引用第一個表,一個基本不包含其他內容。 如果您需要存儲輕鏈或重鏈的特定信息,可能會很有趣。
第三種解決方案是這樣做:
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)。
在嘗試了幾種不同的方法並且無法使用'my_chain.fab_as_light / heavy'語法后,我當前的解決方案是使用@Arthur解決方案的一種變體,在其中我生成了一些稱為'type'和'fab'的屬性。 Chain模型,基於Fab對象的related_name值計算得出。 (例如,在對Chain對象執行操作但不關心鏈是哪種類型的函數中,這些函數將很有用: my_chain.fab
返回輕鏈或重鏈的Fab對象。)
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')
這可能不是最好的路線(並不完全是優美的路線!),但是它對我有用,所以我現在就繼續使用。
謝謝大家的投入。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.