Подклассные модели django со встроенными наборами запросов

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

>>> 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 ответов

Решение

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

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

Вы хотите один из них:

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

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

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

@staticmethod
def __new__(cls, *args, **kwargs):
    print args, kwargs
    return super(License, cls).__new__(cls, *args, **kwargs)

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

Хорошо, это работает: 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's ModelBase metaclass создает новый объект класса, а не использует тот, который был передан в его __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, я не знаю.

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

Другие вопросы по тегам