Полиморфизм в моделях Джанго - PullRequest
10 голосов
/ 19 марта 2011

Я занимаюсь разработкой приложения django, и у меня есть такая структура модели

class Animal(models.Model):
    aul = models.ForeignKey(Aul)
    age = models.IntegerField()

    def __unicode__(self):
        return u'Animal'

class Sheep(Animal):
    wool = models.IntegerField()

    def __unicode__(self):
        return u'Sheep'

И я передаю animal_set в шаблон и выводю каждый объект, подобный этому {{ animal }}.Он выводит Животное , но я создал объекты типа овец и хочу использовать __unicode__ метод овец не животного.

Работает ли полиморфизм в моделях Django?Я нашел несколько ответов, но есть фрагменты кода для написания внутри моделей, но я заинтересован в нативных решениях.

Ответы [ 5 ]

8 голосов
/ 19 марта 2011

На момент написания, последняя версия Django была 1.2

Но для работы нужны дополнительные элементы.

Вам необходимо назначить пользовательские модели. Менеджеробъект для каждой модели животных, который будет вызывать свой собственный объект QuerySet.

По сути, вместо возврата Animal экземпляров (это то, что вы получаете), SubclassingQuerySet вызывает as_leaf_class() метод, чтобы проверить, является ли модель элементаAnimal или нет - если это так, просто верните его, в противном случае выполните поиск в контексте модели.Вот и все.

#models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.db.models.query import QuerySet


class SubclassingQuerySet(QuerySet):
    def __getitem__(self, k):
        result = super(SubclassingQuerySet, self).__getitem__(k)
        if isinstance(result, models.Model):
            return result.as_leaf_class()
        return result

    def __iter__(self):
        for item in super(SubclassingQuerySet, self).__iter__():
            yield item.as_leaf_class()


class AnimalManager(models.Manager):
    def get_query_set(self):  # Use get_queryset for Django >= 1.6
        return SubclassingQuerySet(self.model)


class Animal(models.Model):
    name = models.CharField(max_length=100)
    content_type = models.ForeignKey(ContentType, editable=False, null=True)
    objects = AnimalManager()

    def __unicode__(self):
        return "Animal: %s" % (self.name)

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

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


class Sheep(Animal):
    wool = models.IntegerField()
    objects = AnimalManager()

    def __unicode__(self):
        return 'Sheep: %s' % (self.name)

Тестирование:

>>> from animals.models import *
>>> Animal.objects.all()
[<Sheep: Sheep: White sheep>, <Animal: Animal: Dog>]
>>> s, d = Animal.objects.all()
>>> str(s)
'Sheep: White sheep'
>>> str(d)
'Animal: Dog'
>>> 
3 голосов
/ 19 марта 2011

Возможно, вы добьетесь успеха, получив доступ к {{animal.sheep}} - наследование модели - это не то, что вы думаете, под прикрытием находится механизм тяжелого метакласса, который «преобразует» такое наследование в неявные отношения OneToOneField.

1 голос
/ 03 декабря 2013

Я бы порекомендовал использовать прокси-модели Django, например, если у вас есть базовая модель Animal, которая разделена на подкласс овец и лошадей, вы бы использовали:

class Animal(models.Model):
    pass

class Horse(Animal):
    class Meta(Animal.Meta):
        proxy = True

class Sheep(Animal):
    class Meta(Animal.Meta):
        proxy = True

Это не , что предназначены для прокси-моделей для, но я бы не рекомендовал использовать полиморфизм Django, если вам не нужны преимущества хранения данных, специфичных для модели, в отдельных таблицах.Если у вас есть атрибуты на сто лошадей, у которых все значения по умолчанию хранятся в базе данных, а затем только 2 объекта с лошадьми, но у вас есть миллион овец, у вас есть миллион строк, у каждого из которых есть значения, специфичные для ста лошадей, которые вам не нужны.о, но опять же, это действительно актуально, только если у вас недостаточно места на диске, что маловероятно.Когда полиморфизм работает хорошо, это хорошо, но когда нет, это боль.

1 голос
/ 19 марта 2011

Существует очень простое приложение django под названием django-polymorphic-models , которое поможет вам в этом. Он предоставит вам метод downcast() в самой модели, который будет возвращать ваш «дочерний» объект, а также специальный класс наборов запросов для решения этих проблем!

Также очень полезно знать, что использование select_related() в наборе запросов базовой модели также получит дочерние объекты, на которые ссылаются через OneToOneField, что иногда может быть хорошим приростом производительности!

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

Вы должны проверить этот ответ: https://stackoverflow.com/a/929982/684253

Предлагаемое решение аналогично использованию django-polymorphic-models, которое уже упоминалось @lazerscience.Но я бы сказал, что django-model-utils немного лучше задокументирован, чем django-polymorphic, и библиотека проще в использовании.Проверьте файл в разделе «Диспетчер наследования»: https://github.com/carljm/django-model-utils/#readme

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