Göra förfrågningar¶
När du har skapat dina datamodeller ger Django dig automatiskt ett API för databasabstraktion som låter dig skapa, hämta, uppdatera och radera objekt. Detta dokument förklarar hur man använder detta API. Se data model reference för fullständig information om alla de olika alternativen för modelluppslagning.
I den här handboken (och i referensen) hänvisar vi till följande modeller, som utgör en bloggapplikation:
from datetime import date
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField(default=date.today)
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField(default=0)
number_of_pingbacks = models.IntegerField(default=0)
rating = models.IntegerField(default=5)
def __str__(self):
return self.headline
Skapa objekt¶
För att representera databastabelldata i Python-objekt använder Django ett intuitivt system: En modellklass representerar en databastabell, och en instans av den klassen representerar en viss post i databastabellen.
För att skapa ett objekt, instansiera det med hjälp av nyckelordsargument till modellklassen och anropa sedan save()
för att spara det i databasen.
Förutsatt att modellerna finns i filen models.py
i en Django-app med namnet blog
, följer här ett exempel:
>>> from blog.models import Blog
>>> b = Blog(name="Beatles Blog", tagline="All the latest Beatles news.")
>>> b.save()
Detta utför en INSERT
SQL-sats bakom kulisserna. Django träffar inte databasen förrän du uttryckligen anropar save()
.
Metoden save()
har inget returvärde.
Spara ändringar av objekt¶
Om du vill spara ändringar i ett objekt som redan finns i databasen använder du save()
.
Med en Blog
-instans b5
som redan har sparats i databasen ändrar detta exempel dess namn och uppdaterar dess post i databasen:
>>> b5.name = "New name"
>>> b5.save()
Detta utför en UPDATE
SQL-sats bakom kulisserna. Django träffar inte databasen förrän du uttryckligen anropar save()
.
Spara fälten ForeignKey
och ManyToManyField
¶
Uppdatering av ett ForeignKey
-fält fungerar exakt på samma sätt som att spara ett vanligt fält - tilldela ett objekt av rätt typ till fältet i fråga. Detta exempel uppdaterar attributet blog
för en Entry
-instans entry
, förutsatt att lämpliga instanser av Entry
och Blog
redan har sparats i databasen (så att vi kan hämta dem nedan):
>>> from blog.models import Blog, Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()
Uppdatering av en ManyToManyField
fungerar lite annorlunda - använd metoden add()
på fältet för att lägga till en post i relationen. Detta exempel lägger till Author
-instansen joe
till entry
-objektet:
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)
För att lägga till flera poster till en ManyToManyField
på en gång, inkludera flera argument i anropet till add()
, så här:
>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)
Django kommer att klaga om du försöker tilldela eller lägga till ett objekt av fel typ.
Hämta objekt¶
För att hämta objekt från din databas, konstruera en QuerySet
via en Manager
på din modellklass.
En QuerySet
representerar en samling objekt från din databas. Den kan ha noll, ett eller många filter. Filter begränsar frågeresultaten baserat på de angivna parametrarna. I SQL-termer motsvarar en QuerySet
en SELECT
-sats, och ett filter är en begränsningsklausul som WHERE
eller LIMIT
.
Du får en QuerySet
genom att använda din modells Manager
. Varje modell har minst en Manager
, och den kallas objects
som standard. Du kan komma åt den direkt via modellklassen, så här:
>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name="Foo", tagline="Bar")
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."
Observera
En Manager
är endast tillgänglig via modellklasser, snarare än från modellinstanser, för att upprätthålla en åtskillnad mellan operationer på ”tabellnivå” och operationer på ”postnivå”.
Manager
är den huvudsakliga källan till querysets för en modell. Till exempel returnerar Blog.objects.all()
en QuerySet
som innehåller alla Blog
-objekt i databasen.
Hämta alla objekt¶
Det enklaste sättet att hämta objekt från en tabell är att hämta dem alla. För att göra detta använder du metoden all()
på en Manager
:
>>> all_entries = Entry.objects.all()
Metoden all()
returnerar en QuerySet
av alla objekt i databasen.
Hämta specifika objekt med filter¶
Den QuerySet
som returneras av all()
beskriver alla objekt i databastabellen. Vanligtvis behöver du dock bara välja en delmängd av den fullständiga uppsättningen objekt.
För att skapa en sådan delmängd förfinar du den ursprungliga QuerySet
genom att lägga till filtervillkor. De två vanligaste sätten att förfina en QuerySet
är:
filter(**kwargs)
Returnerar en ny
QuerySet
som innehåller objekt som matchar de angivna uppslagsparametrarna.exkludera(**kwargs)
Returnerar en ny
QuerySet
som innehåller objekt som inte matchar de angivna uppslagsparametrarna.
Uppslagsparametrarna (**kwargs
i ovanstående funktionsdefinitioner) ska vara i det format som beskrivs i Field lookups nedan.
Om du till exempel vill få en QuerySet
med bloggposter från år 2006 använder du filter()
så här:
Entry.objects.filter(pub_date__year=2006)
Med den förvalda managerklassen är det samma sak som:
Entry.objects.all().filter(pub_date__year=2006)
Kedja filter¶
Resultatet av att förfina en QuerySet
är i sig en QuerySet
, så det är möjligt att kedja förfiningar tillsammans. Till exempel:
>>> Entry.objects.filter(headline__startswith="What").exclude(
... pub_date__gte=datetime.date.today()
... ).filter(pub_date__gte=datetime.date(2005, 1, 30))
Detta tar den ursprungliga QuerySet
av alla poster i databasen, lägger till ett filter, sedan en uteslutning, sedan ytterligare ett filter. Slutresultatet är en QuerySet
som innehåller alla poster med en rubrik som börjar med ”What”, som publicerades mellan den 30 januari 2005 och den aktuella dagen.
Filtrerade QuerySet
är unika¶
Varje gång du förfinar en QuerySet
får du en helt ny QuerySet
som inte på något sätt är bunden till den tidigare QuerySet
. Varje förfining skapar en separat och distinkt QuerySet
som kan lagras, användas och återanvändas.
Exempel:
>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())
Dessa tre querysets är separata. Den första är en bas QuerySet
som innehåller alla poster som innehåller en rubrik som börjar med ”What”. Den andra är en delmängd av den första, med ett ytterligare kriterium som utesluter poster vars pub_date
är idag eller i framtiden. Den tredje är en delmängd av den första, med ett ytterligare kriterium som endast väljer poster vars pub_date
är idag eller i framtiden. Den ursprungliga QuerySet
(q1
) påverkas inte av förfiningsprocessen.
”QuerySet”: ”De är lata¶
QuerySet
-objekt är lata - handlingen att skapa en QuerySet
involverar inte någon databasaktivitet. Du kan stapla filter tillsammans hela dagen, och Django kommer faktiskt inte att köra frågan förrän QuerySet
är utvärderad. Ta en titt på detta exempel:
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)
Även om det här ser ut som tre träffar i databasen, träffar den i själva verket databasen bara en gång, på den sista raden (print(q)
). I allmänhet hämtas inte resultaten från en QuerySet
från databasen förrän du ”ber” om dem. När du gör det utvärderas QuerySet
genom att komma åt databasen. För mer information om exakt när utvärderingen sker, se När QuerySet utvärderas.
Hämta ett enskilt objekt med get()
¶
filter()
ger dig alltid en QuerySet
, även om bara ett enda objekt matchar frågan - i det här fallet kommer det att vara en QuerySet
som innehåller ett enda element.
Om du vet att det bara finns ett objekt som matchar din fråga kan du använda metoden get()
på en Manager
som returnerar objektet direkt:
>>> one_entry = Entry.objects.get(pk=1)
Du kan använda vilket frågeuttryck som helst med get()
, precis som med filter()
- se Fältuppslagningar nedan.
Observera att det är skillnad mellan att använda get()
och att använda filter()
med en slice av [0]
. Om det inte finns några resultat som matchar frågan kommer get()
att ge upphov till ett DoesNotExist
undantag. Detta undantag är ett attribut för den modellklass som frågan utförs på - så i koden ovan, om det inte finns något Entry
-objekt med en primärnyckel på 1, kommer Django att höja Entry.DoesNotExist
.
På samma sätt kommer Django att klaga om mer än ett objekt matchar get()
-frågan. I det här fallet kommer det att höja MultipleObjectsReturned
, vilket återigen är ett attribut för själva modellklassen.
Andra metoder för QuerySet
¶
För det mesta använder du all()
, get()
, filter()
och exclude()
när du behöver leta upp objekt från databasen. Men det är långt ifrån allt som finns; se QuerySet API Reference för en fullständig lista över alla olika QuerySet
-metoder.
Begränsning av QuerySet
¶
Använd en delmängd av Pythons array-slicing-syntax för att begränsa din QuerySet
till ett visst antal resultat. Detta är motsvarigheten till SQL:s LIMIT
och OFFSET
-klausuler.
Till exempel returneras de 5 första objekten (LIMIT 5
):
>>> Entry.objects.all()[:5]
Detta returnerar det sjätte till det tionde objektet (OFFSET 5 LIMIT 5
):
>>> Entry.objects.all()[5:10]
Negativ indexering (t.ex. Entry.objects.all()[-1]
) stöds inte.
Generellt sett returnerar skivning av en QuerySet
en ny QuerySet
– den utvärderar inte frågan. Ett undantag är om du använder parametern ”step” i Pythons slice-syntax. Till exempel skulle detta faktiskt utföra frågan för att returnera en lista över varje andra objekt av de första 10:
>>> Entry.objects.all()[:10:2]
Ytterligare filtrering eller ordning av en skivad frågeuppsättning är förbjuden på grund av den tvetydiga karaktären hos hur det kan fungera.
Om du vill hämta ett enkelt objekt i stället för en lista (t.ex. SELECT foo FROM bar LIMIT 1
) använder du ett index i stället för en slice. Till exempel returnerar detta den första Entry
i databasen, efter att ha ordnat posterna alfabetiskt efter rubrik:
>>> Entry.objects.order_by("headline")[0]
Detta motsvarar ungefär:
>>> Entry.objects.order_by("headline")[0:1].get()
Observera dock att den första av dessa kommer att ge upphov till IndexError
medan den andra kommer att ge upphov till DoesNotExist
om inga objekt matchar de angivna kriterierna. Se get()
för mer information.
Fältuppslagningar¶
Fältuppslagningar är hur du anger köttet i en SQL WHERE
-klausul. De anges som nyckelordsargument till QuerySet
-metoderna filter()
, exclude()
och get()
.
Argument för nyckelord i Basic Lookups har formen field__lookuptype=value
. (Det är en dubbel-underscore). Till exempel:
>>> Entry.objects.filter(pub_date__lte="2006-01-01")
översätts (ungefär) till följande SQL:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
Hur detta är möjligt
Python har möjlighet att definiera funktioner som accepterar godtyckliga namn-värde-argument vars namn och värden utvärderas vid körning. För mer information, se Keyword Arguments i den officiella Python-handledningen.
Fältet som anges i en lookup måste vara namnet på ett modellfält. Det finns dock ett undantag, i händelse av en ForeignKey
kan du ange fältnamnet suffixerat med _id
. I det här fallet förväntas parametern value innehålla råvärdet för den utländska modellens primärnyckel. Till exempel:
>>> Entry.objects.filter(blog_id=4)
Om du skickar ett ogiltigt nyckelordsargument kommer en lookup-funktion att ge upphov till TypeError
.
Databasens API stöder ungefär två dussin uppslagstyper; en fullständig referens finns i :ref:field lookup reference <field-lookups>
. För att ge dig en försmak av vad som finns tillgängligt, här är några av de vanligaste uppslagningarna du förmodligen kommer att använda:
exakt
En ”exakt” matchning. Till exempel:
>>> Entry.objects.get(headline__exact="Cat bites dog")
Skulle generera SQL enligt dessa linjer:
SELECT ... WHERE headline = 'Cat bites dog';
Om du inte anger någon uppslagstyp - det vill säga om ditt nyckelordsargument inte innehåller ett dubbelt understreck - antas uppslagstypen vara ”exakt”.
Till exempel är följande två påståenden likvärdiga:
>>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied
Detta är för enkelhetens skull, eftersom
exakta
uppslagningar är det vanliga fallet.iexact
En matchning utan skiftlägeskänslighet. Så, frågan:
>>> Blog.objects.get(name__iexact="beatles blog")
Skulle matcha en
Blogg
med titeln"Beatles Blog"
,"beatles blog"
, eller till och med"BeAtlES blOG"
.innehåller
Test med skiftlägeskänslighet. Till exempel:
Entry.objects.get(headline__contains="Lennon")
Översätts ungefär till denna SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
Observera att detta kommer att matcha rubriken
'Today Lennon honored'
men inte'today lennon honored'
.Det finns också en version som inte tar hänsyn till skiftlägeskod,
icontains
.startarmed
,slutarmed
Börjar med respektive slutar med sökning. Det finns också versioner utan skiftlägeskänslighet som heter
istartswith
ochiendswith
.
Återigen, detta skrapar bara på ytan. En fullständig referens finns i :ref:field lookup reference <field-lookups>
.
Uppslagningar som sträcker sig över relationer¶
Django erbjuder ett kraftfullt och intuitivt sätt att ”följa” relationer i uppslagningar, och tar hand om SQL JOIN
åt dig automatiskt, bakom kulisserna. För att spänna över en relation använder du fältnamnet på relaterade fält i olika modeller, separerade med dubbla understreck, tills du kommer till det fält du vill ha.
Detta exempel hämtar alla Entry
-objekt med en Blog
vars namn
är 'Beatles Blog'
:
>>> Entry.objects.filter(blog__name="Beatles Blog")
Denna spänning kan vara så djup som du vill.
Det fungerar också baklänges. Även om det kan anpassas
, hänvisar du som standard till ett ”omvänt” förhållande i en uppslagning med hjälp av modellens gemena namn.
Detta exempel hämtar alla Blog
objekt som har minst en Entry
vars headline
innehåller 'Lennon'
:
>>> Blog.objects.filter(entry__headline__contains="Lennon")
Om du filtrerar över flera relationer och en av de mellanliggande modellerna inte har ett värde som uppfyller filtervillkoret, kommer Django att behandla det som om det finns ett tomt (alla värden är NULL
), men giltigt, objekt där. Allt detta innebär är att inget fel kommer att uppstå. Till exempel i detta filter:
Blog.objects.filter(entry__authors__name="Lennon")
(om det fanns en relaterad Author
-modell), om det inte fanns någon author
associerad med en post, skulle det behandlas som om det inte heller fanns något name
bifogat, snarare än att ge ett fel på grund av den saknade author
. Vanligtvis är detta precis vad du vill ska hända. Det enda fallet där det kan vara förvirrande är om du använder isnull
. Alltså:
Blog.objects.filter(entry__authors__name__isnull=True)
kommer att returnera Blog
-objekt som har ett tomt namn
på författaren
och även de som har en tom författare
på inlägget
. Om du inte vill ha de senare objekten kan du skriva:
Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)
Spänner över flervärdiga relationer¶
När man spänner över en ManyToManyField
eller en omvänd ForeignKey
(t.ex. från Blog
till Entry
), väcker filtrering på flera attribut frågan om man ska kräva att varje attribut sammanfaller i samma relaterade objekt. Vi kan söka efter bloggar som har ett inlägg från 2008 med ”Lennon” i rubriken, eller så kan vi söka efter bloggar som bara har något inlägg från 2008 samt något nyare eller äldre inlägg med ”Lennon” i rubriken.
För att välja ut alla bloggar som innehåller minst ett inlägg från 2008 som har ”Lennon” i rubriken (samma inlägg uppfyller båda villkoren), skulle vi skriva:
Blog.objects.filter(entry__headline__contains="Lennon", entry__pub_date__year=2008)
Annars, för att utföra en mer tillåtande fråga som väljer alla bloggar med bara något inlägg med ”Lennon” i rubriken och något inlägg från 2008, skulle vi skriva:
Blog.objects.filter(entry__headline__contains="Lennon").filter(
entry__pub_date__year=2008
)
Anta att det bara finns en blogg som har både poster som innehåller ”Lennon” och poster från 2008, men att ingen av posterna från 2008 innehöll ”Lennon”. Den första frågan skulle inte returnera några bloggar, men den andra frågan skulle returnera den enda bloggen. (Detta beror på att de poster som väljs av det andra filtret kanske eller kanske inte är desamma som posterna i det första filtret. Vi filtrerar Blog
-objekten med varje filteruttalande, inte Entry
-objekten.) Kort sagt, om varje villkor behöver matcha samma relaterade objekt, bör varje innehålla ett enda filter()
-anrop.
Observera
Eftersom den andra (mer tillåtande) frågan kedjar flera filter utförs flera kopplingar till den primära modellen, vilket kan ge upphov till dubbletter.
>>> from datetime import date
>>> beatles = Blog.objects.create(name="Beatles Blog")
>>> pop = Blog.objects.create(name="Pop Music Blog")
>>> Entry.objects.create(
... blog=beatles,
... headline="New Lennon Biography",
... pub_date=date(2008, 6, 1),
... )
<Entry: New Lennon Biography>
>>> Entry.objects.create(
... blog=beatles,
... headline="New Lennon Biography in Paperback",
... pub_date=date(2009, 6, 1),
... )
<Entry: New Lennon Biography in Paperback>
>>> Entry.objects.create(
... blog=pop,
... headline="Best Albums of 2008",
... pub_date=date(2008, 12, 15),
... )
<Entry: Best Albums of 2008>
>>> Entry.objects.create(
... blog=pop,
... headline="Lennon Would Have Loved Hip Hop",
... pub_date=date(2020, 4, 1),
... )
<Entry: Lennon Would Have Loved Hip Hop>
>>> Blog.objects.filter(
... entry__headline__contains="Lennon",
... entry__pub_date__year=2008,
... )
<QuerySet [<Blog: Beatles Blog>]>
>>> Blog.objects.filter(
... entry__headline__contains="Lennon",
... ).filter(
... entry__pub_date__year=2008,
... )
<QuerySet [<Blog: Beatles Blog>, <Blog: Beatles Blog>, <Blog: Pop Music Blog]>
Observera
Beteendet hos filter()
för frågor som spänner över flervärdesrelationer, som beskrivs ovan, implementeras inte på motsvarande sätt för exclude()
. Istället kommer villkoren i ett enda exclude()
-anrop inte nödvändigtvis att hänvisa till samma objekt.
Till exempel skulle följande fråga utesluta bloggar som innehåller både poster med ”Lennon” i rubriken och poster som publicerades 2008:
Blog.objects.exclude(
entry__headline__contains="Lennon",
entry__pub_date__year=2008,
)
Till skillnad från beteendet när du använder filter()
kommer detta dock inte att begränsa bloggar baserat på poster som uppfyller båda villkoren. För att göra det, dvs. för att välja alla bloggar som inte innehåller poster som publicerats med ”Lennon” och som publicerades 2008, måste du göra två frågor:
Blog.objects.exclude(
entry__in=Entry.objects.filter(
headline__contains="Lennon",
pub_date__year=2008,
),
)
Filter kan referera till fält i modellen¶
I de exempel som vi har gett hittills har vi konstruerat filter som jämför värdet på ett modellfält med en konstant. Men hur gör man om man vill jämföra värdet på ett modellfält med ett annat fält i samma modell?
Django tillhandahåller F expressions
för att tillåta sådana jämförelser. Instanser av F()
fungerar som en referens till ett modellfält i en fråga. Dessa referenser kan sedan användas i frågefilter för att jämföra värdena för två olika fält på samma modellinstans.
Om du till exempel vill hitta en lista över alla bloggposter som har fått fler kommentarer än pingbacks, konstruerar vi ett F()
-objekt för att referera till pingback-antalet och använder det F()
-objektet i frågan:
>>> from django.db.models import F
>>> Entry.objects.filter(number_of_comments__gt=F("number_of_pingbacks"))
Django stöder användningen av addition, subtraktion, multiplikation, division, modulo och potensaritmetik med F()
-objekt, både med konstanter och med andra F()
-objekt. För att hitta alla bloggposter med mer än två gånger så många kommentarer som pingbacks ändrar vi frågan:
>>> Entry.objects.filter(number_of_comments__gt=F("number_of_pingbacks") * 2)
För att hitta alla poster där betyget för posten är mindre än summan av antalet pingbackar och antalet kommentarer, skulle vi ställa frågan:
>>> Entry.objects.filter(rating__lt=F("number_of_comments") + F("number_of_pingbacks"))
Du kan också använda dubbla understrykningar för att spänna över relationer i ett F()
-objekt. Ett F()
-objekt med en dubbel understrykning introducerar alla kopplingar som behövs för att komma åt det relaterade objektet. Om du t.ex. vill hämta alla poster där författarens namn är detsamma som bloggens namn kan du ställa följande fråga:
>>> Entry.objects.filter(authors__name=F("blog__name"))
För datum- och datum/tid-fält kan du lägga till eller dra ifrån ett timedelta
-objekt. Följande skulle returnera alla poster som ändrades mer än 3 dagar efter att de publicerades:
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F("pub_date") + timedelta(days=3))
Objekten F()
stöder bitvisa operationer med .bitand()
, .bitor()
, .bitxor()
, .bitrightshift()
och .bitleftshift()
. Till exempel:
>>> F("somefield").bitand(16)
Oracle
Oracle stöder inte bitvis XOR-operation.
Uttryck kan referera till transformationer¶
Django stöder användning av transformationer i uttryck.
Till exempel för att hitta alla Entry
-objekt som publicerades samma år som de senast ändrades:
>>> from django.db.models import F
>>> Entry.objects.filter(pub_date__year=F("mod_date__year"))
För att hitta det tidigaste året som en post publicerades kan vi ställa frågan:
>>> from django.db.models import Min
>>> Entry.objects.aggregate(first_published_year=Min("pub_date__year"))
Detta exempel visar värdet på den högst rankade posten och det totala antalet kommentarer på alla poster för varje år:
>>> from django.db.models import OuterRef, Subquery, Sum
>>> Entry.objects.values("pub_date__year").annotate(
... top_rating=Subquery(
... Entry.objects.filter(
... pub_date__year=OuterRef("pub_date__year"),
... )
... .order_by("-rating")
... .values("rating")[:1]
... ),
... total_comments=Sum("number_of_comments"),
... )
Genvägen pk
för uppslagning¶
För enkelhetens skull tillhandahåller Django en genväg för uppslagning pk
, som står för ”primary key”.
I exemplet med modellen Blog
är primärnyckeln fältet id
, så dessa tre påståenden är likvärdiga:
>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact
Användningen av pk
är inte begränsad till __exact
-frågor - alla frågetermer kan kombineras med pk
för att utföra en fråga på primärnyckeln i en modell:
# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1, 4, 7])
# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)
pk
-uppslag fungerar också över sammanfogningar. Till exempel är dessa tre uttalanden likvärdiga:
>>> Entry.objects.filter(blog__id__exact=3) # Explicit form
>>> Entry.objects.filter(blog__id=3) # __exact is implied
>>> Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
Utelämnande av procenttecken och understrykningstecken i LIKE
-satser¶
De fältuppslagningar som motsvarar LIKE
SQL-satser (iexact
, contains
, icontains
, startswith
, istartswith
, endswith
och iendswith
) kommer automatiskt att undkomma de två specialtecken som används i LIKE
-satser - procenttecknet och understrecket. (I en LIKE
-sats betyder procenttecknet ett jokertecken med flera tecken och understreckaren ett jokertecken med ett tecken)
Detta innebär att saker och ting ska fungera intuitivt, så att abstraktionen inte läcker. Om du t.ex. vill hämta alla poster som innehåller ett procenttecken använder du procenttecknet som vilket annat tecken som helst:
>>> Entry.objects.filter(headline__contains="%")
Django tar hand om citering åt dig; den resulterande SQL kommer att se ut ungefär så här:
SELECT ... WHERE headline LIKE '%\%%';
Detsamma gäller för understrykningstecken. Både procenttecken och understrykningar hanteras transparent åt dig.
Cachelagring och ”QuerySet¶
Varje :klass:`~django.db.models.query.QuerySet` innehåller en cache för att minimera databasåtkomst. Att förstå hur det fungerar gör att du kan skriva den mest effektiva koden.
I en nyskapad QuerySet
är cacheminnet tomt. Första gången en QuerySet
utvärderas - och därmed en databasfråga sker - sparar Django frågeresultaten i QuerySet
itereras över). Efterföljande utvärderingar av QuerySet
återanvänder de cachade resultaten.
Tänk på detta cachningsbeteende, eftersom det kan drabba dig om du inte använder dina QuerySet
på rätt sätt. Till exempel kommer följande att skapa två QuerySet
s, utvärdera dem och kasta bort dem:
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])
Det innebär att samma databasförfrågan kommer att köras två gånger, vilket effektivt fördubblar din databasbelastning. Det finns också en möjlighet att de två listorna kanske inte innehåller samma databasposter, eftersom en Entry
kan ha lagts till eller tagits bort i den delade sekunden mellan de två förfrågningarna.
För att undvika detta problem, spara QuerySet
och återanvänd den:
>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Reuse the cache from the evaluation.
När QuerySet
inte cachelagras¶
Querysets cachelagrar inte alltid sina resultat. När endast en del av frågeuppsättningen utvärderas kontrolleras cacheminnet, men om det inte fylls på så cachas inte de objekt som returneras av den efterföljande frågan. Specifikt innebär detta att :ref:begränsning av queryset <limiting-querysets>
med hjälp av en array-slice eller ett index inte kommer att fylla på cacheminnet.
Om man t.ex. upprepade gånger hämtar ett visst index i ett queryset-objekt kommer databasen att genomsökas varje gång:
>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again
Om hela frågeuppsättningen redan har utvärderats kommer dock cacheminnet att kontrolleras i stället:
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache
Här följer några exempel på andra åtgärder som leder till att hela frågeuppsättningen utvärderas och därmed fyller på cacheminnet:
>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)
Observera
Att bara skriva ut querysetet kommer inte att fylla på cachen. Detta beror på att anropet till __repr__()
endast returnerar en del av hela frågeuppsättningen.
Asynkrona förfrågningar¶
Om du skriver asynkrona vyer eller kod kan du inte använda ORM för frågor på det sätt som vi har beskrivit ovan, eftersom du inte kan anropa blockerande synkron kod från asynkron kod - det kommer att blockera händelseslingan (eller, mer troligt, Django kommer att märka och höja en SynchronousOnlyOperation
för att förhindra att det händer).
Lyckligtvis kan du göra många frågor med hjälp av Djangos asynkrona fråge-API. Varje metod som kan blockera - till exempel get()
eller delete()
- har en asynkron variant (aget()
eller adelete()
), och när du itererar över resultat kan du använda asynkron iteration (async for
) istället.
Fråga iteration¶
Standardmetoden för att iterera över en fråga - med for
- kommer att resultera i en blockerande databasfråga bakom kulisserna eftersom Django laddar resultaten vid iterationstiden. För att åtgärda detta kan du byta till async for
:
async for entry in Authors.objects.filter(name__startswith="A"):
...
Tänk på att du inte heller kan göra andra saker som kan iterera över queryset, till exempel att linda list()
runt den för att tvinga fram dess utvärdering (du kan använda async for
i en comprehension, om du vill det).
Eftersom QuerySet
-metoder som filter()
och exclude()
faktiskt inte kör frågan - de ställer in queryset för att köras när det itereras över - kan du använda dem fritt i asynkron kod. För en guide till vilka metoder som kan fortsätta att användas på det här sättet och vilka som har asynkrona versioner, läs nästa avsnitt.
QuerySet
och manager-metoder¶
Vissa metoder för hanterare och frågeuppsättningar - som get()
och first()
- tvingar fram exekvering av frågeuppsättningen och är blockerande. Vissa, som filter()
och exclude()
, tvingar inte fram exekvering och är därför säkra att köra från asynkron kod. Men hur ska du kunna se skillnaden?
Även om du kan rota runt och se om det finns en a
-prefixerad version av metoden (till exempel har vi aget()
men inte afilter()
), finns det ett mer logiskt sätt - leta upp vilken typ av metod det är i QuerySet-referensen.
Där hittar du metoderna för QuerySets grupperade i två avsnitt:
Metoder som returnerar nya querysets: Dessa är de icke-blockerande och har inga asynkrona versioner. Du är fri att använda dessa i alla situationer, men läs noterna om
defer()
ochonly()
innan du använder dem.Metoder som inte returnerar querysets: Dessa är de blockerande och har asynkrona versioner - det asynkrona namnet för varje metod anges i dokumentationen, men vårt standardmönster är att lägga till prefixet
a
.
Med hjälp av denna distinktion kan du räkna ut när du behöver använda asynkrona versioner och när du inte behöver göra det. Här är till exempel en giltig asynkron fråga:
user = await User.objects.filter(username=my_input).afirst()
filter()
returnerar en queryset, och därför går det bra att fortsätta kedja den i en asynkron miljö, medan first()
utvärderar och returnerar en modellinstans - därför ändrar vi till afirst()
och använder await
i början av hela uttrycket för att anropa det på ett asynkronvänligt sätt.
Observera
Om du glömmer att lägga in await
-delen kan du se fel som ”coroutine-objektet har inget attribut x” eller ”<coroutine …>” strängar i stället för dina modellinstanser. Om du någonsin ser dessa, saknar du en await
någonstans för att göra den coroutine till ett verkligt värde.
Transaktioner¶
Transaktioner stöds inte för närvarande med asynkrona frågor och uppdateringar. Du kommer att upptäcka att försök att använda en sådan ger upphov till SynchronousOnlyOperation
.
Om du vill använda en transaktion föreslår vi att du skriver din ORM-kod i en separat, synkron funktion och sedan anropar den med hjälp av sync_to_async
- se Asynkront stöd för mer information.
Fråga om JSONField
¶
Lookups implementation är annorlunda i JSONField
, främst på grund av förekomsten av nyckeltransformationer. För att demonstrera kommer vi att använda följande exempelmodell:
from django.db import models
class Dog(models.Model):
name = models.CharField(max_length=200)
data = models.JSONField(null=True)
def __str__(self):
return self.name
Lagring av och frågor om None
¶
Som med andra fält kommer lagring av None
som fältets värde att lagra det som SQL NULL
. Även om det inte rekommenderas är det möjligt att lagra JSON skalär null
istället för SQL NULL
genom att använda Value(None, JSONField())
.
Oavsett vilket av värdena som lagras, när det hämtas från databasen, är Python-representationen av JSON-skalaren null
densamma som SQL NULL
, dvs None
. Därför kan det vara svårt att skilja mellan dem.
Detta gäller endast för None
som fältets värde på högsta nivå. Om None
är inuti en list
eller dict
, kommer det alltid att tolkas som JSON null
.
Vid frågor kommer värdet None
alltid att tolkas som JSON null
. För att fråga efter SQL NULL
, använd isnull`
:
>>> Dog.objects.create(name="Max", data=None) # SQL NULL.
<Dog: Max>
>>> Dog.objects.create(name="Archie", data=Value(None, JSONField())) # JSON null.
<Dog: Archie>
>>> Dog.objects.filter(data=None)
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data=Value(None, JSONField()))
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data__isnull=True)
<QuerySet [<Dog: Max>]>
>>> Dog.objects.filter(data__isnull=False)
<QuerySet [<Dog: Archie>]>
Om du inte är säker på att du vill arbeta med SQL-värden av typen NULL
, bör du överväga att ange null=False
och tillhandahålla en lämplig standard för tomma värden, t.ex. default=dict
.
Observera
Lagring av JSON-skalaren null
bryter inte mot null=False
.
Nyckel-, index- och banomvandlingar¶
Om du vill göra en sökning baserad på en viss nyckel i en ordbok använder du den nyckeln som uppslagsnamn:
>>> Dog.objects.create(
... name="Rufus",
... data={
... "breed": "labrador",
... "owner": {
... "name": "Bob",
... "other_pets": [
... {
... "name": "Fishy",
... }
... ],
... },
... },
... )
<Dog: Rufus>
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": None})
<Dog: Meg>
>>> Dog.objects.filter(data__breed="collie")
<QuerySet [<Dog: Meg>]>
Flera nycklar kan kedjas ihop för att bilda en sökvägsuppslagning:
>>> Dog.objects.filter(data__owner__name="Bob")
<QuerySet [<Dog: Rufus>]>
Om nyckeln är ett heltal tolkas den som en indextransform i en array:
>>> Dog.objects.filter(data__owner__other_pets__0__name="Fishy")
<QuerySet [<Dog: Rufus>]>
Om den nyckel som du vill söka på krockar med namnet på en annan lookup, använd istället lookupen contains
.
För att söka efter nycklar som saknas, använd isnull
lookup:
>>> Dog.objects.create(name="Shep", data={"breed": "collie"})
<Dog: Shep>
>>> Dog.objects.filter(data__owner__isnull=True)
<QuerySet [<Dog: Shep>]>
Observera
Uppslagningsexemplen ovan använder implicit uppslagningen exact
. Nyckel-, index- och sökvägstransformationer kan också kedjas med: icontains
, endswith
, iendswith
, iexact
, regex
, iregex
, startswith
, istartswith
, lt
, lte
, gt
, and gte
, as well as with Inneslutning och nyckeluppslagningar.
KT()
uttryck¶
- class KT(lookup)¶
Representerar textvärdet för en nyckel-, index- eller sökvägstransformation av
JSONField
. Du kan använda den dubbla understrecksnoteringen ilookup
för att kedja nyckel- och indextransformationer för ordböcker.Till exempel:
>>> from django.db.models.fields.json import KT >>> Dog.objects.create( ... name="Shep", ... data={ ... "owner": {"name": "Bob"}, ... "breed": ["collie", "lhasa apso"], ... }, ... ) <Dog: Shep> >>> Dog.objects.annotate( ... first_breed=KT("data__breed__1"), owner_name=KT("data__owner__name") ... ).filter(first_breed__startswith="lhasa", owner_name="Bob") <QuerySet [<Dog: Shep>]>
Observera
På grund av det sätt på vilket sökvägsfrågor fungerar är exclude()
och filter()
inte garanterade att producera uttömmande uppsättningar. Om du vill inkludera objekt som inte har sökvägen, lägg till isnull
lookup.
Varning
Eftersom vilken sträng som helst kan vara en nyckel i ett JSON-objekt kommer alla andra uppslagningar än de som anges nedan att tolkas som en nyckeluppslagning. Inga felmeddelanden skickas ut. Var extra försiktig med skrivfel och kontrollera alltid att dina frågor fungerar som du tänkt dig.
MariaDB- och Oracle-användare
Om du använder order_by()
på nyckel-, index- eller sökvägstransformationer sorteras objekten med hjälp av strängrepresentationen av värdena. Detta beror på att MariaDB och Oracle Database inte tillhandahåller en funktion som konverterar JSON-värden till deras motsvarande SQL-värden.
Oracle-användare
Om du använder None
som uppslagsvärde i en exclude()
-fråga i Oracle Database returneras objekt som inte har null
som värde på den angivna sökvägen, inklusive objekt som inte har sökvägen. På andra databasbackends kommer frågan att returnera objekt som har sökvägen och värdet är inte null
.
PostgreSQL-användare
På PostgreSQL, om endast en nyckel eller ett index används, används SQL-operatören ->
. Om flera operatörer används används operatören `` #>``.
SQLite-användare
I SQLite kommer strängvärdena "true"
, "false"
och "null"
alltid att tolkas som True
, False
respektive JSON null
.
Inneslutning och nyckeluppslagningar¶
innehåller
¶
Uppslaget contains
åsidosätts för JSONField
. De objekt som returneras är de där den givna dict
av nyckel-värdepar alla finns i fältets toppnivå. Till exempel:
>>> Dog.objects.create(name="Rufus", data={"breed": "labrador", "owner": "Bob"})
<Dog: Rufus>
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
<Dog: Meg>
>>> Dog.objects.create(name="Fred", data={})
<Dog: Fred>
>>> Dog.objects.create(
... name="Merry", data={"breed": "pekingese", "tricks": ["fetch", "dance"]}
... )
>>> Dog.objects.filter(data__contains={"owner": "Bob"})
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>
>>> Dog.objects.filter(data__contains={"breed": "collie"})
<QuerySet [<Dog: Meg>]>
>>> Dog.objects.filter(data__contains={"tricks": ["dance"]})
<QuerySet [<Dog: Merry>]>
Oracle och SQLite
contains
stöds inte av Oracle och SQLite.
innehålls_av
¶
Detta är motsatsen till contains
- de objekt som returneras är de där nyckel-värdeparen i objektet är en delmängd av dem i det värde som skickas. Till exempel:
>>> Dog.objects.create(name="Rufus", data={"breed": "labrador", "owner": "Bob"})
<Dog: Rufus>
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
<Dog: Meg>
>>> Dog.objects.create(name="Fred", data={})
<Dog: Fred>
>>> Dog.objects.create(
... name="Merry", data={"breed": "pekingese", "tricks": ["fetch", "dance"]}
... )
>>> Dog.objects.filter(data__contained_by={"breed": "collie", "owner": "Bob"})
<QuerySet [<Dog: Meg>, <Dog: Fred>]>
>>> Dog.objects.filter(data__contained_by={"breed": "collie"})
<QuerySet [<Dog: Fred>]>
>>> Dog.objects.filter(
... data__contained_by={"breed": "pekingese", "tricks": ["dance", "fetch", "hug"]}
... )
<QuerySet [<Dog: Merry>, <Dog: Fred>]>
Oracle och SQLite
contained_by
stöds inte av Oracle och SQLite.
har_nyckel
¶
Returnerar objekt där den angivna nyckeln finns i den översta nivån av data. Till exempel:
>>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
<Dog: Rufus>
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
<Dog: Meg>
>>> Dog.objects.filter(data__has_key="owner")
<QuerySet [<Dog: Meg>]>
har_nycklar
¶
Returnerar objekt där alla de angivna nycklarna finns på den översta nivån i data. Till exempel:
>>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
<Dog: Rufus>
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
<Dog: Meg>
>>> Dog.objects.filter(data__has_keys=["breed", "owner"])
<QuerySet [<Dog: Meg>]>
har_några_nycklar
¶
Returnerar objekt där någon av de angivna nycklarna finns på den översta nivån i data. Till exempel:
>>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
<Dog: Rufus>
>>> Dog.objects.create(name="Meg", data={"owner": "Bob"})
<Dog: Meg>
>>> Dog.objects.filter(data__has_any_keys=["owner", "breed"])
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>
Komplexa uppslagningar med Q
-objekt¶
Frågor med nyckelordsargument – i filter()
, etc. – är ”AND” tillsammans. Om du behöver köra mer komplexa frågor (till exempel frågor med OR
-satser) kan du använda Q objects
.
Ett Q-objekt
(django.db.models.Q
) är ett objekt som används för att kapsla in en samling nyckelordsargument. Dessa nyckelordsargument specificeras som i ”Fältuppslagningar” ovan.
Till exempel kapslar detta Q
-objekt in en enda LIKE
-fråga:
from django.db.models import Q
Q(question__startswith="What")
Q
-objekt kan kombineras med hjälp av operatörerna &
, |
och ^
. När en operator används på två Q
-objekt, ger det ett nytt Q
-objekt.
Till exempel ger detta uttalande ett enda Q
-objekt som representerar ”OR” av två "question__startswith"
-frågor:
Q(question__startswith="Who") | Q(question__startswith="What")
Detta motsvarar följande SQL WHERE
klausul:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
Du kan komponera satser av godtycklig komplexitet genom att kombinera Q
-objekt med operatörerna &
, |
och ^
och använda parentetisk gruppering. Dessutom kan Q
-objekt negeras med operatorn ~
, vilket möjliggör kombinerade uppslagningar som kombinerar både en normal fråga och en negerad (NOT
) fråga:
Q(question__startswith="Who") | ~Q(pub_date__year=2005)
Varje uppslagsfunktion som tar nyckelordsargument (t.ex. filter()
, exclude()
, get()
) kan också skickas ett eller flera Q
-objekt som positionella (ej namngivna) argument. Om du ger flera Q
-objektargument till en uppslagsfunktion kommer argumenten att ”AND”-kopplas ihop. Till exempel:
Poll.objects.get(
Q(question__startswith="Who"),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
)
…översätts ungefär till SQL:
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
Uppslagsfunktioner kan blanda användningen av Q
-objekt och nyckelordsargument. Alla argument som anges i en lookup-funktion (oavsett om det är nyckelordsargument eller Q
-objekt) ”AND”-kopplas ihop. Om ett Q
-objekt anges måste det dock föregå definitionen av eventuella nyckelordsargument. Till exempel:
Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith="Who",
)
… skulle vara en giltig fråga, motsvarande det föregående exemplet; men:
# INVALID QUERY
Poll.objects.get(
question__startswith="Who",
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
)
… skulle inte vara giltigt.
Se även
OR lookups examples i Djangos enhetstester visar några möjliga användningsområden för Q
.
Jämförelse av objekt¶
För att jämföra två modellinstanser använder du Pythons standardjämförelseoperator, det dubbla likhetstecknet: ==
. Bakom kulisserna jämför det de primära nyckelvärdena för två modeller.
Med hjälp av exemplet Entry
ovan är följande två uttalanden likvärdiga:
>>> some_entry == other_entry
>>> some_entry.id == other_entry.id
Om en modells primärnyckel inte heter id
är det inget problem. Jämförelser kommer alltid att använda primärnyckeln, oavsett vad den heter. Om till exempel en modells primärnyckelfält heter name
är dessa två påståenden likvärdiga:
>>> some_obj == other_obj
>>> some_obj.name == other_obj.name
Radering av objekt¶
Metoden delete heter lägligt nog delete()
. Denna metod raderar omedelbart objektet och returnerar antalet objekt som raderats och en ordbok med antalet raderingar per objekttyp. Exempel på detta:
>>> e.delete()
(1, {'blog.Entry': 1})
Du kan också radera objekt i bulk. Varje QuerySet
har en delete()
-metod, som raderar alla medlemmar i den QuerySet
.
Detta raderar till exempel alla Entry
-objekt med pub_date
år 2005:
>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})
Tänk på att detta, när det är möjligt, kommer att utföras enbart i SQL, och därför kommer inte delete()
-metoderna för enskilda objektinstanser nödvändigtvis att anropas under processen. Om du har tillhandahållit en anpassad delete()
-metod för en modellklass och vill se till att den anropas, måste du ”manuellt” radera instanser av den modellen (t.ex, genom att iterera över en QuerySet
och anropa delete()
på varje objekt individuellt) i stället för att använda bulk delete()
-metoden för en QuerySet
.
När Django raderar ett objekt emulerar det som standard beteendet hos SQL-begränsningen ON DELETE CASCADE
- med andra ord kommer alla objekt som hade främmande nycklar som pekar på det objekt som ska raderas att raderas tillsammans med det. Till exempel:
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()
Detta kaskadbeteende kan anpassas via argumentet on_delete
till ForeignKey
.
Observera att delete()
är den enda QuerySet
-metoden som inte exponeras på en Manager
själv. Detta är en säkerhetsmekanism för att förhindra att du av misstag begär Entry.objects.delete()
och raderar alla posterna. Om du vill ta bort alla objekt måste du uttryckligen begära en komplett frågeuppsättning:
Entry.objects.all().delete()
Kopiera modellinstanser¶
Även om det inte finns någon inbyggd metod för att kopiera modellinstanser, är det möjligt att enkelt skapa en ny instans med alla fältvärden kopierade. I det enklaste fallet kan du ställa in pk
till None
och _state.adding
till True
. Med hjälp av vårt bloggexempel:
blog = Blog(name="My blog", tagline="Blogging is easy")
blog.save() # blog.pk == 1
blog.pk = None
blog._state.adding = True
blog.save() # blog.pk == 2
Saker och ting blir mer komplicerade om man använder sig av arv. Tänk dig en subklass av Blog
:
class ThemeBlog(Blog):
theme = models.CharField(max_length=200)
django_blog = ThemeBlog(name="Django", tagline="Django is easy", theme="python")
django_blog.save() # django_blog.pk == 3
På grund av hur arv fungerar måste du ställa in både pk
och id
till None
, och _state.adding
till True
:
django_blog.pk = None
django_blog.id = None
django_blog._state.adding = True
django_blog.save() # django_blog.pk == 4
Denna process kopierar inte relationer som inte är en del av modellens databastabell. Till exempel har Entry
en ManyToManyField
till Author
. När du har duplicerat en post måste du ställa in relationerna många-till-många för den nya posten:
entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry._state.adding = True
entry.save()
entry.authors.set(old_authors)
För ett OneToOneField
måste du duplicera det relaterade objektet och tilldela det till det nya objektets fält för att undvika att bryta mot den unika begränsningen för en-till-en. Om vi till exempel antar att entry
redan är duplicerat enligt ovan:
detail = EntryDetail.objects.all()[0]
detail.pk = None
detail._state.adding = True
detail.entry = entry
detail.save()
Uppdatering av flera objekt samtidigt¶
Ibland vill man sätta ett fält till ett visst värde för alla objekt i en QuerySet
. Det kan du göra med metoden update()
. Till exempel:
# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline="Everything is the same")
Du kan bara ställa in fält som inte är relationsfält och ForeignKey
-fält med den här metoden. För att uppdatera ett fält som inte är ett relationsfält anger du det nya värdet som en konstant. För att uppdatera ForeignKey
-fält anger du det nya värdet som den nya modellinstans du vill peka på. Till exempel:
>>> b = Blog.objects.get(pk=1)
# Change every Entry so that it belongs to this Blog.
>>> Entry.objects.update(blog=b)
Metoden update()
tillämpas omedelbart och returnerar antalet rader som matchas av frågan (vilket kanske inte är lika med antalet rader som uppdaterats om vissa rader redan har det nya värdet). Den enda begränsningen för QuerySet
som uppdateras är att den endast kan komma åt en databastabell: modellens huvudtabell. Du kan filtrera baserat på relaterade fält, men du kan bara uppdatera kolumner i modellens huvudtabell. Ett exempel:
>>> b = Blog.objects.get(pk=1)
# Update all the headlines belonging to this Blog.
>>> Entry.objects.filter(blog=b).update(headline="Everything is the same")
Tänk på att metoden update()
konverteras direkt till en SQL-sats. Det är en bulkoperation för direkta uppdateringar. Den kör inte några save()
-metoder på dina modeller, eller avger signalerna pre_save
eller post_save
(som är en konsekvens av att anropa save()
), eller hedrar fältalternativet auto_now
. Om du vill spara varje objekt i en QuerySet
och se till att metoden save()
anropas på varje instans, behöver du ingen speciell funktion för att hantera det. Loopa över dem och anropa save()
:
for item in my_queryset:
item.save()
Anrop till update kan också använda F expressions
för att uppdatera ett fält baserat på värdet av ett annat fält i modellen. Detta är särskilt användbart för att öka räknare baserat på deras aktuella värde. Till exempel för att öka antalet pingbackar för varje inlägg i bloggen:
>>> Entry.objects.update(number_of_pingbacks=F("number_of_pingbacks") + 1)
Men till skillnad från F()
-objekt i filter- och exkluderingsklausuler kan du inte införa joins när du använder F()
-objekt i en uppdatering - du kan bara referera till fält som är lokala för den modell som uppdateras. Om du försöker införa en join med ett F()
-objekt, kommer ett FieldError
att uppstå:
# This will raise a FieldError
>>> Entry.objects.update(headline=F("blog__name"))
Återgår till rå SQL¶
Om du behöver skriva en SQL-fråga som är för komplex för Djangos databasmappare att hantera, kan du falla tillbaka på att skriva SQL för hand. Django har ett par alternativ för att skriva råa SQL-frågor; se Utföra SQL-frågor i råformat.
Slutligen är det viktigt att notera att Djangos databaslager bara är ett gränssnitt till din databas. Du kan komma åt din databas via andra verktyg, programmeringsspråk eller databasramverk; det finns inget Django-specifikt med din databas.