Question

Comme dans cette question , sauf que je veux être en mesure d'avoir QuerySets qui retournent un corps mixte d'objets:

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

Je me suis dit que je ne peux pas mettre Product.Meta.abstract à vrai ou non seulement ou ensemble QuerySets d'objets différents. Très bien, mais ce sont toutes les sous-classes d'une classe commune, donc si je laisse leur superclasse comme non abstraite je devrais être heureux, tant que je peux obtenir son directeur pour retourner des objets de la classe appropriée. Le code de requête dans django fait son travail et fait juste appel au produit (). Cela semble assez facile, sauf qu'il explose quand j'Override Product.__new__, je devine à cause du __metaclass__ dans le modèle ... Voici le code non-django qui se comporte à peu près ce que je veux:

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>

Mais cela ne fonctionne pas si je hérité de django.db.models.Model au lieu 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)

Ce qui est un backtrace notamment merdique; Je ne peux pas entrer dans le cadre de mon code __new__ dans le débogueur, que ce soit. Je l'ai essayé diverses super(A, cls), Top, super(A, A), et tous ci-dessus en combinaison avec le passage cls comme premier argument à __new__, en vain. Pourquoi est-ce si dur me donner des coups? Dois-je comprendre les métaclasses de django pour être en mesure de résoudre ce problème ou est-il une meilleure façon d'atteindre mes objectifs?

Était-ce utile?

La solution

En fait ce que vous essayez de faire est de retourner les différentes classes d'enfants, alors que l'interrogation d'une classe de base commune. C'est: vous voulez que les classes de feuilles. Vérifiez cet extrait d'une solution: http://www.djangosnippets.org/snippets/1034/

Assurez-vous aussi de consulter la documentation sur le cadre ContentTypes de Django: http : //docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ Il peut être un peu déroutant au début, mais ContentTypes résoudra des problèmes supplémentaires que vous aurez probablement rencontrer lorsque vous utilisez des classes de base non abstraites avec ORM de Django.

Autres conseils

Vous voulez un de ces:

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

Il y a des inconvénients, à savoir des requêtes supplémentaires.

D'accord, cela fonctionne: https://gist.github.com/348872

Le peu délicat était cela.

class A(Top):
    pass

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

A.__new__ = staticmethod(newA)

Maintenant, il y a quelque chose sur la façon dont Python lie __new__ que je peut-être ne comprends pas tout à fait, mais l'essentiel de c'est ceci: ModelBase la métaclasse de django crée un nouvel objet de classe, plutôt que d'utiliser celui qui est passé dans son __new__; appeler A_prime. Ensuite, il colle tous les attributs que vous aviez dans la définition de classe pour A à A_prime, mais __new__ ne soit pas lié à nouveau correctement.

Ensuite, lorsque vous évaluez A(1), A est en fait A_prime ici, appelle python <A.__new__>(A_prime, 1), qui ne correspond pas, et il explose.

La solution est donc de définir votre __new__ après A_prime a été défini.

Peut-être que ce bogue dans django.db.models.base.ModelBase.add_to_class, peut-être que c'est un bogue dans Python, je ne sais pas.

Maintenant, quand je dis « cela fonctionne » plus tôt, je voulais dire cela fonctionne en vase clos avec le cas d'essai de construction d'objet minimal dans la version actuelle SVN de Django . Je ne sais pas si cela fonctionne en fait comme un modèle ou est utile dans un QuerySet. Si vous utilisez dans votre code de production, je ferai un éclairage public de parler pour pdxpython et demandez-leur vous moquer de nous jusqu'à ce que vous achetez tous les pizzas sans gluten.

Il suffit de coller @staticmethod avant que la méthode __new__.

@staticmethod
def __new__(cls, *args, **kwargs):
    print args, kwargs
    return super(License, cls).__new__(cls, *args, **kwargs)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top