Modeller¶
En modell är den enda, definitiva källan till information om dina data. Den innehåller de viktigaste fälten och beteendena för de data som du lagrar. I allmänhet mappar varje modell till en enda databastabell.
Grunderna:
Varje modell är en Python-klass som är underklass till
django.db.models.Model
.Varje attribut i modellen representerar ett fält i databasen.
Med allt detta ger Django dig ett automatiskt genererat API för databasåtkomst; se Göra förfrågningar.
Snabbt exempel¶
Denna exempelmodell definierar en Person
, som har ett förnamn
och efternamn
:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
förnamn
och efternamn
är fält i modellen. Varje fält anges som ett klassattribut och varje attribut mappar till en databaskolumn.
Modellen Person
ovan skulle skapa en databastabell så här:
CREATE TABLE myapp_person (
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
Några tekniska anmärkningar:
Tabellens namn,
myapp_person
, härleds automatiskt från vissa modellmetadata men kan åsidosättas. Se Tabellnamn för mer information.Ett
id
-fält läggs till automatiskt, men detta beteende kan åsidosättas. Se Automatiska primärnyckelfält.SQL-filen
CREATE TABLE
i det här exemplet är formaterad med PostgreSQL-syntax, men det är värt att notera att Django använder SQL skräddarsydd för databasens backend som anges i din settings-fil.
Använda modeller¶
När du har definierat dina modeller måste du tala om för Django att du ska använda dessa modeller. Gör detta genom att redigera din inställningsfil och ändra inställningen INSTALLED_APPS
för att lägga till namnet på den modul som innehåller dina models.py
.
Om modellerna för din applikation till exempel finns i modulen myapp.models
(den paketstruktur som skapas för en applikation av skriptet manage.py startapp
), bör INSTALLED_APPS
delvis lyda:
INSTALLED_APPS = [
# ...
"myapp",
# ...
]
När du lägger till nya appar i INSTALLED_APPS
ska du se till att köra manage.py migrate
, och eventuellt göra migreringar för dem först med manage.py makemigrations
.
Fält¶
Den viktigaste delen av en modell - och den enda obligatoriska delen av en modell - är listan över databasfält som den definierar. Fält specificeras av klassattribut. Var försiktig så att du inte väljer fältnamn som står i konflikt med models API som clean
, save
eller delete
.
Exempel:
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
Fälttyper¶
Varje fält i din modell bör vara en instans av den lämpliga Field
-klassen. Django använder fältklasstyperna för att bestämma några saker:
Kolumntypen, som talar om för databasen vilken typ av data som ska lagras (t.ex.
INTEGER
,VARCHAR
,TEXT
).Standard HTML widget som ska användas vid rendering av ett formulärfält (t.ex.
<input type="text">
,<select>
).De minimala valideringskraven, som används i Djangos admin och i automatiskt genererade formulär.
Django levereras med dussintals inbyggda fälttyper; du kan hitta den fullständiga listan i :ref:``model field reference <model-field-types>`. Du kan enkelt skriva dina egna fält om Djangos inbyggda inte räcker till; se Så här skapar du anpassade modellfält.
Alternativ för fält¶
Varje fält tar en viss uppsättning fältspecifika argument (dokumenterade i model field reference). Till exempel kräver CharField
(och dess underklasser) ett max_length
-argument som anger storleken på databasfältet VARCHAR
som används för att lagra data.
Det finns också en uppsättning gemensamma argument som är tillgängliga för alla fälttyper. Alla är valfria. De förklaras fullständigt i reference, men här är en snabb sammanfattning av de mest använda:
null
Om
True
, kommer Django att lagra tomma värden somNULL
i databasen. Standard ärFalse
.blank
Om
True
, får fältet vara tomt. Standard ärFalse
.Observera att detta inte är samma sak som
null
.null
är rent databasrelaterat, medanblank
är valideringsrelaterat. Om ett fält harblank=True
, tillåter formulärvalideringen att ett tomt värde anges. Om ett fält harblank=False
kommer fältet att vara obligatoriskt.choices
En sekvens av 2-värdestuplar, en mappning, en uppräkningstyp, eller en callable (som inte förväntar sig några argument och returnerar något av de tidigare formaten), att använda som val för detta fält. Om detta anges kommer standardformulärwidgeten att vara en valruta istället för standardtextfältet och begränsa valen till de angivna alternativen.
En lista med valmöjligheter ser ut så här:
YEAR_IN_SCHOOL_CHOICES = [ ("FR", "Freshman"), ("SO", "Sophomore"), ("JR", "Junior"), ("SR", "Senior"), ("GR", "Graduate"), ]
Observera
En ny migration skapas varje gång ordningen på
valen
ändras.Det första elementet i varje tupel är det värde som kommer att lagras i databasen. Det andra elementet visas av fältets formulärwidget.
Givet en modellinstans kan visningsvärdet för ett fält med
choices
nås med metodenget_FOO_display()
. 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=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L") >>> p.save() >>> p.shirt_size 'L' >>> p.get_shirt_size_display() 'Large'
Du kan också använda uppräkningsklasser för att definiera
val
på ett kortfattat sätt:from django.db import models class Runner(models.Model): MedalType = models.TextChoices("MedalType", "GOLD SILVER BRONZE") name = models.CharField(max_length=60) medal = models.CharField(blank=True, choices=MedalType, max_length=10)
Ytterligare exempel finns i modellfältets referens.
default
Standardvärdet för fältet. Detta kan vara ett värde eller ett anropsbart objekt. Om det är anropsbart kommer det att anropas varje gång ett nytt objekt skapas.
db_default
Det databasberäknade standardvärdet för fältet. Detta kan vara ett bokstavligt värde eller en databasfunktion.
Om både
db_default
ochField.default`
är inställda kommerdefault
att ha företräde när instanser skapas i Python-kod.db_default
kommer fortfarande att vara inställt på databasnivå och kommer att användas när rader infogas utanför ORM eller när ett nytt fält läggs till i en migrering.help_text
Extra ”hjälp”-text som ska visas med formulärwidgeten. Det är användbart för dokumentation även om ditt fält inte används i ett formulär.
primary_key
Om
True
, är detta fält primärnyckel för modellen.Om du inte anger
primary_key=True
för några fält i din modell, kommer Django automatiskt att lägga till ett fält för att hålla primärnyckeln, så du behöver inte angeprimary_key=True
på något av dina fält om du inte vill åsidosätta standardbeteendet för primärnyckeln. För mer information, se Automatiska primärnyckelfält.Fältet primary key är skrivskyddat. Om du ändrar värdet på primärnyckeln i ett befintligt objekt och sedan sparar det, skapas ett nytt objekt vid sidan av det gamla. Till exempel:
from django.db import models class Fruit(models.Model): name = models.CharField(max_length=100, primary_key=True)
>>> fruit = Fruit.objects.create(name="Apple") >>> fruit.name = "Pear" >>> fruit.save() >>> Fruit.objects.values_list("name", flat=True) <QuerySet ['Apple', 'Pear']>
unique
Om
True
måste detta fält vara unikt i hela tabellen.
Återigen, detta är bara korta beskrivningar av de vanligaste fältalternativen. Fullständig information finns i :ref:``referens för fältalternativ för vanliga modeller <common-model-field-options>`.
Automatiska primärnyckelfält¶
Som standard ger Django varje modell en automatiskt ökande primärnyckel med den typ som anges per app i AppConfig.default_auto_field
eller globalt i inställningen DEFAULT_AUTO_FIELD
. Till exempel:
id = models.BigAutoField(primary_key=True)
Om du vill ange en anpassad primärnyckel, ange primary_key=True
på ett av dina fält. Om Django ser att du uttryckligen har angett Field.primary_key
, kommer den inte att lägga till den automatiska kolumnen id
.
Varje modell kräver att exakt ett fält har primary_key=True
(antingen explicit deklarerat eller automatiskt tillagt).
Utförliga fältnamn¶
Varje fälttyp, med undantag för ForeignKey
, ManyToManyField
och OneToOneField
, tar ett valfritt första positionellt argument - ett verbose-namn. Om verbose-namnet inte anges skapar Django det automatiskt med hjälp av fältets attributnamn och konverterar understrykningar till mellanslag.
I det här exemplet är det fullständiga namnet "personens förnamn"
:
first_name = models.CharField("person's first name", max_length=30)
I det här exemplet är det fullständiga namnet "förnamn"
:
first_name = models.CharField(max_length=30)
ForeignKey
, ManyToManyField
och OneToOneField
kräver att det första argumentet är en modellklass, så använd verbose_name
nyckelordsargument:
poll = models.ForeignKey(
Poll,
on_delete=models.CASCADE,
verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
verbose_name="related place",
)
Konventionen är att inte skriva den första bokstaven i verbose_name
med stor bokstav. Django kommer automatiskt att skriva första bokstaven där det behövs.
Relationer¶
Det är uppenbart att styrkan i relationsdatabaser ligger i att relatera tabeller till varandra. Django erbjuder sätt att definiera de tre vanligaste typerna av databasrelationer: många-till-en, många-till-många och en-till-en.
Många-till-en-relationer¶
För att definiera en många-till-en-relation använder du django.db.models.ForeignKey
. Du använder den precis som alla andra Field
-typer: genom att inkludera den som ett klassattribut i din modell.
ForeignKey
kräver ett positionellt argument: den klass som modellen är relaterad till.
Om t.ex. en bilmodell har en tillverkare - dvs. en tillverkare tillverkar flera bilar men varje bil har bara en tillverkare - används följande definitioner:
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
# ...
Du kan också skapa rekursiva relationer (ett objekt med en många-till-en-relation till sig själv) och relationer till modeller som ännu inte definierats; se modellfältreferensen för mer information.
Det föreslås, men krävs inte, att namnet på ett ForeignKey
-fält (tillverkare
i exemplet ovan) är namnet på modellen, med gemener. Du kan kalla fältet vad du vill. Till exempel:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
)
# ...
Se även
ForeignKey
-fält accepterar ett antal extra argument som förklaras i :ref:``modellfältets referens <foreign-key-arguments>`. Dessa alternativ hjälper till att definiera hur relationen ska fungera; alla är valfria.
Mer information om hur du får tillgång till bakåtriktade objekt finns i Följa relationer bakåt exempel.
Exempel på kod finns i Exempel på relationsmodell för man-till-man.
Många-till-många-relationer¶
För att definiera en relation mellan många och många använder du ManyToManyField
. Du använder den precis som alla andra Field
-typer: genom att inkludera den som ett klassattribut i din modell.
ManyToManyField
kräver ett positionellt argument: den klass som modellen är relaterad till.
Till exempel, om en Pizza
har flera Topping
-objekt - det vill säga en Topping
kan vara på flera pizzor och varje Pizza
har flera toppings - så här skulle du representera det:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
Precis som med ForeignKey
kan du också skapa recursiva relationer (ett objekt med en många-till-många-relation till sig själv) och relationer till modeller som ännu inte definierats.
Det föreslås, men krävs inte, att namnet på en ManyToManyField
(toppings
i exemplet ovan) är en plural som beskriver uppsättningen relaterade modellobjekt.
Det spelar ingen roll vilken modell som har ManyToManyField
, men du bör bara lägga till den i en av modellerna - inte i båda.
Generellt sett bör ManyToManyField
-instanser placeras i det objekt som ska redigeras i ett formulär. I exemplet ovan är toppings
i Pizza
(snarare än Topping
som har en pizzas
ManyToManyField
) eftersom det är mer naturligt att tänka på att en pizza har toppings än att en topping finns på flera pizzor. På det sätt som det är inställt ovan skulle formuläret Pizza
låta användarna välja toppings.
Se även
Ett fullständigt exempel finns i Many-to-many relationship model example.
ManyToManyField
-fält accepterar också ett antal extra argument som förklaras i :ref:``modellfältets referens <manytomany-arguments>`. Dessa alternativ hjälper till att definiera hur relationen ska fungera; alla är valfria.
Extra fält i många-till-många-relationer¶
När du bara har att göra med många-till-många-relationer som att blanda och matcha pizzor och pålägg, är en standard ManyToManyField
allt du behöver. Ibland kan du dock behöva associera data med relationen mellan två modeller.
Tänk till exempel på en applikation som spårar de musikgrupper som musiker tillhör. Det finns en many-to-many-relation mellan en person och de grupper som de är medlemmar i, så du kan använda en ManyToManyField
för att representera denna relation. Det finns dock en hel del detaljer om medlemskapet som du kanske vill samla in, till exempel datumet då personen gick med i gruppen.
För dessa situationer tillåter Django dig att ange den modell som ska användas för att styra förhållandet många-till-många. Du kan sedan lägga till extra fält på den mellanliggande modellen. Den mellanliggande modellen är associerad med ManyToManyField
med through
argumentet för att peka på den modell som kommer att fungera som en mellanhand. För vårt musikexempel skulle koden se ut ungefär så här:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through="Membership")
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["person", "group"], name="unique_person_group"
)
]
När du konfigurerar den mellanliggande modellen anger du uttryckligen främmande nycklar till de modeller som är involverade i förhållandet många-till-många. Denna uttryckliga deklaration definierar hur de två modellerna är relaterade.
Om du inte vill ha flera associationer mellan samma instanser, lägg till en UniqueConstraint
som inkluderar fälten from
och to
. Djangos automatiskt genererade many-to-many-tabeller innehåller en sådan begränsning.
Det finns några begränsningar för den mellanliggande modellen:
Din mellanliggande modell måste innehålla en - och endast en - främmande nyckel till källmodellen (detta skulle vara
Group
i vårt exempel), eller så måste du uttryckligen ange de främmande nycklar som Django ska använda för relationen medManyToManyField.through_fields
. Om du har mer än en främmande nyckel ochthrough_fields
inte anges, kommer ett valideringsfel att uppstå. En liknande begränsning gäller för den främmande nyckeln till målmodellen (detta skulle varaPerson
i vårt exempel).För en modell som har ett många-till-många-förhållande till sig själv via en mellanliggande modell, är två främmande nycklar till samma modell tillåtna, men de kommer att behandlas som de två (olika) sidorna av många-till-många-förhållandet. Om
through_fields
inte anges, kommer den första främmande nyckeln att anses representera källsidan avManyToManyField
, medan den andra kommer att anses representera målsidan. Om det finns mer än två främmande nycklar måste du dock angethrough_fields
för att uttryckligen ange vilka främmande nycklar som ska användas, annars kommer ett valideringsfel att uppstå.
Nu när du har ställt in din ManyToManyField
för att använda din mellanliggande modell (Membership
, i det här fallet), är du redo att börja skapa några många-till-många-relationer. Du gör detta genom att skapa instanser av den mellanliggande modellen:
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(
... person=ringo,
... group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.",
... )
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(
... person=paul,
... group=beatles,
... date_joined=date(1960, 8, 1),
... invite_reason="Wanted to form a band.",
... )
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
Du kan också använda add()
, create()
eller set()
för att skapa relationer, så länge du anger through_defaults
för alla obligatoriska fält:
>>> beatles.members.add(john, through_defaults={"date_joined": date(1960, 8, 1)})
>>> beatles.members.create(
... name="George Harrison", through_defaults={"date_joined": date(1960, 8, 1)}
... )
>>> beatles.members.set(
... [john, paul, ringo, george], through_defaults={"date_joined": date(1960, 8, 1)}
... )
Du kanske föredrar att skapa instanser av den intermediära modellen direkt.
Om den anpassade genomgående tabellen som definieras av den mellanliggande modellen inte upprätthåller unikhet för paret (model1, model2)
, och tillåter flera värden, kommer remove()
-anropet att ta bort alla mellanliggande modellinstanser:
>>> Membership.objects.create(
... person=ringo,
... group=beatles,
... date_joined=date(1968, 9, 4),
... invite_reason="You've been gone for a month and we miss you.",
... )
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>
Metoden clear()
kan användas för att ta bort alla många-till-många-relationer för en instans:
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>
När du har etablerat många-till-många-relationerna kan du ställa frågor. Precis som med vanliga many-to-many-relationer kan du ställa frågor med hjälp av attributen i den many-to-many-relaterade modellen:
# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith="Paul")
<QuerySet [<Group: The Beatles>]>
Eftersom du använder en intermediär modell kan du också ställa frågor om dess attribut:
# Find all the members of the Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(
... group__name="The Beatles", membership__date_joined__gt=date(1961, 1, 1)
... )
<QuerySet [<Person: Ringo Starr]>
Om du behöver få tillgång till information om ett medlemskap kan du göra det genom att direkt fråga efter modellen Membership
:
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
Ett annat sätt att komma åt samma information är genom att fråga many-to-many reverse relationship från ett Person
-objekt:
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
En-till-en-relationer¶
För att definiera en en-till-en-relation använder du OneToOneField
. Du använder den precis som alla andra Field
-typer: genom att inkludera den som ett klassattribut i din modell.
Detta är mest användbart på primärnyckeln för ett objekt när det objektet ”utvidgar” ett annat objekt på något sätt.
OneToOneField
kräver ett positionsargument: den klass som modellen är relaterad till.
Till exempel, om du byggde en databas med ”platser”, skulle du bygga ganska standard saker som adress, telefonnummer, etc. i databasen. Sedan, om du vill bygga en databas med restauranger ovanpå platserna, istället för att upprepa dig själv och replikera dessa fält i Restaurant
-modellen, kan du göra Restaurant
har en OneToOneField
till Place
(eftersom en restaurang ”är en” plats; i själva verket, för att hantera detta skulle du vanligtvis använda :ref:arv <model-inheritance>
, vilket innebär en implicit en-till-en-relation).
Precis som med ForeignKey
kan en rekursiv relation definieras och referenser till ännu odefinierade modeller kan göras.
Se även
Se Exempel på modell för en-till-en-relation för ett fullständigt exempel.
OneToOneField
-fält accepterar också ett valfritt parent_link
-argument.
OneToOneField
klasser brukade automatiskt bli den primära nyckeln i en modell. Detta är inte längre sant (även om du manuellt kan skicka in argumentet primary_key
om du vill). Således är det nu möjligt att ha flera fält av typen OneToOneField
på en enda modell.
Modeller över filer¶
Det är helt OK att relatera en modell till en modell från en annan app. För att göra detta importerar du den relaterade modellen högst upp i filen där din modell definieras. Hänvisa sedan till den andra modellklassen där det behövs. Till exempel:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(
ZipCode,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
Alternativt kan du använda en latent referens till den relaterade modellen, som anges som en sträng i formatet "app_label.ModelName"
. Detta kräver inte att den relaterade modellen importeras. Till exempel:
from django.db import models
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(
"geography.ZipCode",
on_delete=models.SET_NULL,
blank=True,
null=True,
)
Se :ref:``lazy relationships <lazy-relationships>` för mer information.
Restriktioner för fältnamn¶
Django lägger vissa restriktioner på modellfältnamn:
Ett fältnamn kan inte vara ett reserverat ord i Python, eftersom det skulle resultera i ett syntaxfel i Python. Ett exempel:
class Example(models.Model): pass = models.IntegerField() # 'pass' is a reserved word!
Ett fältnamn kan inte innehålla mer än ett understreck i en rad, på grund av hur Djangos syntax för frågeuppslagning fungerar. Till exempel:
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
Ett fältnamn kan av samma skäl inte sluta med ett understreck.
Ett fältnamn kan inte vara
check
, eftersom detta skulle åsidosätta check-ramverketsModel.check()
-metod.
Dessa begränsningar kan dock kringgås, eftersom fältnamnet inte nödvändigtvis måste överensstämma med databasens kolumnnamn. Se alternativet db_column
.
Reserverade SQL-ord, såsom ”join”, ”where” eller ”select”, är tillåtna som modellfältnamn, eftersom Django escapar alla databastabellnamn och kolumnnamn i varje underliggande SQL-fråga. Den använder citatsyntaxen för din specifika databasmotor.
Anpassade fälttyper¶
Om något av de befintliga modellfälten inte kan användas för dina ändamål, eller om du vill dra nytta av några mindre vanliga kolumntyper i databasen, kan du skapa en egen fältklass. Fullständig information om hur du skapar egna fält finns i Så här skapar du anpassade modellfält.
alternativ för Meta
¶
Ge din modell metadata genom att använda en inre class Meta
, så här:
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
Modellmetadata är ”allt som inte är ett fält”, t.ex. ordningsalternativ (ordering
), databasens tabellnamn (db_table
) eller mänskligt läsbara namn i singular och plural (verbose_name
och verbose_name_plural
). Inga av dessa är obligatoriska och det är helt frivilligt att lägga till class Meta
till en modell.
En fullständig lista över alla möjliga Meta
-alternativ finns i modellalternativreferens.
Modellens attribut¶
objekt
Det viktigaste attributet för en modell är
Manager
. Det är det gränssnitt genom vilket databasfrågor tillhandahålls till Django-modeller och används för att hämta instanser från databasen. Om ingen anpassadManager
är definierad är standardnamnetobjects
. Managers är endast tillgängliga via modellklasser, inte modellinstanserna.
Modellmetoder¶
Definiera anpassade metoder på en modell för att lägga till anpassad funktionalitet på ”radnivå” till dina objekt. Medan Manager
-metoder är avsedda att göra ”tabellomfattande” saker, bör modellmetoder agera på en viss modellinstans.
Detta är en värdefull teknik för att hålla affärslogiken på ett ställe - modellen.
Till exempel har denna modell några anpassade metoder:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
@property
def full_name(self):
"Returns the person's full name."
return f"{self.first_name} {self.last_name}"
Den sista metoden i detta exempel är en property.
I model instance reference finns en komplett lista över methods som automatiskt ges till varje modell. Du kan åsidosätta de flesta av dessa - se Åsidosätta fördefinierade modellmetoder nedan - men det finns ett par som du nästan alltid vill definiera:
__str__()
En ”magisk metod” i Python som returnerar en strängrepresentation av valfritt objekt. Det här är vad Python och Django kommer att använda när en modellinstans måste tvingas och visas som en vanlig sträng. Framför allt händer detta när du visar ett objekt i en interaktiv konsol eller i administratören.
Du vill alltid definiera den här metoden; standardinställningen är inte alls till någon större hjälp.
get_absolute_url()
Detta talar om för Django hur man beräknar URL:en för ett objekt. Django använder detta i sitt admin-gränssnitt och när som helst när de behöver räkna ut en URL för ett objekt.
Alla objekt som har en URL som identifierar dem på ett unikt sätt bör definiera denna metod.
Åsidosätta fördefinierade modellmetoder¶
Det finns en annan uppsättning modellmetoder som kapslar in en massa databasbeteenden som du vill anpassa. I synnerhet kommer du ofta att vilja ändra hur save()
och delete()
fungerar.
Det står dig fritt att åsidosätta dessa metoder (och alla andra modellmetoder) för att ändra beteendet.
Ett klassiskt användningsfall för att åsidosätta de inbyggda metoderna är om du vill att något ska hända när du sparar ett objekt. Till exempel (se save()
för dokumentation av de parametrar den accepterar):
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, **kwargs):
do_something()
super().save(**kwargs) # Call the "real" save() method.
do_something_else()
Du kan också förhindra sparande:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super().save(**kwargs) # Call the "real" save() method.
Det är viktigt att komma ihåg att anropa superklassmetoden - det är det där med super().save(**kwargs)
- för att säkerställa att objektet fortfarande sparas i databasen. Om du glömmer att anropa superklassmetoden kommer standardbeteendet inte att ske och databasen kommer inte att beröras.
Det är också viktigt att du skickar igenom de argument som kan skickas till modellmetoden - det är vad **kwargs
-biten gör. Django kommer från tid till annan att utöka kapaciteten hos inbyggda modellmetoder och lägga till nya nyckelordsargument. Om du använder **kwargs
i dina metoddefinitioner, är du garanterad att din kod automatiskt kommer att stödja dessa argument när de läggs till.
Om du vill uppdatera ett fältvärde i metoden save()
, kanske du också vill lägga till fältet i nyckelordsargumentet update_fields
. Detta kommer att säkerställa att fältet sparas när update_fields
anges. Till exempel:
from django.db import models
from django.utils.text import slugify
class Blog(models.Model):
name = models.CharField(max_length=100)
slug = models.TextField()
def save(self, **kwargs):
self.slug = slugify(self.name)
if (
update_fields := kwargs.get("update_fields")
) is not None and "name" in update_fields:
kwargs["update_fields"] = {"slug"}.union(update_fields)
super().save(**kwargs)
Se Ange vilka fält som ska sparas för mer information.
Åsidosatta modellmetoder anropas inte vid bulkoperationer
Observera att metoden delete()
för ett objekt inte nödvändigtvis anropas när du raderar objekt i bulk med hjälp av en QuerySet eller som ett resultat av en cascading delete
. För att säkerställa att anpassad raderingslogik körs kan du använda pre_delete
och/eller post_delete
signaler.
Tyvärr finns det ingen lösning när creating
eller updating
objekt i bulk, eftersom ingen av save()
, pre_save
, och post_save
anropas.
Exekvering av anpassad SQL¶
Ett annat vanligt mönster är att skriva anpassade SQL-satser i modellmetoder och metoder på modulnivå. Mer information om hur du använder raw SQL finns i dokumentationen på using raw SQL.
Arv av modell¶
Modellarv i Django fungerar nästan identiskt med hur normalt klassarv fungerar i Python, men grunderna i början av sidan bör fortfarande följas. Det betyder att basklassen bör underordna sig django.db.models.Model
.
Det enda beslut du behöver fatta är om du vill att föräldramodellerna ska vara modeller i sig (med egna databastabeller), eller om föräldrarna bara är innehavare av gemensam information som bara kommer att vara synlig genom barnmodellerna.
Det finns tre olika typer av arv som är möjliga i Django.
Ofta vill du bara använda den överordnade klassen för att hålla information som du inte vill behöva skriva ut för varje underordnad modell. Den här klassen kommer aldrig att användas isolerat, så abstrakta basklasser är vad du är ute efter.
Om du subklassar en befintlig modell (kanske något från en helt annan applikation) och vill att varje modell ska ha sin egen databastabell är Arv från flera tabeller rätt väg att gå.
Slutligen, om du bara vill ändra en modells beteende på Python-nivå utan att ändra modellens fält på något sätt, kan du använda Proxy-modeller.
Abstrakta basklasser¶
Abstrakta basklasser är användbara när du vill lägga in viss gemensam information i ett antal andra modeller. Du skriver din basklass och sätter abstract=True
i Meta klassen. Denna modell kommer då inte att användas för att skapa någon databastabell. Istället, när den används som basklass för andra modeller, kommer dess fält att läggas till fälten i underklassen.
Ett exempel:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
Modellen Student
kommer att ha tre fält: name
, age
och home_group
. Modellen CommonInfo
kan inte användas som en vanlig Django-modell, eftersom det är en abstrakt basklass. Den genererar inte en databastabell eller har en manager och kan inte instansieras eller sparas direkt.
Fält som ärvs från abstrakta basklasser kan åsidosättas med ett annat fält eller värde, eller tas bort med None
.
För många användningsområden är den här typen av modellarv exakt vad du vill ha. Det ger ett sätt att faktorisera ut gemensam information på Python-nivå, samtidigt som det bara skapas en databastabell per underordnad modell på databasnivå.
Meta
arv¶
När en abstrakt basklass skapas gör Django alla Meta innerklasser som du deklarerat i basklassen tillgängliga som attribut. Om en underordnad klass inte deklarerar sin egen Meta-klass, kommer den att ärva förälderns Meta. Om barnet vill utöka förälderns Meta-klass kan det subklassa den. Till exempel:
from django.db import models
class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering = ["name"]
class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = "student_info"
Django gör en justering av klassen Meta för en abstrakt basklass: innan attributet Meta installeras, sätts abstract=False
. Detta innebär att barn till abstrakta basklasser inte automatiskt blir abstrakta klasser själva. För att skapa en abstrakt basklass som ärver från en annan abstrakt basklass måste du uttryckligen ställa in abstract=True
på barnet.
Vissa attribut är inte meningsfulla att inkludera i Meta-klassen för en abstrakt basklass. Att inkludera db_table
skulle till exempel innebära att alla underordnade klasser (de som inte anger sin egen Meta) skulle använda samma databastabell, vilket nästan säkert inte är vad du vill.
På grund av hur Python-arvet fungerar, om en underordnad klass ärver från flera abstrakta basklasser, kommer endast Meta-alternativen från den första listade klassen att ärvas som standard. För att ärva Meta-alternativ från flera abstrakta basklasser måste du uttryckligen deklarera Meta-arvet. Till exempel:
from django.db import models
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
ordering = ["name"]
class Unmanaged(models.Model):
class Meta:
abstract = True
managed = False
class Student(CommonInfo, Unmanaged):
home_group = models.CharField(max_length=5)
class Meta(CommonInfo.Meta, Unmanaged.Meta):
pass
Arv från flera tabeller¶
Den andra typen av modellarv som stöds av Django är när varje modell i hierarkin är en modell i sig själv. Varje modell motsvarar sin egen databastabell och kan efterfrågas och skapas individuellt. Arvsrelationen introducerar länkar mellan barnmodellen och var och en av dess föräldrar (via en automatiskt skapad OneToOneField
). Till exempel:
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
Alla fält i Place
kommer också att finnas tillgängliga i Restaurant
, även om uppgifterna kommer att finnas i en annan databastabell. Så dessa är båda möjliga:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
Om du har ett Place
som också är en Restaurant
, kan du komma från Place
-objektet till Restaurant
-objektet genom att använda den gemena versionen av modellnamnet:
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
Men om p
i exemplet ovan inte var en Restaurant
(den hade skapats direkt som ett Place
-objekt eller var förälder till någon annan klass), skulle en hänvisning till p.restaurant
ge upphov till ett Restaurant.DoesNotExist
undantag.
Den automatiskt skapade OneToOneField
på Restaurant
som länkar den till Place
ser ut så här:
place_ptr = models.OneToOneField(
Place,
on_delete=models.CASCADE,
parent_link=True,
primary_key=True,
)
Du kan åsidosätta det fältet genom att deklarera din egen OneToOneField
med parent_link=True
på Restaurant
.
Meta
och arv av flera tabeller¶
I arvssituationen med flera tabeller är det inte meningsfullt för en underordnad klass att ärva från sin förälders Meta-klass. Alla Meta-alternativ har redan tillämpats på föräldraklassen och att tillämpa dem igen skulle normalt bara leda till motsägelsefullt beteende (detta står i kontrast till fallet med den abstrakta basklassen, där basklassen inte existerar i sin egen rätt).
Så en barnmodell har inte tillgång till sin förälders Meta-klass. Det finns dock några få begränsade fall där barnet ärver beteende från föräldern: om barnet inte anger ett ordering
-attribut eller ett get_latest_by
-attribut, kommer det att ärva dessa från sin förälder.
Om föräldern har en ordning och du inte vill att barnet ska ha någon naturlig ordning kan du uttryckligen inaktivera den:
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
Arv och omvända relationer¶
Eftersom arv med flera tabeller använder en implicit OneToOneField
för att länka barnet och föräldern, är det möjligt att flytta från föräldern ner till barnet, som i exemplet ovan. Detta använder dock upp namnet som är standardvärdet för related_name
för ForeignKey
och ManyToManyField
relationer. Om du lägger dessa typer av relationer på en underklass av modermodellen måste du måste ange attributet related_name
på varje sådant fält. Om du glömmer det kommer Django att göra ett valideringsfel.
Om vi till exempel använder ovanstående klass Place
igen, låt oss skapa en annan underklass med en ManyToManyField
:
class Supplier(Place):
customers = models.ManyToManyField(Place)
Detta resulterar i felet:
Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.
HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.
Om du lägger till related_name
i fältet customers
på följande sätt skulle felet lösas: models.ManyToManyField(Place, related_name='provider')
.
Ange fältet för överordnad länk¶
Som nämnts kommer Django automatiskt att skapa en OneToOneField
som länkar din underordnade klass tillbaka till alla icke-abstrakta överordnade modeller. Om du vill styra namnet på attributet som länkar tillbaka till föräldern kan du skapa din egen OneToOneField
och ställa in parent_link=True
för att ange att ditt fält är länken tillbaka till föräldraklassen.
Proxy-modeller¶
När du använder :ref:multi-table inheritance <multi-table-inheritance>
skapas en ny databastabell för varje underklass av en modell. Detta är vanligtvis det önskade beteendet, eftersom underklassen behöver en plats för att lagra eventuella ytterligare datafält som inte finns i basklassen. Ibland vill man dock bara ändra Python-beteendet för en modell - kanske för att ändra standardhanteraren eller lägga till en ny metod.
Detta är vad arv av proxymodeller är till för: att skapa en proxy för den ursprungliga modellen. Du kan skapa, ta bort och uppdatera instanser av proxymodellen och alla data sparas som om du använde den ursprungliga (icke-proxyiserade) modellen. Skillnaden är att du kan ändra saker som standardmodellens beställning eller standardhanteraren i proxyn utan att behöva ändra originalet.
Proxy-modeller deklareras som vanliga modeller. Du talar om för Django att det är en proxymodell genom att ställa in attributet proxy
i klassen Meta
till True
.
Anta till exempel att du vill lägga till en metod i modellen Person
. Du kan göra det så här:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
Klassen MyPerson
arbetar i samma databastabell som sin överordnade klass Person
. I synnerhet kommer alla nya instanser av Person
också att vara tillgängliga via MyPerson
, och vice versa:
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
Du kan också använda en proxymodell för att definiera en annan standardbeställning på en modell. Du kanske inte alltid vill beställa modellen Person
, men regelbundet beställa efter attributet last_name
när du använder proxy:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
Nu kommer vanliga Person
-frågor att vara oordnade och OrderedPerson
-frågor kommer att vara ordnade efter efternamn
.
Proxy-modeller ärver Meta
-attribut :ref:` på samma sätt som vanliga modeller <meta-and-multi-table-inheritance>`.
QuerySet
returnerar fortfarande den modell som begärdes¶
Det finns inget sätt att få Django att returnera till exempel ett MyPerson
-objekt när du frågar efter Person
-objekt. En queryset för Person
-objekt kommer att returnera dessa typer av objekt. Hela poängen med proxyobjekt är att kod som förlitar sig på den ursprungliga Person
kommer att använda dem och din egen kod kan använda de tillägg du inkluderade (som ingen annan kod förlitar sig på ändå). Det är inte ett sätt att ersätta Person
(eller någon annan) modell överallt med något av din egen skapelse.
Restriktioner för basklass¶
En proxymodell måste ärva från exakt en icke-abstrakt modellklass. Du kan inte ärva från flera icke-abstrakta modeller eftersom proxymodellen inte ger någon koppling mellan raderna i de olika databastabellerna. En proxymodell kan ärva från hur många abstrakta modellklasser som helst, förutsatt att de inte definierar några modellfält. En proxymodell kan också ärva från ett valfritt antal proxymodeller som delar en gemensam icke-abstrakt föräldraklass.
Förvaltare av fullmaktsmodeller¶
Om du inte anger några modellhanterare för en proxymodell ärver den hanterarna från sina modellföräldrar. Om du definierar en hanterare för proxymodellen blir den standard, även om alla hanterare som definierats för de överordnade klasserna fortfarande är tillgängliga.
Om vi fortsätter med vårt exempel från ovan kan du ändra standardhanteraren som används när du frågar Person
-modellen så här:
from django.db import models
class NewManager(models.Manager):
# ...
pass
class MyPerson(Person):
objects = NewManager()
class Meta:
proxy = True
Om du vill lägga till en ny hanterare i proxyn utan att ersätta den befintliga standardhanteraren kan du använda de tekniker som beskrivs i dokumentationen :ref:custom manager <custom-managers-and-inheritance>
: skapa en basklass som innehåller de nya hanterarna och ärva den efter den primära basklassen:
# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
secondary = NewManager()
class Meta:
abstract = True
class MyPerson(Person, ExtraManagers):
class Meta:
proxy = True
Du kommer förmodligen inte att behöva göra det här särskilt ofta, men när du gör det är det möjligt.
Skillnader mellan proxy-arv och icke-hanterade modeller¶
Arv av proxymodeller kan se ut ungefär som att skapa en ohanterad modell med hjälp av attributet managed
på en modells Meta
-klass.
Med en noggrann inställning av Meta.db_table
kan du skapa en ohanterad modell som skuggar en befintlig modell och lägger till Python-metoder till den. Det skulle dock vara mycket repetitivt och bräckligt eftersom du måste hålla båda kopiorna synkroniserade om du gör några ändringar.
Å andra sidan är proxymodeller avsedda att bete sig exakt som den modell som de är proxy för. De är alltid synkroniserade med den överordnade modellen eftersom de direkt ärver dess fält och hanterare.
De allmänna reglerna är:
Om du speglar en befintlig modell eller databastabell och inte vill ha alla kolumner i den ursprungliga databastabellen, använd
Meta.managed=False
. Det alternativet är normalt användbart för modellering av databasvyer och tabeller som inte är under kontroll av Django.Om du vill ändra Python-beteendet för en modell, men behålla alla fält som finns i originalet, använder du
Meta.proxy=True
. Detta ställer in saker så att proxymodellen är en exakt kopia av lagringsstrukturen för den ursprungliga modellen när data sparas.
Multipel arvsmassa¶
Precis som med Pythons subklassning är det möjligt för en Django-modell att ärva från flera överordnade modeller. Tänk på att Pythons normala regler för namnupplösning gäller. Den första basklassen som ett visst namn (t.ex. Meta) förekommer i kommer att vara den som används; till exempel innebär detta att om flera föräldrar innehåller en Meta-klass, kommer endast den första att användas och alla andra kommer att ignoreras.
I allmänhet behöver du inte ärva från flera föräldrar. Det främsta användningsfallet där detta är användbart är för ”mix-in”-klasser: att lägga till ett visst extra fält eller en viss metod till varje klass som ärver mix-in. Försök att hålla dina arvshierarkier så enkla och okomplicerade som möjligt så att du inte behöver kämpa för att ta reda på varifrån en viss information kommer.
Observera att om du ärver från flera modeller som har ett gemensamt primärnyckelfält id
kommer ett fel att uppstå. För att korrekt använda multipelt arv kan du använda en explicit AutoField
i basmodellerna:
class Article(models.Model):
article_id = models.AutoField(primary_key=True)
...
class Book(models.Model):
book_id = models.AutoField(primary_key=True)
...
class BookReview(Book, Article):
pass
Eller använd en gemensam förfader för att hålla AutoField
. Detta kräver att man använder en explicit OneToOneField
från varje överordnad modell till den gemensamma förfadern för att undvika en krock mellan de fält som genereras automatiskt och ärvs av underordnade modeller:
class Piece(models.Model):
pass
class Article(Piece):
article_piece = models.OneToOneField(
Piece, on_delete=models.CASCADE, parent_link=True
)
...
class Book(Piece):
book_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
...
class BookReview(Book, Article):
pass
Fältnamnet ”dölja” är inte tillåtet¶
I normalt Python-klassarv är det tillåtet för en underordnad klass att åsidosätta alla attribut från den överordnade klassen. I Django är detta vanligtvis inte tillåtet för modellfält. Om en icke-abstrakt modellbasklass har ett fält som heter author
, kan du inte skapa ett annat modellfält eller definiera ett attribut som heter author
i någon klass som ärver från den basklassen.
Denna begränsning gäller inte för modellfält som ärvs från en abstrakt modell. Sådana fält kan åsidosättas med ett annat fält eller värde, eller tas bort genom att ange field_name = None
.
Varning
Modellhanterare ärvs från abstrakta basklasser. Att åsidosätta ett nedärvt fält som refereras av en nedärvd Manager
kan orsaka subtila buggar. Se :ref:``Custom managers and model inheritance <custom-managers-and-inheritance>`.
Observera
Vissa fält definierar extra attribut på modellen, t.ex. en ForeignKey
definierar ett extra attribut med _id
som läggs till fältnamnet, liksom related_name
och related_query_name
på den utländska modellen.
Dessa extra attribut kan inte åsidosättas om inte det fält som definierar det ändras eller tas bort så att det inte längre definierar det extra attributet.
Åsidosättande av fält i en överordnad modell leder till svårigheter inom områden som initialisering av nya instanser (specificera vilket fält som initialiseras i Model.__init__
) och serialisering. Det här är funktioner som normalt Python-klassarv inte behöver hantera på samma sätt, så skillnaden mellan Django-modellarv och Python-klassarv är inte godtycklig.
Denna begränsning gäller endast för attribut som är Field
-instanser. Normala Python-attribut kan åsidosättas om du vill. Det gäller också bara namnet på attributet som Python ser det: om du manuellt anger databasens kolumnnamn kan du ha samma kolumnnamn som visas i både ett barn och en förfadersmodell för arv i flera tabeller (de är kolumner i två olika databastabeller).
Django kommer att ge upphov till ett FieldError
om du åsidosätter något modellfält i någon förfadersmodell.
Observera att på grund av det sätt som fält löses under klassdefinitionen, löses modellfält som ärvs från flera abstrakta föräldramodeller i en strikt djupförsta ordning. Detta står i kontrast till standard Python MRO, som löses i bredd-först ordning i fall av diamantformat arv. Denna skillnad påverkar endast komplexa modellhierarkier, som du (enligt rådet ovan) bör försöka undvika.
Organisera modeller i ett paket¶
Med kommandot manage.py startapp
skapas en programstruktur som innehåller filen models.py
. Om du har många modeller kan det vara bra att organisera dem i separata filer.
För att göra det skapar du ett models
-paket. Ta bort models.py
och skapa en katalog myapp/models/
med en fil __init__.py
och filerna för att lagra dina modeller. Du måste importera modellerna i filen __init__.py
.
Om du t.ex. har organic.py
och synthetic.py
i katalogen models
:
myapp/modeller/__init__.py
¶from .organic import Person
from .synthetic import Robot
Att explicit importera varje modell i stället för att använda from .models import *
har fördelarna att namnrymden inte blir rörig, att koden blir mer läsbar och att kodanalysverktygen förblir användbara.
Se även
- Modellernas referens
Täcker alla modellrelaterade API:er, inklusive modellfält, relaterade objekt och
QuerySet
.