Frage

Wie in diese Frage, außer ich möchte in der Lage sein, QuerySets zu haben, die einen gemischten Teil von Objekten zurückgeben:

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

Ich habe herausgefunden, dass ich nicht einfach einstellen kann Product.Meta.abstract zu wahr oder auf andere Weise nur oder zusammen Querysets unterschiedlicher Objekte. Gut, aber das sind alles Unterklassen einer gemeinsamen Klasse. Wenn ich also ihre Superklasse als nicht abstreckt lasse, sollte ich glücklich sein, solange ich seinen Manager dazu bringen kann, Objekte der richtigen Klasse zurückzugeben. Der Abfragecode in Django macht seine Sache und trifft nur an Product (). Klingt einfach genug, außer dass es aufbläst, wenn ich überschreibe Product.__new__, Ich schätze wegen der __metaclass__ In Modell ... hier ist der Nicht-Django-Code, der sich so gut verhält, wie ich es will:

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>

Aber das scheitert, wenn ich von django.db.models.Model Anstatt von 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)

Das ist eine besonders beschissene Backtrace; Ich kann nicht in den Rahmen meiner __new__ auch im Debugger Code. Ich habe unterschiedlich versucht super(A, cls), Top, super(A, A), und all das oben in Kombination mit dem Pass cls in als erstes Argument zu __new__, alles ohne Erfolg. Warum tritt mich das so hart? Muss ich Djangos Metaklasse herausfinden, um dies zu beheben, oder gibt es eine bessere Möglichkeit, meine Ziele zu erreichen?

War es hilfreich?

Lösung

Grundsätzlich versuchen Sie, die verschiedenen Kinderklassen zurückzugeben, während Sie eine gemeinsame Basisklasse abfragen. Das heißt: Sie wollen die Blattklassen. Überprüfen Sie diesen Ausschnitt für eine Lösung: http://www.djangosnippets.org/snippets/1034/

Schauen Sie sich auch die Dokumente im ContentTypes Framework von Django an: http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ Es kann zunächst ein bisschen verwirrend sein, aber ContentTypes löst zusätzliche Probleme, mit denen Sie wahrscheinlich bei nicht abstrakten Basisklassen mit Djangos ORM konfrontiert sind.

Andere Tipps

Sie wollen eines davon:

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

Es gibt Nachteile, nämlich zusätzliche Fragen.

Okay, das funktioniert: https://gist.github.com/348872

Das knifflige Stück war das.

class A(Top):
    pass

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

A.__new__ = staticmethod(newA)

Jetzt gibt es etwas darüber, wie Python bindet __new__ Dass ich vielleicht nicht ganz verstehe, aber das Kern davon ist Folgendes: Django's ModelBase Metaclass erstellt ein neues Klassenobjekt, anstatt das zu verwenden, der in seine übergeben wird __new__; Nennen Sie das A_prime. Dann bleibt alle Attribute, für die Sie in der Klassendefinition hatten A auf zu A_prime, aber __new__ wird nicht richtig wiederhergestellt.

Dann, wenn Sie bewerten A(1), A ist eigentlich A_prime Hier ruft Python an <A.__new__>(A_prime, 1), was nicht übereinstimmt und es explodiert.

Die Lösung besteht also darin, Ihre zu definieren __new__ nach A_prime Wurde definiert.

Vielleicht ist das ein Fehler in django.db.models.base.ModelBase.add_to_class, Vielleicht ist es ein Fehler in Python, ich weiß es nicht.

Nun, als ich früher "das funktioniert" sagte, meinte ich Dies funktioniert isoliert mit dem Fall minimaler Objektkonstruktionstest in der aktuellen SVN -Version von Django. Ich weiß nicht, ob es tatsächlich als Modell funktioniert oder in einem Queryset nützlich ist. Wenn Sie dies tatsächlich im Produktionscode verwenden, werde ich für PDXPython ein öffentliches Blitzgespräch daraus machen und sie verspotten lassen, bis Sie uns alle glutenfreie Pizza kaufen.

Halten Sie einfach @StaticMethod vor die __new__ Methode.

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

Ein weiterer Ansatz, den ich kürzlich gefunden habe: http://jeffelmore.org/2010/11/11/automatic-downcasting-ofherited-models-in-django/

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top