2012-01-03 6 views
1

Я пишу научное веб-приложение в Django, посвященное аминокислотным последовательностям антител Fab-фрагментов, каждый из которых состоит из одной тяжелой цепи и одной легкой цепи. Каждая из этих цепей состоит из последовательности аминокислотных остатков.Для модели Django требуется одно из двух связанных полей: следует ли использовать oneToOneField?

  • 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 цепочки, назначая каждому 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)) 

Однако я хочу:

  1. есть лучший способ, чтобы обратиться к легкой или тяжелой цепи в шаблоне, например, петля над остатками цепи с {% for r in fab.light_chain.residue_set.all %}.
  2. гарантировать, что каждый Fab имеет только 1 легкую цепь и тяжелую цепь 1

Я рассмотрел подклассов 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 данные. В частности, с чем заменить ch = ForeignKey(Chain) в классе Residue?

Будут оценены любые предложения или ссылки.

ответ

0

Решение keni является тем, о котором я собирался писать.

Однако я не думаю, что ограничение «choice = TYPE_CHOICES» применяется на любом уровне, оно просто сообщает Django использовать меню «select» в формах и admin. Поэтому теоретически вы можете иметь тип = «R», «W» или что-то еще. Кстати, я думаю, что вы (jared) означали max_length = 1.

Другим решением было бы просто использовать нескольких таблиц наследования а, как вы, кажется, делают, а не абстрактный базовый класс, что две различные формы model inheritance. В этом случае вы можете просто иметь ch = ForeignKey(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) 

И цепь будет почти пустой (только идентификатором).

+0

Другая вещь, которую вы, возможно, захотите сделать, - это не «ch = ForeignKey (Chain)» в классе Residue, а скорее ManyToManyField для Chain. (Я вообще не отвечаю на ваш вопрос, а скорее разделяю идею). У меня может быть совершенно неправильное понимание того, что такое «Residue», но если это кусок молекулы, который можно найти в разных местах (например, в разных Fab), это может иметь смысл. (среди прочего, это позволит вам ответить на вопрос, «в котором Fabs делает это Residue?», если это имеет смысл) – Arthur

+0

Спасибо за это @Arthur. Попытавшись кратко, похоже, что ваше третье решение будет хорошим вариантом для того, что мне нужно сделать, когда я получу все, что работает.Тем не менее, у меня возникли проблемы с доступом к «заводской» обратной связи из объекта Chain. например 'f = Fab (...); l = цепочка(); f.light = l; print l.fab_as_light' вызывает исключение 'DoesNotExist: Fab соответствия запроса '. Есть ли что-то, что мне не хватает в этом рабочем процессе? – jared

+0

Кроме того, с тех пор, как вы его воспитывали, в биологии аминокислота [остаток] (http://en.wikipedia.org/wiki/Residue_%28chemistry%29) является всего лишь строительным блоком, который составляет часть белка. Если белок был представлен поездом, то каждый остаток был бы одним из вагонов поезда. То, что делает один белок отличным от другого, - это последовательность этих остатков. Вы можете быть прав, говоря, что я должен иметь ManyToManyField здесь, но я бы назвал его ResidueTypes. Здесь я использую Residue для обозначения «экземпляра ResidueType в данной позиции в белковой цепочке». – jared

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) 

выглядит хорошо, как уже использован выше.

0

После того, как вы попробовали пару разных вещей и не могли использовать синтаксис «my_chain.fab_as_light/heavy», мое текущее решение - использовать вариацию в решении @ Arthur, где я создаю пару свойств, называемых «type» и «fab» в модели Chain, которые вычисляются на основе значения related_name объекта Fab. (Это будет полезно, например, в функции, которая выполняет операции над объектом цепи, но не заботится, какой тип цепи это:. 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') 

Это, вероятно, не лучший маршрут (это не совсем грациозно!), Но он работает для меня, поэтому я поеду с ним пока.

Спасибо всем за ваш вклад.

+0

это правда, что первое свойство не очень элегантно. Возможно, вы можете сохранить тип в модели Chain (которую вы также использовали бы во втором свойстве) – Arthur

Смежные вопросы