Подклассные модели django со встроенными наборами запросов - PullRequest
7 голосов
/ 30 марта 2010

Как и в этот вопрос , за исключением того, что я хочу иметь наборы запросов, которые возвращают смешанное тело объектов:

>>> Product.objects.all()
[<SimpleProduct: ...>, <OtherProduct: ...>, <BlueProduct: ...>, ...]

Я понял, что я не могу просто установить Product.Meta.abstract в true или иначе просто объединить наборы запросов различных объектов. Хорошо, но это все подклассы общего класса, поэтому, если я оставлю их суперкласс как неабстрактный, я буду счастлив, пока я могу заставить его менеджера возвращать объекты соответствующего класса. Код запроса в django делает свое дело и просто вызывает функцию Product (). Звучит достаточно легко, за исключением того, что он взрывается, когда я переопределяю Product.__new__, я полагаю, из-за __metaclass__ в модели ... Вот код не для Django, который ведет себя так же, как я этого хочу:

class Top(object):
    _counter = 0
    def __init__(self, arg):
        Top._counter += 1
        print "Top#__init__(%s) called %d times" % (arg, Top._counter)
class A(Top):
    def __new__(cls, *args, **kwargs):
        if cls is A and len(args) > 0:
            if args[0] is B.fav:
                return B(*args, **kwargs)
            elif args[0] is C.fav:
                return C(*args, **kwargs)
            else:
                print "PRETENDING TO BE ABSTRACT"
                return None # or raise?
        else:
            return super(A).__new__(cls, *args, **kwargs)
class B(A):
    fav = 1
class C(A):
    fav = 2
A(0) # => None
A(1) # => <B object>
A(2) # => <C object>

Но это не получится, если я унаследую от django.db.models.Model вместо object:

File "/home/martin/beehive/apps/hello_world/models.py", line 50, in <module>
    A(0)
TypeError: unbound method __new__() must be called with A instance as first argument (got ModelBase instance instead)

Что является заметно дерьмовым следом; Я также не могу войти в кадр моего __new__ кода в отладчике. Я по-разному пробовал super(A, cls), Top, super(A, A) и все вышеперечисленное в сочетании с передачей cls в качестве первого аргумента __new__, но все безрезультатно. Почему это пинает меня так сильно? Нужно ли выяснять метаклассы django, чтобы это исправить, или есть лучший способ достичь своих целей?

Ответы [ 5 ]

4 голосов
/ 30 марта 2010

По сути, вы пытаетесь вернуть разные дочерние классы, запрашивая общий базовый класс. То есть: вы хотите листовые классы. Проверьте этот фрагмент для решения: http://www.djangosnippets.org/snippets/1034/

Также обязательно ознакомьтесь с документацией по инфраструктуре Contenttypes Django: http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ Поначалу это может немного запутать, но Contenttypes решит дополнительные проблемы, с которыми вы, вероятно, столкнетесь при использовании неабстрактных базовых классов с ORM Джанго.

2 голосов
/ 08 февраля 2012

Вам нужен один из них:

http://code.google.com/p/django-polymorphic-models/
https://github.com/bconstantin/django_polymorphic

Есть недостатки, а именно дополнительные запросы.

1 голос
/ 04 апреля 2013

Другой подход, который я недавно нашел: http://jeffelmore.org/2010/11/11/automatic-downcasting-of-inherited-models-in-django/

1 голос
/ 08 февраля 2012

Просто вставьте @staticmethod перед __new__ методом.

@staticmethod
def __new__(cls, *args, **kwargs):
    print args, kwargs
    return super(License, cls).__new__(cls, *args, **kwargs)
1 голос
/ 30 марта 2010

Хорошо, это работает: https://gist.github.com/348872

Хитрость была в этом.

class A(Top):
    pass

def newA(cls, *args, **kwargs):
    # [all that code you wrote for A.__new__]

A.__new__ = staticmethod(newA)

Теперь есть кое-что о том, как Python связывает __new__, что я, возможно, не совсем понимаю, но суть этого в следующем: метакласс django ModelBase создает новый объект класса, а не использует тот, который был передан в к его __new__; называть это A_prime. Затем он прикрепляет все атрибуты, которые были у вас в определении класса для A, к A_prime, но __new__ не получает правильную привязку.

Затем, когда вы оцениваете A(1), A здесь на самом деле A_prime, Python вызывает <A.__new__>(A_prime, 1), который не совпадает, и он взрывается.

Таким образом, решение состоит в том, чтобы определить __new__ после определения A_prime.

Может быть, это ошибка в django.db.models.base.ModelBase.add_to_class, может быть, это ошибка в Python, я не знаю.

Теперь, когда я сказал «это работает» ранее, я имел в виду это работает изолированно с минимальным тестовым примером построения объекта в текущей версии SVN Django . Я не знаю, действительно ли он работает в качестве модели или полезен в QuerySet. Если вы на самом деле используете это в рабочем коде, я сделаю публичный доклад об этом для pdxpython и заставлю их издеваться над вами, пока вы не купите нам всю безглютеновую пиццу.

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