Frage

Ich habe mich gefragt, ob es möglich ist (und, wenn ja, wie) zu verketten mehreren Managern eine Abfrage Satz zu erzeugen, die von beiden der einzelnen Managern betroffen ist. Ich werde das spezielle Beispiel erklärt, dass ich arbeite an:

ich mehr abstrakten Modellklassen, die ich kleine, spezifische Funktionalität zu anderen Modellen zur Verfügung zu stellen verwenden. Zwei dieser Modelle sind eine DeleteMixin und ein GlobalMixin.

Die DeleteMixin ist definiert als solche:

class DeleteMixin(models.Model):
    deleted = models.BooleanField(default=False)
    objects = DeleteManager()

    class Meta:
        abstract = True

    def delete(self):
        self.deleted = True
        self.save()

Grundsätzlich bietet es sich um eine pseudo-gelöscht (der gelöschte Flag), anstatt das Objekt tatsächlich gelöscht wird.

Die GlobalMixin ist definiert als solche:

class GlobalMixin(models.Model):
    is_global = models.BooleanField(default=True)

    objects = GlobalManager()

    class Meta:
        abstract = True

Es ermöglicht jedes Objekt entweder als globales Objekt oder ein privates Objekt (wie eine öffentliche / private Blog-Post) definiert werden.

Beide haben ihre eigenen Manager, der die queryset beeinflussen, der zurückgegeben wird. Mein DeleteManager filtert die queryset nur Ergebnisse angezeigt, die die gelöschte Flag auf False gesetzt haben, während die GlobalManager filtert die queryset nur Ergebnisse angezeigt, die als global markiert sind. Hier ist die Erklärung für beide:

class DeleteManager(models.Manager):
    def get_query_set(self):
        return super(DeleteManager, self).get_query_set().filter(deleted=False)

class GlobalManager(models.Manager):
    def globals(self):
        return self.get_query_set().filter(is_global=1)

Die gewünschte Funktionalität wäre ein Modell haben beide dieser abstrakten Modelle erweitern und die Fähigkeit zu gewähren, um nur die Ergebnisse angezeigt, die sowohl nicht-gelöscht und global sind. Ich lief ein Testfall auf einem Modell mit vier Instanzen: zum einen global und nicht gelöscht, war global und gelöscht, war nicht global und nicht gelöscht, und man war nicht globale und gelöscht werden. Wenn ich versuche, Ergebnismengen als solche zu erhalten: SomeModel.objects.all (), erhalte ich Beispiel 1 und 3 (die beide nicht-gelöschten - super!). Wenn ich SomeModel.objects.globals versuchen (), erhalte ich eine Fehlermeldung, dass DeleteManager kein Globals hat (dies ist mein Modell Erklärung wird unter der Annahme, ist als solche. SomeModel (DeleteMixin, GlobalMixin) Wenn ich die Reihenfolge umkehren, I don‘ t die Fehler, aber es nicht die gelöschten herauszufiltern). Wenn ich GlobalMixin ändern anstelle von Objekten Globals GlobalManager zu befestigen (so der neue Befehl wäre SomeModel.globals.globals ()), erhalte ich Instanzen 1 und 2 (die beide Globals), während mein beabsichtigtes Ergebnis nur wäre zu bekommen Instanz 1 (die globale, nicht-gelöschten).

ich war nicht sicher, ob jemand in jede Situation dies ähnlich laufen hatte und zu einem Ergebnis kommen. Entweder ein Weg, um es in meinem derzeitigen Überlegungen oder Nacharbeiten zu machen Arbeit, die die Funktionalität bietet, nachdem ich würde sehr geschätzt werden. Ich weiß, dass dieser Beitrag ein wenig langatmig gewesen. Wenn mehr Erklärung erforderlich ist, würde ich mich freuen, es zu schaffen.

Edit:

Ich habe die schließliche Lösung gepostet ich unten auf dieses spezifische Problem verwendet. Es basiert auf dem Link zu Simons benutzerdefinierten QuerySetManager.

War es hilfreich?

Lösung

Sehen Sie dieses Snippet auf Djangosnippets: http://djangosnippets.org/snippets/734/

Statt einen eigenen Methoden in einem Manager des Setzens, Unterklasse Sie die queryset selbst. Es ist sehr einfach und funktioniert perfekt. Das einzige Problem, das ich je hatte mit Modell Vererbung, man muss immer die Manager in Modell-Subklassen definiert (nur: „Objekte = QuerySetManager ()“ in der Unterklasse), obwohl sie die queryset erben. Dies wird mehr Sinn machen, wenn Sie QuerySetManager verwenden.

Andere Tipps

Hier ist die spezifische Lösung für mein Problem des benutzerdefinierte QuerySetManager von Simon mit, dass Scott verknüpft.

from django.db import models
from django.contrib import admin
from django.db.models.query import QuerySet
from django.core.exceptions import FieldError

class MixinManager(models.Manager):    
    def get_query_set(self):
        try:
            return self.model.MixinQuerySet(self.model).filter(deleted=False)
        except FieldError:
            return self.model.MixinQuerySet(self.model)

class BaseMixin(models.Model):
    admin = models.Manager()
    objects = MixinManager()

    class MixinQuerySet(QuerySet):

        def globals(self):
            try:
                return self.filter(is_global=True)
            except FieldError:
                return self.all()

    class Meta:
        abstract = True

class DeleteMixin(BaseMixin):
    deleted = models.BooleanField(default=False)

    class Meta:
        abstract = True

    def delete(self):
        self.deleted = True
        self.save()

class GlobalMixin(BaseMixin):
    is_global = models.BooleanField(default=True)

    class Meta:
        abstract = True

Jede mixin in die Zukunft, die zusätzliche Funktionalität für die Abfrage muss eingestellt hinzufügen möchte einfach BaseMixin (oder haben es irgendwo in der Hierarchie) verlängern. Jedes Mal, wenn ich versuche, die Abfrage abgesetzt zu filtern, wickelte ich es in einem Try-Catch, falls dieses Feld existiert nicht wirklich (dh es nicht, dass mixin verlängern). Der globale Filter wird mit Globals aufgerufen (), während der Löschfilter automatisch aufgerufen wird (wenn etwas gelöscht wird, ich will es nie zeigen). Die Verwendung dieses Systems ermöglicht die folgenden Arten von Befehlen:

TemporaryModel.objects.all() # If extending DeleteMixin, no deleted instances are returned
TemporaryModel.objects.all().globals() # Filter out the private instances (non-global)
TemporaryModel.objects.filter(...) # Ditto about excluding deleteds

Eine Sache zu beachten ist, dass die Löschfilter nicht Admin-Schnittstellen beeinflussen, da die Standard-Manager zuerst deklariert wird (es der Standard machen). Ich erinnere mich nicht, wenn sie den Admin geändert Model._default_manager statt Model.objects zu verwenden, aber alle gelöschten Fällen wird noch im Admin erscheint (falls Sie müssen un-löschen).

verbrachte ich bei dem Versuch, einen Weg zu kommen eine schöne Fabrik zu bauen, dies zu tun, aber ich bin mit in eine Menge Probleme.

Das Beste, was ich Ihnen vorschlagen können, ist zu Kette Ihr Erbe. Es ist nicht sehr allgemein gehalten, so dass ich bin mir nicht sicher, wie nützlich es ist, aber alles, was Sie tun würden, ist:

class GlobalMixin(DeleteMixin):
    is_global = models.BooleanField(default=True)

    objects = GlobalManager()

    class Meta:
        abstract = True

class GlobalManager(DeleteManager):
    def globals(self):
        return self.get_query_set().filter(is_global=1)

Wenn Sie etwas allgemeinere wollen, das Beste kann ich kommen mit ist eine Basis Mixin und Manager zu definieren, die get_query_set() neu definiert (ich nehme an, Sie dies nur einmal tun wollen, die Dinge ziemlich anders bekommen kompliziert) und dann passieren eine Liste der Felder, die Sie über Mixins hinzugefügt wollen würde.

Es wäre etwa so aussehen (nicht getestet):

class DeleteMixin(models.Model):
    deleted = models.BooleanField(default=False)

    class Meta:
        abstract = True

def create_mixin(base_mixin, **kwargs):
    class wrapper(base_mixin):
        class Meta:
            abstract = True
    for k in kwargs.keys():
        setattr(wrapper, k, kwargs[k])
    return wrapper

class DeleteManager(models.Manager):
    def get_query_set(self):
        return super(DeleteManager, self).get_query_set().filter(deleted=False)

def create_manager(base_manager, **kwargs):
    class wrapper(base_manager):
        pass
    for k in kwargs.keys():
        setattr(wrapper, k, kwargs[k])
    return wrapper

Ok, so ist dieses hässliche, aber was es Sie bekommt? Im Wesentlichen ist es die gleiche Lösung, aber viel dynamischer, und ein wenig mehr DRY, obwohl komplizierter zu lesen.

Zuerst erstellen Sie Ihren Manager dynamisch:

def globals(inst):
    return inst.get_query_set().filter(is_global=1)

GlobalDeleteManager = create_manager(DeleteManager, globals=globals)

Dies erzeugt einen neuen Manager, die eine Unterklasse von DeleteManager und hat eine Methode namens globals.

Als Nächstes erstellen Sie Ihr mixin Modell:

GlobalDeleteMixin = create_mixin(DeleteMixin,
                                 is_global=models.BooleanField(default=False),
                                 objects = GlobalDeleteManager())

Wie ich schon sagte, es ist hässlich. Aber es bedeutet, dass Sie nicht globals() neu definieren. Wenn Sie eine andere Art von Managern wollen globals() haben, rufen Sie einfach create_manager erneut mit einer anderen Basis. Und Sie können so viele neue Methoden hinzufügen, wie Sie möchten. Das Gleiche gilt für den Manager, Sie halten nur neue Funktionen hinzufügen, die verschiedenen querysets zurückkehren wird.

Also, das ist wirklich praktisch? Vielleicht nicht. Diese Antwort ist eine Übung in der (ab) Pythons Flexibilität verwendet wird. Ich habe nicht versucht, diese mit, obwohl ich einige der zugrunde liegenden Prinzipien der dynamisch verlaufenden Klassen verwenden Sie die Dinge leichter zugänglich zu machen.

Lassen Sie mich wissen, wenn etwas unklar ist, und ich werde die Antwort aktualisieren.

Eine weitere Option eine Überlegung wert ist die PassThroughManager:

https: //django-model-utils.readthedocs. org / en / latest / managers.html # passthroughmanager

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