Django - проверка типа многотабличного наследования Querysets - PullRequest
0 голосов
/ 01 декабря 2011

Я пытаюсь сохранить в своей базе вид структуры содержимого. Упрощенный пример:

models.py

class Section (models.Model):
    title = models.CharField(max_length=80)
    order = models.IntegerField()

class SectionClickable(Section):
    link = models.CharField(max_length=80)

class SectionHeading(Section):
    background_color = models.CharField(max_length=6)

views.py

sections = Section.objects.filter(title="Hello!")
for section in sections:
        if(section.sectionheading):
            logger.debug("It's a heading")

Мне нужно выполнить некоторые операции обработки, если это экземпляр SectionHeading, но (как в руководстве по Django) доступ к section.sectionheading вызовет ошибку DoesNotExist, если объект не имеет типа SectionHeading.

Я искал альтернативы для такого рода проблем и пробежался по универсальным внешним ключам в пакете contenttypes. Тем не менее, похоже, что это вызовет еще больше головной боли на стороне администратора Django. Может ли кто-нибудь посоветовать лучшее решение, чем приведенное выше?

Редактировать: я избегал абстрактного наследования из-за поля order. Мне нужно объединить два QuerySets вместе и отсортировать их по порядку

Ответы [ 4 ]

2 голосов
/ 01 декабря 2011

хорошо, вы можете проверить тип:

if isinstance(section, SectionHeading)

, но типизирование утки обычно предпочтительнее

edit:

на самом деле, это, вероятно, не сработает.объект будет Section.но вы можете искать атрибут:

if hasattr(section, 'sectionheading')

или

try:
    do_something_with(section.sectionheading)
except AttributeError:
    pass  # i guess it wasn't one of those
1 голос
/ 01 декабря 2011

Решение, которое я использовал, включало дополнительное поле, указывающее на (довольно полезный) класс ContentType:

class Section(models.Model):
    name = models.CharField(max_length=50)
    content_type = models.ForeignKey(ContentType,editable=False,null=True)

    def __unicode__(self):
        try:
            return self.as_leaf_class().__unicode__()
        except:
            return self.name

    def save(self, *args, **kwargs):
        if(not self.content_type):
            self.content_type = ContentType.objects.get_for_model(self.__class__)
        super(Section, self).save(*args, **kwargs)

    def as_leaf_class(self):
        content_type = self.content_type
        model = content_type.model_class()
        if(model == Section):
            return self
        return model.objects.get(id=self.id)

Если вы проходите «базовый» объект, я думаю, что это решениедовольно приятно и удобно работать.

0 голосов
/ 08 декабря 2011

ОП здесь.

Хотя ответ second является правильным для вопроса, я хотел бы добавить, что я считаю, что наследование нескольких таблиц является неэффективным подходом для этого сценария. Доступ к атрибуту модели подкласса вызовет выполнение запроса, что потребует запроса для каждой возвращаемой строки. Уч. Насколько я могу судить, select_related пока не работает для многостолового наследования.

Я также исключил ContentTypes, потому что он не будет делать это достаточно элегантно и, похоже, потребует много запросов.

Я остановился на использовании абстрактного класса:

class Section (models.Model):
    title = models.CharField(max_length=80)
    order = models.IntegerField()

    class Meta:
        abstract=True
        ordering=['order']

Запрашивает обе таблицы:

section_clickables = SectionClickable.objects.filter(video=video)
section_headings= SectionHeading.objects.filter(video=video)

и объединил два набора запросов

#Join querysets /323162/kak-obedinit-2-ili-bolee-naborov-zaprosov-v-predstavlenii-django
s = sorted(chain(section_headings, section_clickables), key=attrgetter('order'))

Наконец, я сделал тег шаблона для проверки экземпляра:

from my.models import SectionHeading, SectionClickable

@register.filter()
def is_instance(obj, c):
    try:
        return isinstance(obj, eval(c))
    except:
        raise ObjectDoesNotExist('Class supplied to is_instance could not be found. Import it in the template tag file.')

чтобы в моем шаблоне ( HamlPy ) я мог сделать это:

- if s|is_instance:"SectionClickable"
    %span {{s.title}}
- if s|is_instance:"SectionHeading"
    %span{'style':'color: #{{s.color}};'}
      {{s.title}}

В результате я использовал только два запроса: один для получения SectionClickable объектов и один для SectionHeading объектов

0 голосов
/ 01 декабря 2011

Я использовал что-то похожее на то, что секунда предлагает в своем редактировании:

class SomeBaseModel(models.Model):
    reverse_name_cache = models.CharField(_('relation cache'), max_length=10, 
                                          null=True, editable=False)

    def get_reverse_instance(self):
        try:
            return getattr(self, self.reverse_name_cache)
        except AttributeError:
            for name in ['sectionclickable', 'sectionheading']:
                try:
                    i = getattr(self, name)
                    self.reverse_name_cache = name
                    return i
                except ObjectDoesNotExist:
                    pass

Теперь это не совсем красиво, но он возвращает экземпляр подкласса изцентральное место, поэтому мне не нужно заключать другие утверждения в try.Возможно, можно было бы избежать жесткого кодирования имен обратного менеджера подкласса, но такого подхода было достаточно для моих нужд.

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