I’m writing a scientific web app in Django dealing with the amino acid sequences of antibodies Fab fragments, each of which is comprised of exactly one Heavy Chain and one Light Chain. Each of these chains consists of a sequence of amino acid Residues.
- Fab 1
- Light Chain
- Residue 1
- Residue 2
- …
- Heavy Chain
- Residue 1
- Residue 2
- …
- Light Chain
- Fab 2
- etc…
My models.py is essentially this:
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)
...
So in the process of entering an Fab into the database, I create 2 chains, assign each a type and an fab foreign key. Then, to use these in a template, I use the following view, getting each chain as an object and passing it to the template independent of its Fab parent object, which isn’t exactly ideal.
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))
However, I want to:
- have a better way to refer to the Light or Heavy Chain in a template, e.g. to loop over the residues of the chain with
{% for r in fab.light_chain.residue_set.all %}. - ensure that each Fab has only 1 light chain and 1 heavy chain
I’ve considered subclassing Chain but wasn’t sure exactly how to achieve a similar result. I came up with something along the lines of:
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):
???
The main problem I’m having is how to get the LightChain and HeavyChain fields to contain Residue data. Specifically, with what do I replace ch = ForeignKey(Chain) in the Residue class?
Any suggestions or references will be greatly appreciated.
keni’s solution is the one I was about to write.
However, I don’t think that the “choices=TYPE_CHOICES” constraint is enforced at any level, it just tells Django to use a “select” menu in forms and admin. So theoretically you could have type = ‘R’, ‘W’ or anything. Btw, I think you (jared) meant max_length=1.
Another solution would be to simply use a multi-table inheritance, as you seem to do, and not an abstract base class, which are two different forms of model inheritance. In which case you can simply have
ch = ForeignKey(Chain). But that may be too much overhead: three tables would be created, one for Chain, one for Light and one for Heavy, the latter two ones referencing the first, one and containing basically nothing else. It may be interesting if you need to store specific information for Light or Heavy chains.A third solution would be to do this:
This way you can do fab.light and fab.heavy very easily, and uniqueness is enforced. I’m pretty sure it’s legal to have two OneToOneField towards the same model. If it’s not you can still have a Foreign Key and set it “unique”.
I think the third one is your solution.
For completeness, you’d have:
And Chain would be almost empty (just the id).