Для модели Django необходимо по одному на каждое из двух связанных полей: я должен использовать oneToOneField? - PullRequest
1 голос
/ 04 января 2012

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

  • Fab 1
    • Light Chain
      • Остаток 1
      • Остаток 2
      • ...
    • Тяжелая цепь
      • Остаток 1
      • Остаток 2
      • ...
  • Fab 2
    • и т.д ...

My 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) в классе остатков?

Будем весьма благодарны за любые предложения или ссылки.

Ответы [ 3 ]

0 голосов
/ 04 января 2012

Решение Кени - это то, что я собирался написать.

Однако я не думаю, что ограничение «choices = TYPE_CHOICES» применяется на любом уровне, оно просто говорит Django использовать «select»Меню в формах и админ.Таким образом, теоретически вы можете иметь 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)

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

0 голосов
/ 05 января 2012

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

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

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

0 голосов
/ 04 января 2012

Например, у вас может быть мета-класс, чтобы сделать поля уникальными для комбинации типа и типа цепочки.

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)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...