Как в этом вопросе, кроме я хочу смочь иметь querysets, которые возвращают смешанное тело объектов:
>>> Product.objects.all()
[, , , ...]
Я выяснил, что не могу просто установить Product.Meta.abstract
к истинному или иначе просто ИЛИ вместе querysets отличающихся объектов. Прекрасный, но это все подклассы общего класса, поэтому если я оставляю их суперкласс как некраткий обзор, я должен быть счастливым, пока я могу получить его менеджера к эхо-сигналам надлежащего класса. Код запроса в django делает свою вещь и просто выполняет вызовы к продукту (). Звучит достаточно легким, кроме него аварийные завершения, когда я переопределяю Product.__new__
, Я предполагаю из-за __metaclass__
в Модели... Вот код non-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) # =>
A(2) # =>
Но это перестало работать, если я наследовался django.db.models.Model
вместо object
:
File "/home/martin/beehive/apps/hello_world/models.py", line 50, in
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, чтобы смочь зафиксировать это или есть ли лучший способ выполнить мои концы?
По сути, вы пытаетесь вернуть различные дочерние классы, запрашивая общий базовый класс. То есть: вам нужны классы листа. Посмотрите этот фрагмент для решения: http://www.djangosnippets.org/snippets/1034/
Также обязательно ознакомьтесь с документацией по фреймворку Contenttypes в Django: http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ Поначалу это может быть немного запутанно, но Contenttypes решает дополнительные проблемы, с которыми вы, вероятно, столкнетесь при использовании неабстрактных базовых классов с ORM 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 ModelBase
создает новый объект класса, а не использует тот, который передается в его __new__
; назовем это A_prime
. Затем все атрибуты, которые были в определении класса A
, передаются в A_prime
, но __new__
не перепривязывается правильно.
Затем, когда вы оцениваете A(1)
, A
на самом деле A_prime
здесь, python вызывает
, что не совпадает, и он взрывается.
Так что решение состоит в том, чтобы определять __new__
после того, как A_prime
уже определен.
Возможно, это ошибка в django.db.models.base.ModelBase.add_to_class
, возможно, это ошибка в Python, я не знаю.
Теперь, когда я сказал "это работает" ранее, я имел в виду это работает в изоляции с тестовым примером построения минимального объекта в текущей SVN версии Django. Я не знаю, работает ли это на самом деле как Модель или полезно в QuerySet. Если вы действительно используете это в производственном коде, я сделаю из этого публичный lightning talk для pdxpython, и пусть они издеваются над вами, пока вы не купите нам всем пиццу без глютена.