2010-03-30 18 views
7

Al igual que en this question, excepto que quiero ser capaz de tener QuerySets que devuelven un cuerpo mixto de objetos:modelos de Django subclases con QuerySets integrados

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

me di cuenta de que no puedo acaba de establecer Product.Meta.abstract a cierto o de lo contrario solo O O BIEN conjunto de conjuntos de objetos diferentes. Bien, pero todas son subclases de una clase común, así que si dejo su superclase como no abstracta, debería estar contento, siempre y cuando pueda hacer que su administrador devuelva los objetos de la clase adecuada. El código de consulta en django hace su trabajo, y solo hace llamadas a Product(). Suena bastante fácil, excepto que explota cuando puedo reemplazar Product.__new__, supongo que debido a la __metaclass__ en el Modelo ... Aquí es código no Django que se comporta más o menos como lo quiero:

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> 

Pero eso no funciona si heredar de django.db.models.Model en lugar de 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) 

Cuál es la traza sobre todo de mierda; Tampoco puedo entrar en el marco de mi código __new__ en el depurador. He intentado varias veces super(A, cls), Top, super(A, A), y todo lo anterior en combinación con pasar cls como primer argumento a __new__, todo en vano. ¿Por qué me está pateando tan duro? ¿Tengo que descubrir las metaclases de django para poder arreglar esto o hay una mejor manera de lograr mis objetivos?

+1

Es tentador tratar de averiguar el rompecabezas, pero el instinto me dice que estás haciendo mal. Esto es como una tortura para el pobre ORM de Django. – keturn

Respuesta

4

Básicamente, lo que intenta hacer es devolver las diferentes clases secundarias, mientras consulta una clase base compartida. Es decir: quieres las clases de hojas.Compruebe este fragmento de una solución: http://www.djangosnippets.org/snippets/1034/

También asegúrese de revisar los documentos sobre el marco ContentTypes de Django: http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ Puede ser un poco confuso al principio, pero ContentTypes va a resolver los problemas adicionales que probablemente se enfrentará al utilizar no clases base abstractas con el ORM de Django.

+0

upvoted para no usar '__new__' o' __metaclass__'. – keturn

+0

fragmento de enlace está roto. – freyley

1

bien, esto funciona: https://gist.github.com/348872

El poco complicado era esto.

class A(Top): 
    pass 

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

A.__new__ = staticmethod(newA) 

Ahora, hay algo acerca de la forma en Python se une __new__ que tal vez no entiendo muy bien, pero el quid de la cuestión es la siguiente: ModelBase metaclase de Django crea un nuevo objeto de la clase, en lugar de utilizar el que está aprobada en a su __new__; llame al A_prime. A continuación, pega todos los atributos que tenía en la definición de clase para A en A_prime, pero __new__ no se vuelve a vincular correctamente.

A continuación, cuando se evalúa A(1), A es en realidad A_prime aquí, pitón llama <A.__new__>(A_prime, 1), que no se corresponde, y que explote.

Así que la solución es definir su __new__después deA_prime ha sido definido.

Tal vez esto es un error en django.db.models.base.ModelBase.add_to_class, tal vez es un error en Python, no sé.

Ahora, cuando dije "esto funciona" anteriormente, quise decir esto funciona de forma aislada con el caso de prueba de construcción de objeto mínimo en la versión SVN actual de Django. No sé si realmente funciona como un modelo o si es útil en un QuerySet. Si realmente usas esto en el código de producción, haré un discurso público sobre rayos para pdxpython y haré que se burlen de ti hasta que nos compres todas las pizzas sin gluten.

+0

Heh. Bueno, eso me da el comportamiento de subclases necesario, ¡pero nunca rompe todas mis relaciones! Ninguno de mis objetos puede relacionarse con otros modelos. Lo investigaré más a fondo para ver si puedo solucionar ese aspecto. – outofculture

1

Simplemente adhiere @staticmethod antes del método __new__.

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