Referens till modellinstans¶
Detta dokument beskriver detaljerna i API:et Model
. Det bygger på det material som presenteras i guiderna model och database query, så du vill förmodligen läsa och förstå dessa dokument innan du läser det här.
I den här referensen använder vi exempel på bloggmodeller som presenteras i guide för databasfrågor.
Skapa objekt¶
För att skapa en ny instans av en modell instansierar du den som vilken annan Python-klass som helst:
Nyckelordsargumenten är namnen på de fält som du har definierat i din modell. Observera att instansiering av en modell inte på något sätt rör din databas; för det måste du save()
.
Observera
Det kan vara frestande att anpassa modellen genom att åsidosätta metoden __init__
. Om du gör det, var dock noga med att inte ändra den anropande signaturen eftersom varje ändring kan förhindra att modellinstansen sparas. Dessutom kan hänvisning till modellfält inom __init__
potentiellt resultera i oändliga rekursionsfel under vissa omständigheter. I stället för att åsidosätta __init__
kan du prova att använda något av dessa tillvägagångssätt:
Lägg till en classmethod på modellklassen:
from django.db import models class Book(models.Model): title = models.CharField(max_length=100) @classmethod def create(cls, title): book = cls(title=title) # do something with the book return book book = Book.create("Pride and Prejudice")
Lägg till en metod på en anpassad manager (vanligtvis att föredra):
class BookManager(models.Manager): def create_book(self, title): book = self.create(title=title) # do something with the book return book class Book(models.Model): title = models.CharField(max_length=100) objects = BookManager() book = Book.objects.create_book("Pride and Prejudice")
Anpassning av modellbelastning¶
Metoden from_db()
kan användas för att anpassa skapandet av modellinstanser vid laddning från databasen.
Argumentet db
innehåller databasaliaset för den databas som modellen laddas från, field_names
innehåller namnen på alla inlästa fält och values
innehåller de inlästa värdena för varje fält i field_names
. field_names
är i samma ordning som values
. Om alla modellens fält finns med, kommer values
garanterat att finnas i den ordning som __init__()
förväntar sig dem. Det vill säga, instansen kan skapas av cls(*values)
. Om några fält är uppskjutna kommer de inte att visas i field_names
. Tilldela i så fall ett värde av django.db.models.DEFERRED
till vart och ett av de saknade fälten.
Förutom att skapa den nya modellen måste metoden from_db()
ställa in flaggorna adding
och db
i den nya instansens attribut _state
.
Nedan följer ett exempel som visar hur man registrerar de ursprungliga värdena för fält som laddas från databasen:
from django.db.models import DEFERRED
@classmethod
def from_db(cls, db, field_names, values):
# Default implementation of from_db() (subject to change and could
# be replaced with super()).
if len(values) != len(cls._meta.concrete_fields):
values = list(values)
values.reverse()
values = [
values.pop() if f.attname in field_names else DEFERRED
for f in cls._meta.concrete_fields
]
instance = cls(*values)
instance._state.adding = False
instance._state.db = db
# customization to store the original field values on the instance
instance._loaded_values = dict(
zip(field_names, (value for value in values if value is not DEFERRED))
)
return instance
def save(self, **kwargs):
# Check how the current values differ from ._loaded_values. For example,
# prevent changing the creator_id of the model. (This example doesn't
# support cases where 'creator_id' is deferred).
if not self._state.adding and (
self.creator_id != self._loaded_values["creator_id"]
):
raise ValueError("Updating the value of creator isn't allowed")
super().save(**kwargs)
Exemplet ovan visar en fullständig from_db()
-implementering för att klargöra hur det görs. I det här fallet skulle det vara möjligt att använda ett super()
-anrop i from_db()
-metoden.
Uppdatering av objekt från databas¶
Om du tar bort ett fält från en modellinstans laddas värdet från databasen när du öppnar det igen:
>>> obj = MyModel.objects.first()
>>> del obj.field
>>> obj.field # Loads the field from the database
- Model.arefresh_from_db(using=None, fields=None, from_queryset=None)¶
Asynkron version: arefresh_from_db()
Om du behöver ladda om en modells värden från databasen kan du använda metoden refresh_from_db()
. När denna metod anropas utan argument görs följande:
Alla icke uppskjutna fält i modellen uppdateras till de värden som för närvarande finns i databasen.
Alla cachelagrade relationer rensas från den omladdade instansen.
Endast modellens fält laddas om från databasen. Andra databasberoende värden som t.ex. annoteringar laddas inte om. Eventuella @cached_property
-attribut rensas inte heller.
Omladdningen sker från den databas som instansen laddades från, eller från standarddatabasen om instansen inte laddades från databasen. Argumentet using
kan användas för att tvinga fram den databas som används för omlastning.
Det är möjligt att tvinga fram en uppsättning fält som ska laddas genom att använda argumentet fields
.
Om du till exempel vill testa att ett anrop av update()
resulterar i den förväntade uppdateringen, kan du skriva ett test som liknar detta:
def test_update_result(self):
obj = MyModel.objects.create(val=1)
MyModel.objects.filter(pk=obj.pk).update(val=F("val") + 1)
# At this point obj.val is still 1, but the value in the database
# was updated to 2. The object's updated value needs to be reloaded
# from the database.
obj.refresh_from_db()
self.assertEqual(obj.val, 2)
Observera att när man använder uppskjutna fält sker laddningen av det uppskjutna fältets värde med den här metoden. Det är alltså möjligt att anpassa hur den uppskjutna laddningen sker. Exemplet nedan visar hur man kan ladda om instansens alla fält när ett uppskjutet fält laddas om:
class ExampleModel(models.Model):
def refresh_from_db(self, using=None, fields=None, **kwargs):
# fields contains the name of the deferred field to be
# loaded.
if fields is not None:
fields = set(fields)
deferred_fields = self.get_deferred_fields()
# If any deferred field is going to be loaded
if fields.intersection(deferred_fields):
# then load all of them
fields = fields.union(deferred_fields)
super().refresh_from_db(using, fields, **kwargs)
Argumentet from_queryset
gör det möjligt att använda en annan queryset än den som skapats från _base_manager
. Det ger dig mer kontroll över hur modellen laddas om. Till exempel:, när din modell använder soft deletion kan du göra refresh_from_db()
för att ta hänsyn till detta:
obj.refresh_from_db(from_queryset=MyModel.active_objects.all())
Du kan cacha relaterade objekt som annars skulle rensas bort från den omladdade instansen:
obj.refresh_from_db(from_queryset=MyModel.objects.select_related("related_field"))
Du kan låsa raden till slutet av transaktionen innan du laddar om en modells värden:
obj.refresh_from_db(from_queryset=MyModel.objects.select_for_update())
Argumentet from_queryset
lades till.
En hjälpmetod som returnerar en uppsättning som innehåller attributnamnen för alla de fält som för närvarande är uppskjutna för denna modell.
Validering av objekt¶
Det finns fyra steg i valideringen av en modell:
Validera modellfälten -
Model.clean_fields()
Validera modellen som helhet -
Model.clean()
Validera fältets unika egenskaper -
Model.validate_unique()
Validera begränsningarna -
Model.validate_constraints()
Alla fyra stegen utförs när du anropar en modells full_clean()
-metod.
När du använder en ModelForm
kommer anropet till is_valid()
att utföra dessa valideringssteg för alla fält som ingår i formuläret. Se ModelForm-dokumentationen för mer information. Du bör bara behöva anropa en modells full_clean()
-metod om du planerar att hantera valideringsfel själv, eller om du har uteslutit fält från ModelForm
som kräver validering.
Denna metod anropar Model.clean_fields()
, Model.clean()
, Model.validate_unique()
(om validate_unique
är True
), och Model.validate_constraints()
(om validate_constraints
är True
) i den ordningen och ger upphov till ett ValidationError
som har attributet message_dict
som innehåller fel från alla fyra stegen.
Det valfria argumentet exclude
kan användas för att tillhandahålla en uppsättning
av fältnamn som kan uteslutas från validering och rengöring. ModelForm
använder detta argument för att utesluta fält som inte finns i ditt formulär från att valideras eftersom eventuella fel som uppstår inte kan korrigeras av användaren.
Observera att full_clean()
inte kommer att anropas automatiskt när du anropar din modells save()
-metod. Du måste anropa den manuellt när du vill köra modellvalidering i ett steg för dina egna manuellt skapade modeller. Till exempel:
from django.core.exceptions import ValidationError
try:
article.full_clean()
except ValidationError as e:
# Do something based on the errors contained in e.message_dict.
# Display them to a user, or handle them programmatically.
pass
Det första steget som full_clean()
utför är att rengöra varje enskilt fält.
Denna metod validerar alla fält i din modell. Det valfria exclude
argumentet låter dig tillhandahålla en uppsättning
av fältnamn som ska uteslutas från validering. Det kommer att ge upphov till en ValidationError
om några fält misslyckas med valideringen.
Det andra steget som full_clean()
utför är att anropa Model.clean()
. Denna metod bör åsidosättas för att utföra anpassad validering på din modell.
Den här metoden bör användas för att tillhandahålla anpassad modellvalidering och för att ändra attribut på din modell om så önskas. Du kan t.ex. använda den för att automatiskt ange ett värde för ett fält eller för att göra en validering som kräver åtkomst till mer än ett enda fält:
import datetime
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
class Article(models.Model):
...
def clean(self):
# Don't allow draft entries to have a pub_date.
if self.status == "draft" and self.pub_date is not None:
raise ValidationError(_("Draft entries may not have a publication date."))
# Set the pub_date for published items if it hasn't been set already.
if self.status == "published" and self.pub_date is None:
self.pub_date = datetime.date.today()
Observera dock att i likhet med Model.full_clean()
anropas inte en modells clean()
-metod när du anropar din modells save()
-metod.
I exemplet ovan instantierades undantaget ValidationError
av Model.clean()
med en sträng, så det kommer att lagras i en speciell felordboksnyckel, NON_FIELD_ERRORS
. Den här nyckeln används för fel som är knutna till hela modellen istället för till ett specifikt fält:
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
try:
article.full_clean()
except ValidationError as e:
non_field_errors = e.message_dict[NON_FIELD_ERRORS]
För att tilldela undantag till ett specifikt fält, instansiera ValidationError
med en ordbok, där nycklarna är fältnamnen. Vi kan uppdatera det föregående exemplet för att tilldela felet till fältet pub_date
:
class Article(models.Model):
...
def clean(self):
# Don't allow draft entries to have a pub_date.
if self.status == "draft" and self.pub_date is not None:
raise ValidationError(
{"pub_date": _("Draft entries may not have a publication date.")}
)
...
Om du upptäcker fel i flera fält under Model.clean()
kan du också skicka en ordlista som mappar fältnamn till errors:
raise ValidationError(
{
"title": ValidationError(_("Missing title."), code="required"),
"pub_date": ValidationError(_("Invalid date."), code="invalid"),
}
)
Sedan kommer full_clean()
att kontrollera unika begränsningar för din modell.
Så här skapar du fältspecifika valideringsfel om dessa fält inte visas i ett ModelForm
Du kan inte skapa valideringsfel i Model.clean()
för fält som inte visas i ett modellformulär (ett formulär kan begränsa sina fält med hjälp av Meta.fields
eller Meta.exclude
). Om du gör det kommer ett ValueError
att uppstå eftersom valideringsfelet inte kommer att kunna associeras med det uteslutna fältet.
För att komma runt detta dilemma, åsidosätt istället Model.clean_fields()
eftersom den tar emot listan över fält som är undantagna från validering. Till exempel:
class Article(models.Model):
...
def clean_fields(self, exclude=None):
super().clean_fields(exclude=exclude)
if self.status == "draft" and self.pub_date is not None:
if exclude and "status" in exclude:
raise ValidationError(
_("Draft entries may not have a publication date.")
)
else:
raise ValidationError(
{
"status": _(
"Set status to draft if there is not a publication date."
),
}
)
Denna metod liknar clean_fields()
, men validerar unikhetsbegränsningar som definieras via Field.unique
, Field.unique_for_date
, Field.unique_for_month
, Field.unique_for_year
eller Meta.unique_together
på din modell istället för enskilda fältvärden. Det valfria argumentet exclude
låter dig tillhandahålla en uppsättning
av fältnamn som ska uteslutas från validering. Det kommer att ge upphov till en ValidationError
om några fält misslyckas med valideringen.
UniqueConstraint
s som definieras i Meta.constraints
valideras av Model.validate_constraints()
.
Observera att om du anger ett exclude
-argument till validate_unique()
, kommer alla unique_together
-begränsningar som involverar ett av de fält du angav inte att kontrolleras.
Slutligen kommer full_clean()
att kontrollera eventuella andra begränsningar för din modell.
Denna metod validerar alla begränsningar som definieras i Meta.constraints
. Det valfria exclude
-argumentet låter dig tillhandahålla en uppsättning
av fältnamn som ska uteslutas från validering. Det kommer att ge upphov till en ValidationError
om några begränsningar misslyckas med valideringen.
Spara objekt¶
För att spara ett objekt tillbaka till databasen, anropa save()
:
- Model.save(*, force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)[source]¶
- Model.asave(*, force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)¶
Asynkron version: asave()
Mer information om hur du använder argumenten force_insert
och force_update
finns i Tvinga fram en INSERT eller UPDATE. Detaljer om argumentet update_fields
finns i avsnittet Ange vilka fält som ska sparas.
Om du vill ha ett anpassat sparbeteende kan du åsidosätta denna metod save()
. Se Åsidosätta fördefinierade modellmetoder för mer information.
Processen för att spara modeller har också vissa finesser; se avsnitten nedan.
Ersatt sedan version 5.1: Stöd för positionella argument är borttaget.
Automatiskt ökande primärnycklar¶
Om en modell har en AutoField
- en automatiskt ökande primärnyckel - kommer det automatiskt ökande värdet att beräknas och sparas som ett attribut på ditt objekt första gången du anropar save()
:
>>> b2 = Blog(name="Cheddar Talk", tagline="Thoughts on cheese.")
>>> b2.id # Returns None, because b2 doesn't have an ID yet.
>>> b2.save()
>>> b2.id # Returns the ID of your new object.
Det finns inget sätt att säga vad värdet på ett ID kommer att vara innan du anropar save()
, eftersom det värdet beräknas av din databas, inte av Django.
För enkelhetens skull har varje modell en AutoField
med namnet id
som standard om du inte uttryckligen anger primary_key=True
på ett fält i din modell. Se dokumentationen för AutoField
för mer information.
Egenskapen ”pk¶
- Model.pk¶
Oavsett om du definierar ett primärnyckelfält själv, eller låter Django leverera ett åt dig, kommer varje modell att ha en egenskap som heter pk
. Den beter sig som ett vanligt attribut på modellen, men är faktiskt ett alias för vilket fält eller fält som utgör primärnyckeln för modellen. Du kan läsa och ställa in det här värdet, precis som du skulle göra för vilket annat attribut som helst, och det kommer att uppdatera rätt fält i modellen.
Stöd för att primärnyckeln kan bestå av flera fält har lagts till via CompositePrimaryKey
.
Explicit specificering av värden för auto-primary-key¶
Om en modell har en AutoField
men du vill definiera ett nytt objekts ID uttryckligen när du sparar, definierar du det uttryckligen innan du sparar, i stället för att förlita dig på den automatiska tilldelningen av ID:
>>> b3 = Blog(id=3, name="Cheddar Talk", tagline="Thoughts on cheese.")
>>> b3.id # Returns 3.
>>> b3.save()
>>> b3.id # Returns 3.
Om du tilldelar auto-primary-key-värden manuellt, se till att inte använda ett redan existerande primary-key-värde! Om du skapar ett nytt objekt med ett explicit primary-key-värde som redan finns i databasen, kommer Django att anta att du ändrar den befintliga posten snarare än att skapa en ny.
Med tanke på bloggexemplet ”Cheddar Talk” ovan skulle detta exempel åsidosätta den föregående posten i databasen:
b4 = Blog(id=3, name="Not Cheddar", tagline="Anything but cheese.")
b4.save() # Overrides the previous blog with ID=3!
Se Hur Django vet att UPDATE vs. INSERT, nedan, för anledningen till att detta händer.
Att uttryckligen ange värden för automatisk primärnyckel är mest användbart för masslagring av objekt, när du är säker på att du inte kommer att få kollisioner mellan primärnycklar.
Om du använder PostgreSQL kan sekvensen som är associerad med den primära nyckeln behöva uppdateras; se Manuellt specificerade värden för primärnycklar med automatisk inkrementering.
Vad händer när du sparar?¶
När du sparar ett objekt utför Django följande steg:
**Sänd en signal före sparande
pre_save
-signalen skickas, vilket gör att alla funktioner som lyssnar på den signalen kan göra något.**Varje fälts metod
pre_save()
anropas för att utföra alla automatiserade datamodifieringar som behövs. Till exempel: åsidosätter datum-/tidsfältenpre_save()
för att implementeraauto_now_add
ochauto_now
.**Varje fälts
get_db_prep_save()
-metod ombeds att tillhandahålla sitt aktuella värde i en datatyp som kan skrivas till databasen.De flesta fält kräver ingen dataförberedelse. Enkla datatyper, t.ex. heltal och strängar, är ”skrivklara” som Python-objekt. Mer komplexa datatyper kräver dock ofta viss modifiering.
Till exempel: använder
DateField
-fält ett Python-objekt meddatetime
för att lagra data. Databaser lagrar intedatetime
-objekt, så fältvärdet måste konverteras till en ISO-kompatibel datumsträng för att infogas i databasen.**De förbehandlade, förberedda uppgifterna sammanställs till en SQL-sats för inmatning i databasen.
**Skicka en post-save-signal
post_save
-signalen skickas, vilket gör att alla funktioner som lyssnar på den signalen kan göra något.
Hur Django vet att UPDATE vs. INSERT¶
Du kanske har märkt att Djangos databasobjekt använder samma save()
-metod för att skapa och ändra objekt. Django abstraherar behovet av att använda INSERT
eller UPDATE
SQL-satser. När du anropar save()
och objektets primära nyckelattribut inte definierar en default
eller db_default
, följer Django denna algoritm:
Om objektets primära nyckelattribut är inställt på något annat än
None
, utför Django enUPDATE
.Om objektets primärnyckelattribut inte är inställt eller om
UPDATE
inte uppdaterade någonting (t.ex. om primärnyckeln är inställd på ett värde som inte finns i databasen), utför Django enINSERT
.
Om objektets primära nyckelattribut definierar en default
eller db_default
så utför Django en UPDATE
om det är en befintlig modellinstans och den primära nyckeln är inställd på ett värde som finns i databasen. Annars utför Django en INSERT
.
Det enda problemet här är att du bör vara försiktig så att du inte uttryckligen anger ett primärnyckelvärde när du sparar nya objekt, om du inte kan garantera att primärnyckelvärdet är oanvänt. För mer information om denna nyans, se Explicit specificering av auto-primary-key-värden ovan och Tvinga fram en INSERT eller UPDATE nedan.
I Django 1.5 och tidigare gjorde Django en SELECT
när primärnyckelattributet var inställt. Om SELECT
hittade en rad, gjorde Django en UPDATE
, annars gjorde den en INSERT
. Den gamla algoritmen resulterar i ytterligare en fråga i fallet UPDATE
. Det finns några sällsynta fall där databasen inte rapporterar att en rad uppdaterades även om databasen innehåller en rad för objektets primära nyckelvärde. Ett exempel är PostgreSQL ON UPDATE
trigger som returnerar NULL
. I sådana fall är det möjligt att återgå till den gamla algoritmen genom att ställa in alternativet select_on_save
till True
.
Tvinga fram en INSERT eller UPDATE¶
Under vissa sällsynta omständigheter är det nödvändigt att kunna tvinga save()
-metoden att utföra en SQL INSERT
och inte falla tillbaka till att göra en UPDATE
. Eller vice versa: uppdatera, om möjligt, men inte infoga en ny rad. I dessa fall kan du skicka parametrarna force_insert=True
eller force_update=True
till metoden save()
. Att skicka båda parametrarna är ett fel: du kan inte både infoga och uppdatera på samma gång!
När du använder :ref:multi-table inheritance <multi-table-inheritance>
är det också möjligt att tillhandahålla en tupel av överordnade klasser till force_insert
för att tvinga fram INSERT
-satser för varje bas. Till exempel:
Restaurant(pk=1, name="Bob's Cafe").save(force_insert=(Place,))
Restaurant(pk=1, name="Bob's Cafe", rating=4).save(force_insert=(Place, Rating))
Du kan skicka force_insert=(models.Model,)
för att tvinga fram en INSERT
-sats för alla föräldrar. Som standard tvingar force_insert=True
bara fram en ny rad för den aktuella modellen.
Det bör vara mycket sällsynt att du behöver använda dessa parametrar. Django kommer nästan alltid att göra det rätta och att försöka åsidosätta det kommer att leda till fel som är svåra att spåra. Denna funktion är endast avsedd för avancerad användning.
Om du använder update_fields
kommer du att tvinga fram en uppdatering på samma sätt som force_update
.
Uppdatering av attribut baserat på befintliga fält¶
Ibland behöver du utföra en enkel aritmetisk uppgift på ett fält, till exempel att öka eller minska det aktuella värdet. Ett sätt att uppnå detta är att göra aritmetiken i Python som:
>>> product = Product.objects.get(name="Venezuelan Beaver Cheese")
>>> product.number_sold += 1
>>> product.save()
Om det gamla värdet för number_sold
som hämtades från databasen var 10, kommer värdet 11 att skrivas tillbaka till databasen.
Processen kan göras robust, undvika ett tävlingstillstånd, samt något snabbare genom att uttrycka uppdateringen relativt det ursprungliga fältvärdet, snarare än som en explicit tilldelning av ett nytt värde. Django tillhandahåller F expressions
för att utföra denna typ av relativ uppdatering. Med hjälp av F expressions
uttrycks det föregående exemplet som:
>>> from django.db.models import F
>>> product = Product.objects.get(name="Venezuelan Beaver Cheese")
>>> product.number_sold = F("number_sold") + 1
>>> product.save()
För mer information, se dokumentationen om F uttryck
och deras användning i uppdateringsfrågor.
Ange vilka fält som ska sparas¶
Om save()
får en lista med fältnamn i nyckelordsargumentet update_fields
, kommer endast de fält som anges i listan att uppdateras. Detta kan vara önskvärt om du bara vill uppdatera ett eller ett fåtal fält på ett objekt. Det finns en liten prestandafördel med att förhindra att alla modellfält uppdateras i databasen. Till exempel:
product.name = "Name changed again"
product.save(update_fields=["name"])
Argumentet update_fields
kan vara en iterabel som innehåller strängar. En tom update_fields
iterabel hoppar över sparandet. Ett värde av None
kommer att utföra en uppdatering på alla fält.
Om du anger update_fields
kommer du att tvinga fram en uppdatering.
När du sparar en modell som hämtats genom uppskjuten modellbelastning (only()
eller defer()
) kommer endast de fält som laddats från DB att uppdateras. I själva verket finns det en automatisk update_fields
i det här fallet. Om du tilldelar eller ändrar något uppskjutet fältvärde läggs fältet till bland de uppdaterade fälten.
Field.pre_save()
och update_fields
Om update_fields
skickas in anropas endast pre_save()
-metoderna i update_fields
. Detta innebär till exempel att datum-/tidsfält med auto_now=True
inte kommer att uppdateras om de inte ingår i update_fields
.
Radering av objekt¶
- Model.adelete(using=DEFAULT_DB_ALIAS, keep_parents=False)¶
Asynkron version: adelete()
Utfärdar en SQL DELETE
för objektet. Detta raderar endast objektet i databasen; Python-instansen kommer fortfarande att existera och kommer fortfarande att ha data i sina fält, med undantag för primärnyckeln som är inställd på None
. Denna metod returnerar antalet objekt som raderats och en ordbok med antalet raderingar per objekttyp.
Mer information, bland annat om hur du tar bort objekt i bulk, finns i Radering av objekt.
Om du vill ha ett anpassat borttagningsbeteende kan du åsidosätta metoden delete()
. Se Åsidosätta fördefinierade modellmetoder för mer information.
Ibland med :ref:multi-table inheritance <multi-table-inheritance>
kan du vilja radera endast en underordnad modells data. Om du anger keep_parents=True
behålls föräldramodellens data.
Betning av föremål¶
När du pickle
en modell, är dess nuvarande tillstånd pickled. När du plockar upp den kommer den att innehålla modellinstansen vid den tidpunkt då den plockades upp, snarare än de data som för närvarande finns i databasen.
Andra metoder för modellinstans¶
Några objektmetoder har speciella syften.
__str__()`
¶
Metoden __str__()
anropas när du anropar str()
på ett objekt. Django använder str(obj)
på ett antal ställen. Framför allt för att visa ett objekt på Django-adminsidan och som det värde som infogas i en mall när den visar ett objekt. Därför bör du alltid returnera en trevlig, mänskligt läsbar representation av modellen från metoden __str__()
.
Till exempel:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
def __str__(self):
return f"{self.first_name} {self.last_name}"
__eq__()`
¶
Metoden equality är definierad så att instanser med samma primära nyckelvärde och samma konkreta klass anses vara lika, förutom att instanser med ett primärt nyckelvärde på None
inte är lika med något annat än sig själva. För proxymodeller definieras den konkreta klassen som modellens första icke-proxyförälder; för alla andra modeller är det helt enkelt modellens klass.
Till exempel:
from django.db import models
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
class MyProxyModel(MyModel):
class Meta:
proxy = True
class MultitableInherited(MyModel):
pass
# Primary keys compared
MyModel(id=1) == MyModel(id=1)
MyModel(id=1) != MyModel(id=2)
# Primary keys are None
MyModel(id=None) != MyModel(id=None)
# Same instance
instance = MyModel(id=None)
instance == instance
# Proxy model
MyModel(id=1) == MyProxyModel(id=1)
# Multi-table inheritance
MyModel(id=1) != MultitableInherited(id=1)
__hash__()`
¶
Metoden __hash__()
baseras på instansens primärnyckelvärde. Det är i praktiken hash(obj.pk)
. Om instansen inte har ett primärt nyckelvärde kommer ett TypeError
att uppstå (annars skulle __hash__()
-metoden returnera olika värden före och efter att instansen sparas, men att ändra __hash__()
-värdet för en instans är förbjudet i Python.
get_absolute_url()
¶
- Model.get_absolute_url()¶
Definiera en get_absolute_url()
-metod för att tala om för Django hur man beräknar den kanoniska URL:en för ett objekt. För anropare bör denna metod se ut att returnera en sträng som kan användas för att hänvisa till objektet via HTTP.
Till exempel:
def get_absolute_url(self):
return "/people/%i/" % self.id
Även om den här koden är korrekt och enkel, är det kanske inte det mest portabla sättet att skriva den här typen av metod. Funktionen reverse()
är vanligtvis det bästa tillvägagångssättet.
Till exempel:
def get_absolute_url(self):
from django.urls import reverse
return reverse("people-detail", kwargs={"pk": self.pk})
En plats där Django använder get_absolute_url()
är i admin-appen. Om ett objekt definierar den här metoden kommer objektredigeringssidan att ha en ”Visa på webbplatsen”-länk som hoppar direkt till objektets offentliga vy, som ges av get_absolute_url()
.
På samma sätt använder ett par andra delar av Django, till exempel syndication feed framework, get_absolute_url()
när det är definierat. Om det är vettigt för din modells instanser att var och en har en unik URL, bör du definiera get_absolute_url()
.
Varning
Du bör undvika att bygga URL:en från obekräftad användarinmatning, för att minska risken för förgiftning av länkar eller omdirigeringar:
def get_absolute_url(self):
return "/%s/" % self.name
Om self.name
är '/example.com'
returneras '//example.com/'
, vilket i sin tur är en giltig schemarelativ URL men inte den förväntade '/%2Fexample.com/'
.
Det är god praxis att använda get_absolute_url()
i mallar, istället för att hårdkoda objektens URL:er. Till exempel: är den här mallkoden dålig:
<!-- BAD template code. Avoid! -->
<a href="/people/{{ object.id }}/">{{ object.name }}</a>
Den här mallkoden är mycket bättre:
<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>
Logiken här är att om du ändrar URL-strukturen för dina objekt, även för något litet som att korrigera ett stavfel, vill du inte behöva spåra varje plats där URL kan skapas. Specificera den en gång, i get_absolute_url()
och låt all din övriga kod anropa den enda platsen.
Observera
Den sträng du returnerar från get_absolute_url()
måste endast innehålla ASCII-tecken (krävs enligt URI-specifikationen, RFC 3986 Section 2) och vara URL-kodad, om nödvändigt.
Kod och mallar som anropar get_absolute_url()
bör kunna använda resultatet direkt utan någon ytterligare bearbetning. Du kanske vill använda funktionen django.utils.encoding.iri_to_uri()
för att hjälpa till med detta om du använder strängar som innehåller tecken utanför ASCII-området.
Extra instansmetoder¶
Förutom save()
, delete()
, kan ett modellobjekt ha några av följande metoder:
- Model.get_FOO_display()¶
För varje fält som har choices
inställt, kommer objektet att ha en get_FOO_display()
metod, där FOO
är namnet på fältet. Denna metod returnerar det ”mänskligt läsbara” värdet för fältet.
Till exempel:
from django.db import models
class Person(models.Model):
SHIRT_SIZES = {
"S": "Small",
"M": "Medium",
"L": "Large",
}
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
- Model.get_next_by_FOO(**kwargs)¶
- Model.get_previous_by_FOO(**kwargs)¶
För varje DateField
och DateTimeField
som inte har null=True
, kommer objektet att ha get_next_by_FOO()
och get_previous_by_FOO()
metoder, där FOO
är namnet på fältet. Detta returnerar nästa och föregående objekt med avseende på datumfältet och ger upphov till ett DoesNotExist
-undantag när det är lämpligt.
Båda dessa metoder kommer att utföra sina frågor med hjälp av standardhanteraren för modellen. Om du behöver emulera filtrering som används av en anpassad hanterare, eller vill utföra enstaka anpassad filtrering, accepterar båda metoderna också valfria nyckelordsargument, som bör vara i det format som beskrivs i Field lookups.
Observera att om datumvärdena är identiska kommer dessa metoder att använda primärnyckeln som utslagsgivande. Detta garanterar att inga poster hoppas över eller dupliceras. Det innebär också att du inte kan använda dessa metoder på osparade objekt.
Åsidosättande av extra instansmetoder
I de flesta fall bör åsidosättande eller arv av get_FOO_display()
, get_next_by_FOO()
och get_previous_by_FOO()
fungera som förväntat. Eftersom de läggs till av metaklassen är det dock inte praktiskt möjligt att ta hänsyn till alla möjliga arvsstrukturer. I mer komplexa fall bör du åsidosätta Field.contribute_to_class()
för att ställa in de metoder du behöver.
Övriga attribut¶
_stat
¶
- Model._state¶
Attributet
_state
hänvisar till ettModelState
-objekt som spårar modellinstansens livscykel.Objektet
ModelState
har två attribut: ”Adding”, en flagga som är ”True” om modellen ännu inte har sparats i databasen, och ”db”, en sträng som hänvisar till det databasalias som instansen laddades från eller sparades till.Nyinstallerade instanser har
adding=True
ochdb=None
, eftersom de ännu inte har sparats. Instanser som hämtas från enQuerySet
kommer att haadding=False
ochdb
satt till aliaset för den associerade databasen.
_is_pk_set()
¶
Metoden _is_pk_set()
returnerar om modellinstansens pk
är inställd. Den abstraherar modellens definition av primärnyckeln och säkerställer ett konsekvent beteende oavsett den specifika konfigurationen av pk
.