Ansvariga¶
En Manager
är det gränssnitt genom vilket databasfrågeoperationer tillhandahålls till Django-modeller. Det finns minst en Manager
för varje modell i en Django-applikation.
Hur klasserna Manager
fungerar finns dokumenterat i Göra förfrågningar; detta dokument tar specifikt upp modellalternativ som anpassar Manager
beteende.
Namn på chefer¶
Som standard lägger Django till en Manager
med namnet objects
till varje Django modellklass. Men om du vill använda objects
som fältnamn, eller om du vill använda ett annat namn än objects
för Manager
, kan du byta namn på den per modell. Om du vill byta namn på Manager
för en viss klass definierar du ett klassattribut av typen models.Manager()
för den modellen. Till exempel:
from django.db import models
class Person(models.Model):
# ...
people = models.Manager()
Med hjälp av denna exempelmodell kommer Person.objects
att generera ett AttributeError
undantag, men Person.people.all()
kommer att ge en lista över alla Person
objekt.
Anpassade chefer¶
Du kan använda en anpassad Manager
i en viss modell genom att utöka basklassen Manager
och instansiera din anpassade Manager
i din modell.
Det finns två anledningar till att du vill anpassa en Manager
: att lägga till extra Manager
-metoder och/eller att ändra den första QuerySet
som Manager
returnerar.
Lägga till extra förvaltningsmetoder¶
Att lägga till extra Manager
-metoder är det bästa sättet att lägga till funktionalitet på ”tabellnivå” i dina modeller. (För funktioner på ”radnivå”, dvs. funktioner som verkar på en enda instans av ett modellobjekt, ska du använda Model methods, inte anpassade Manager
-metoder)
Till exempel lägger den här anpassade Manager
till en metod with_counts()
:
from django.db import models
from django.db.models.functions import Coalesce
class PollManager(models.Manager):
def with_counts(self):
return self.annotate(num_responses=Coalesce(models.Count("response"), 0))
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
objects = PollManager()
class Response(models.Model):
poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
# ...
I det här exemplet skulle du använda OpinionPoll.objects.with_counts()
för att få en QuerySet
av OpinionPoll
-objekt med det extra attributet num_responses
.
En anpassad Manager
-metod kan returnera vad du vill. Den behöver inte returnera en QuerySet
.
En annan sak att notera är att Manager
-metoder kan komma åt self.model
för att hämta den modellklass som de är kopplade till.
Ändra en chefs initiala QuerySet
¶
En Manager
’s bas QuerySet
returnerar alla objekt i systemet. Till exempel, med hjälp av denna modell:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
…kommer uttalandet Book.objects.all()
att returnera alla böcker i databasen.
Du kan åsidosätta en Manager
’s bas QuerySet
genom att åsidosätta metoden Manager.get_queryset()
. get_queryset()
bör returnera en QuerySet
med de egenskaper du behöver.
Till exempel har följande modell två Manager
- en som returnerar alla objekt och en som bara returnerar böckerna av Roald Dahl:
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(author="Roald Dahl")
# Then hook it into the Book model explicitly.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
dahl_objects = DahlBookManager() # The Dahl-specific manager.
Med den här exempelmodellen kommer Book.objects.all()
att returnera alla böcker i databasen, men Book.dahl_objects.all()
kommer bara att returnera de som är skrivna av Roald Dahl.
Eftersom get_queryset()
returnerar ett QuerySet
-objekt kan du använda filter()
, exclude()
och alla andra QuerySet
-metoder på det. Så dessa uttalanden är alla lagliga:
Book.dahl_objects.all()
Book.dahl_objects.filter(title="Matilda")
Book.dahl_objects.count()
Detta exempel visade också på en annan intressant teknik: att använda flera managers på samma modell. Du kan koppla så många Manager()
-instanser till en modell som du vill. Detta är ett icke-repetitivt sätt att definiera gemensamma ”filter” för dina modeller.
Till exempel:
class AuthorManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(role="A")
class EditorManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(role="E")
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
role = models.CharField(max_length=1, choices={"A": _("Author"), "E": _("Editor")})
people = models.Manager()
authors = AuthorManager()
editors = EditorManager()
I det här exemplet kan du begära Person.authors.all()
, Person.editors.all()
och Person.people.all()
, vilket ger förutsägbara resultat.
Förvaltare som standard¶
- Model._default_manager¶
Om du använder anpassade Manager
-objekt, notera att den första Manager
som Django stöter på (i den ordning de definieras i modellen) har en speciell status. Django tolkar den första Manager
som definieras i en klass som ”standard” Manager
, och flera delar av Django (inklusive dumpdata
) kommer att använda den Manager
exklusivt för den modellen. Som ett resultat är det en bra idé att vara försiktig i ditt val av standardhanterare för att undvika en situation där åsidosättande av get_queryset()
resulterar i en oförmåga att hämta objekt som du vill arbeta med.
Du kan ange en anpassad standardhanterare med Meta.default_manager_name <django.db.models.Options.default_manager_name>`
.
Om du skriver kod som måste hantera en okänd modell, t.ex. i en tredjepartsapp som implementerar en generisk vy, ska du använda den här hanteraren (eller _base_manager
) i stället för att anta att modellen har en objects
-hanterare.
Baschefer¶
- Model._base_manager¶
Filtrera inte bort några resultat i den här typen av manager-underklass¶
Denna manager används för att komma åt objekt som är relaterade till från någon annan modell. I dessa situationer måste Django kunna se alla objekt för den modell som hämtas, så att allt som hänvisas till kan hämtas.
Därför bör du inte åsidosätta get_queryset()
för att filtrera bort några rader. Om du gör det kommer Django att returnera ofullständiga resultat.
Anropa anpassade metoder för QuerySet
från hanteraren¶
Medan de flesta metoder från standard QuerySet
är tillgängliga direkt från Manager
, är detta endast fallet för de extra metoder som definieras på en anpassad QuerySet
om du också implementerar dem på Manager
:
class PersonQuerySet(models.QuerySet):
def authors(self):
return self.filter(role="A")
def editors(self):
return self.filter(role="E")
class PersonManager(models.Manager):
def get_queryset(self):
return PersonQuerySet(self.model, using=self._db)
def authors(self):
return self.get_queryset().authors()
def editors(self):
return self.get_queryset().editors()
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
role = models.CharField(max_length=1, choices={"A": _("Author"), "E": _("Editor")})
people = PersonManager()
I det här exemplet kan du anropa både authors()
och editors()
direkt från hanteraren Person.people
.
Skapa en manager med QuerySet
-metoder¶
I stället för ovanstående tillvägagångssätt som kräver duplicering av metoder på både QuerySet
och Manager
, QuerySet.as_manager()
kan användas för att skapa en instans av Manager
med en kopia av en anpassad QuerySet
’s metoder:
class Person(models.Model):
...
people = PersonQuerySet.as_manager()
Instansen Manager
som skapas av QuerySet.as_manager()
kommer att vara praktiskt taget identisk med PersonManager
från föregående exempel.
Inte alla QuerySet
-metoder är meningsfulla på Manager
-nivån; till exempel förhindrar vi avsiktligt QuerySet.delete()
-metoden från att kopieras till Manager
-klassen.
Metoderna kopieras enligt följande regler:
Publika metoder kopieras som standard.
Privata metoder (som börjar med en understrykning) kopieras inte som standard.
Metoder med attributet
queryset_only
inställt påFalse
kopieras alltid.Metoder med attributet
queryset_only
inställt påTrue
kopieras aldrig.
Till exempel:
class CustomQuerySet(models.QuerySet):
# Available on both Manager and QuerySet.
def public_method(self):
return
# Available only on QuerySet.
def _private_method(self):
return
# Available only on QuerySet.
def opted_out_public_method(self):
return
opted_out_public_method.queryset_only = True
# Available on both Manager and QuerySet.
def _opted_in_private_method(self):
return
_opted_in_private_method.queryset_only = False
from_queryset()
¶
- classmethod from_queryset(queryset_class)¶
För avancerad användning kanske du vill ha både en anpassad Manager
och en anpassad QuerySet
. Du kan göra det genom att anropa Manager.from_queryset()
som returnerar en subklass av din bas Manager
med en kopia av de anpassade QuerySet
metoderna:
class CustomManager(models.Manager):
def manager_only_method(self):
return
class CustomQuerySet(models.QuerySet):
def manager_and_queryset_method(self):
return
class MyModel(models.Model):
objects = CustomManager.from_queryset(CustomQuerySet)()
Du kan också lagra den genererade klassen i en variabel:
MyManager = CustomManager.from_queryset(CustomQuerySet)
class MyModel(models.Model):
objects = MyManager()
Anpassade förvaltare och modellarv¶
Så här hanterar Django egna managers och modellarv:
Hanterare från basklasser ärvs alltid av underklassen, med Pythons normala namnupplösningsordning (namn på underklassen åsidosätter alla andra; sedan kommer namn på den första föräldraklassen, och så vidare).
Om inga managers deklareras på en modell och/eller dess föräldrar skapar Django automatiskt manager
objects
.Standardhanteraren för en klass är antingen den som valts med
Meta.default_manager_name
, eller den första hanteraren som deklarerats på modellen, eller standardhanteraren för den första överordnade modellen.
Dessa regler ger den flexibilitet som krävs om du vill installera en samling anpassade hanterare på en grupp modeller, via en abstrakt basklass, men ändå anpassa standardhanteraren. Anta till exempel att du har den här basklassen:
class AbstractBase(models.Model):
# ...
objects = CustomManager()
class Meta:
abstract = True
Om du använder detta direkt i en underordnad klass kommer objects
att vara standardhanteraren om du inte deklarerar några hanterare i den underordnade klassen:
class ChildA(AbstractBase):
# ...
# This class has CustomManager as the default manager.
pass
Om du vill ärva från AbstractBase
, men tillhandahålla en annan standardhanterare, kan du ange standardhanteraren på barnklassen:
class ChildB(AbstractBase):
# ...
# An explicit default manager.
default_manager = OtherManager()
Här är default_manager
standard. Hanteraren objects
är fortfarande tillgänglig, eftersom den ärvs, men används inte som standard.
Slutligen för det här exemplet, anta att du vill lägga till extra chefer i barnklassen, men fortfarande använda standard från AbstractBase
. Du kan inte lägga till den nya hanteraren direkt i barnklassen, eftersom det skulle åsidosätta standardvärdet och du måste också uttryckligen inkludera alla hanterare från den abstrakta basklassen. Lösningen är att lägga de extra förvaltarna i en annan basklass och införa den i arvshierarkin efter standardvärdena:
class ExtraManager(models.Model):
extra_manager = OtherManager()
class Meta:
abstract = True
class ChildC(AbstractBase, ExtraManager):
# ...
# Default manager is CustomManager, but OtherManager is
# also available via the "extra_manager" attribute.
pass
Observera att även om du kan definiera en anpassad manager på den abstrakta modellen, kan du inte invoke några metoder som använder den abstrakta modellen. Det vill säga:
ClassA.objects.do_something()
är lagligt, men:
AbstractBase.objects.do_something()
kommer att ge upphov till ett undantag. Detta beror på att managers är avsedda att kapsla in logik för att hantera samlingar av objekt. Eftersom du inte kan ha en samling av abstrakta objekt är det inte meningsfullt att hantera dem. Om du har funktionalitet som gäller för den abstrakta modellen bör du lägga den funktionaliteten i en staticmethod
eller classmethod
på den abstrakta modellen.
Problem med genomförandet¶
Oavsett vilka funktioner du lägger till i din anpassade Manager
måste det vara möjligt att göra en ytlig kopia av en Manager
-instans, dvs. följande kod måste fungera:
>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)
Django gör ytliga kopior av managerobjekt under vissa frågor; om din manager inte kan kopieras kommer dessa frågor att misslyckas.
Detta kommer inte att vara ett problem för de flesta anpassade förvaltare. Om du bara lägger till enkla metoder till din Manager
är det osannolikt att du oavsiktligt kommer att göra instanser av din Manager
okopierbara. Men om du åsidosätter __getattr__
eller någon annan privat metod i ditt Manager
-objekt som kontrollerar objektets tillstånd, bör du se till att du inte påverkar möjligheten för din Manager
att kopieras.