Skriva din första Django-app, del 2¶
Denna tutorial börjar där Tutorial 1 slutade. Vi ställer in databasen, skapar din första modell och får en snabb introduktion till Djangos automatiskt genererade administratörssida.
Var du kan få hjälp:
Om du har problem med att gå igenom den här handledningen kan du gå till avsnittet Att få hjälp i FAQ.
Uppsättning av databas¶
Öppna nu mysite/settings.py
. Det är en vanlig Python-modul med variabler på modulnivå som representerar Django-inställningar.
Som standard använder konfigurationen DATABASER
SQLite. Om du är nybörjare på databaser, eller bara är intresserad av att prova Django, är detta det enklaste valet. SQLite ingår i Python, så du behöver inte installera något annat för att stödja din databas. När du startar ditt första riktiga projekt kanske du dock vill använda en mer skalbar databas som PostgreSQL för att undvika huvudvärk för databasbyte längs vägen.
Om du vill använda en annan databas, se :ref:detaljer för att anpassa och få igång din databas <database-installation>
.
Medan du redigerar mysite/settings.py
, ställ in TIME_ZONE
till din tidszon.
Lägg också märke till INSTALLED_APPS
högst upp i filen. Den innehåller namnen på alla Django-applikationer som är aktiverade i den här Django-instansen. Appar kan användas i flera projekt, och du kan paketera och distribuera dem för användning av andra i deras projekt.
Som standard innehåller INSTALLED_APPS
följande appar, som alla levereras med Django:
django.contrib.admin
– Administrationssidan. Du kommer att använda den inom kort.django.contrib.auth
– Ett system för autentisering.django.contrib.contenttypes
– Ett ramverk för innehållstyper.django.contrib.sessions
– Ett ramverk för sessioner.django.contrib.messages
– Ett ramverk för meddelanden.django.contrib.staticfiles
– Ett ramverk för hantering av statiska filer.
Dessa applikationer ingår som standard som en bekvämlighet för det vanliga fallet.
Vissa av dessa program använder dock minst en databastabell, så vi måste skapa tabellerna i databasen innan vi kan använda dem. Det gör du genom att köra följande kommando:
$ python manage.py migrate
...\> py manage.py migrate
Kommandot migrate
tittar på inställningen INSTALLED_APPS
och skapar alla nödvändiga databastabeller enligt databasinställningarna i filen mysite/settings.py
och de databasmigreringar som levereras med appen (vi går igenom dem senare). Du kommer att se ett meddelande för varje migrering som den tillämpar. Om du är intresserad kan du köra kommandoradsklienten för din databas och skriva dt
(PostgreSQL), SHOW TABLES;
(MariaDB, MySQL), .tables
(SQLite) eller SELECT TABLE_NAME FROM USER_TABLES;
(Oracle) för att visa de tabeller som Django skapade.
För minimalisterna
Som vi sa ovan ingår standardapplikationerna för det vanliga fallet, men alla behöver dem inte. Om du inte behöver någon eller alla av dem kan du kommentera ut eller ta bort lämpliga rader från INSTALLED_APPS
innan du kör migrate
. Kommandot migrate
kommer endast att köra migreringar för appar i INSTALLED_APPS
.
Skapa modeller¶
Nu ska vi definiera dina modeller - i princip din databaslayout, med ytterligare metadata.
Filosofi
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 du lagrar. Django följer DRY-principen. Målet är att definiera din datamodell på ett ställe och automatiskt härleda saker från den.
Detta inkluderar migreringarna - till skillnad från i Ruby On Rails, till exempel, härrör migreringarna helt från din models-fil och är i huvudsak en historia som Django kan rulla igenom för att uppdatera ditt databasschema så att det matchar dina nuvarande modeller.
I vår poll-app skapar vi två modeller: Question
och Choice
. En Question
har en fråga och ett publiceringsdatum. En Choice
har två fält: texten i valet och en rösträkning. Varje Choice
är associerat med en Question
.
Dessa koncept representeras av Python-klasser. Redigera filen polls/models.py
så att den ser ut så här:
polls/modeller.py
¶from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
Här representeras varje modell av en klass som subklassar django.db.models.Model
. Varje modell har ett antal klassvariabler, som var och en representerar ett databasfält i modellen.
Varje fält representeras av en instans av en Field
-klass - t.ex. CharField
för teckenfält och DateTimeField
för datatider. Detta talar om för Django vilken typ av data varje fält innehåller.
Namnet på varje Field
-instans (t.ex. question_text
eller pub_date
) är fältets namn i ett maskinvänligt format. Du kommer att använda detta värde i din Python-kod och din databas kommer att använda det som kolumnnamn.
Du kan använda ett valfritt första positionellt argument till en Field
för att ange ett mänskligt läsbart namn. Det används i ett par introspektiva delar av Django, och det fungerar även som dokumentation. Om detta fält inte tillhandahålls kommer Django att använda det maskinläsbara namnet. I det här exemplet har vi bara definierat ett mänskligt läsbart namn för Question.pub_date
. För alla andra fält i denna modell kommer fältets maskinläsbara namn att räcka som dess mänskligt läsbara namn.
Vissa Field
-klasser har obligatoriska argument. CharField
, till exempel, kräver att du ger den en max_length
. Det används inte bara i databasschemat utan även i valideringen, som vi snart ska se.
En Field
kan också ha olika valfria argument; i det här fallet har vi satt default
-värdet för votes
till 0.
Slutligen, notera att ett förhållande definieras med ForeignKey
. Det säger till Django att varje Choice
är relaterad till en enda Question
. Django stöder alla vanliga databasrelationer: många-till-en, många-till-många och en-till-en.
Aktivering av modeller¶
Den lilla biten modellkod ger Django en hel del information. Med den kan Django göra följande:
Skapa ett databasschema (
CREATE TABLE
-uttalanden) för den här appen.Skapa ett Python API för databasåtkomst för åtkomst till objekten
Question
ochChoice
.
Men först måste vi tala om för vårt projekt att appen polls
är installerad.
Filosofi
Django-appar är ”pluggbara”: Du kan använda en app i flera projekt och du kan distribuera appar, eftersom de inte behöver vara knutna till en viss Django-installation.
För att inkludera appen i vårt projekt måste vi lägga till en referens till dess konfigurationsklass i inställningen INSTALLED_APPS
. Klassen PollsConfig
finns i filen polls/apps.py
, så dess prickade sökväg är 'polls.apps.PollsConfig'
. Redigera filen mysite/settings.py
och lägg till den prickade sökvägen i inställningen INSTALLED_APPS
. Det kommer att se ut så här:
mysite/settings.py
¶INSTALLED_APPS = [
"polls.apps.PollsConfig",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
Nu vet Django att appen polls
ska inkluderas. Låt oss köra ett annat kommando:
$ python manage.py makemigrations polls
...\> py manage.py makemigrations polls
Du bör se något som liknar följande:
Migrations for 'polls':
polls/migrations/0001_initial.py
+ Create model Question
+ Create model Choice
Genom att köra makemigrations
talar du om för Django att du har gjort några ändringar i dina modeller (i det här fallet har du gjort nya) och att du vill att ändringarna ska lagras som en migration.
Migreringar är hur Django lagrar ändringar i dina modeller (och därmed ditt databasschema) - de är filer på disken. Du kan läsa migreringen för din nya modell om du vill; det är filen polls/migrations/0001_initial.py
. Oroa dig inte, du förväntas inte läsa dem varje gång Django gör en, men de är utformade för att vara mänskligt redigerbara om du manuellt vill justera hur Django ändrar saker.
Det finns ett kommando som kör migreringarna åt dig och hanterar ditt databasschema automatiskt - det kallas migrate
, och vi kommer till det om en stund - men först ska vi se vilken SQL den migreringen skulle köra. Kommandot sqlmigrate
tar migreringsnamn och returnerar deras SQL:
$ python manage.py sqlmigrate polls 0001
...\> py manage.py sqlmigrate polls 0001
Du bör se något som liknar följande (vi har omformat det för läsbarhetens skull):
BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"question_text" varchar(200) NOT NULL,
"pub_date" timestamp with time zone NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL,
"question_id" bigint NOT NULL
);
ALTER TABLE "polls_choice"
ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
FOREIGN KEY ("question_id")
REFERENCES "polls_question" ("id")
DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;
Notera följande:
Den exakta utmatningen varierar beroende på vilken databas du använder. Exemplet ovan genereras för PostgreSQL.
Tabellnamnen genereras automatiskt genom att kombinera appens namn (
polls
) och modellens namn med gemener –question
ochchoice
. (Du kan åsidosätta detta beteende.)Primärnycklar (ID:n) läggs till automatiskt. (Du kan även åsidosätta detta.)
Enligt konvention lägger Django till
"_id"
till fältnamnet för den främmande nyckeln. (Ja, du kan åsidosätta detta också)Det främmande nyckelförhållandet görs explicit av en `` FOREIGN KEY `` begränsning. Oroa dig inte för `` DEFERRABLE`` -delarna; det säger till PostgreSQL att inte genomdriva den främmande nyckeln förrän i slutet av transaktionen.
Den är skräddarsydd för den databas du använder, så databasspecifika fälttyper som
auto_increment
(MySQL),bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
(PostgreSQL) ellerinteger primary key autoincrement
(SQLite) hanteras automatiskt åt dig. Detsamma gäller för citering av fältnamn - t.ex. med dubbla citat eller enkla citat.Kommandot
sqlmigrate
kör faktiskt inte migreringen på din databas - istället skriver det ut den på skärmen så att du kan se vilken SQL Django tror krävs. Det är användbart för att kontrollera vad Django kommer att göra eller om du har databasadministratörer som kräver SQL-skript för ändringar.
Om du är intresserad kan du också köra python manage.py check
; detta kontrollerar eventuella problem i ditt projekt utan att göra migreringar eller röra databasen.
Kör nu migrate
igen för att skapa dessa modelltabeller i din databas:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Rendering model states... DONE
Applying polls.0001_initial... OK
...\> py manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Rendering model states... DONE
Applying polls.0001_initial... OK
Kommandot migrate
tar alla migreringar som inte har tillämpats (Django spårar vilka som tillämpas med hjälp av en speciell tabell i din databas som heter django_migrations
) och kör dem mot din databas - i huvudsak synkroniserar de ändringar du gjorde i dina modeller med schemat i databasen.
Migreringar är mycket kraftfulla och låter dig ändra dina modeller över tid, när du utvecklar ditt projekt, utan att du behöver ta bort din databas eller tabeller och skapa nya - det är specialiserat på att uppgradera din databas live, utan att förlora data. Vi kommer att gå igenom dem mer ingående i en senare del av handledningen, men för tillfället bör du komma ihåg trestegsguiden för att göra modelländringar:
Ändra dina modeller (i
models.py
).Kör
python manage.py makemigrations
för att skapa migreringar för dessa ändringarKör
python manage.py migrate
för att tillämpa dessa ändringar i databasen.
Anledningen till att det finns separata kommandon för att skapa och tillämpa migreringar är att du ska överföra migreringar till ditt versionshanteringssystem och skicka dem med din app; de gör inte bara din utveckling enklare, de kan också användas av andra utvecklare och i produktionen.
Läs django-admin-dokumentationen för fullständig information om vad verktyget manage.py
kan göra.
Leka med API:et¶
Låt oss nu hoppa in i det interaktiva Python-skalet och leka med det kostnadsfria API som Django ger dig. För att anropa Python-skalet använder du det här kommandot:
$ python manage.py shell
...\> py manage.py shell
Vi använder detta i stället för att helt enkelt skriva ”python”, eftersom manage.py
ställer in miljövariabeln DJANGO_SETTINGS_MODULE
, som ger Django Python-importsökvägen till din mysite/settings.py
-fil. Som standard importerar kommandot shell
automatiskt modellerna från din INSTALLED_APPS
.
När du väl är inne i skalet kan du utforska databas-API:
# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>
# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
# Save the object into the database. You have to call save() explicitly.
>>> q.save()
# Now it has an ID.
>>> q.id
1
# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=datetime.timezone.utc)
# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()
# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
Vänta lite. <Fråga: Fråga objekt (1)>`
är inte en användbar representation av detta objekt. Låt oss fixa det genom att redigera modellen Question
(i filen polls/models.py
) och lägga till en __str__()
-metod till både Question
och Choice
:
polls/modeller.py
¶from django.db import models
class Question(models.Model):
# ...
def __str__(self):
return self.question_text
class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text
Det är viktigt att lägga till __str__()
-metoder till dina modeller, inte bara för din egen bekvämlighet när du hanterar den interaktiva prompten, utan också för att objektens representationer används i Djangos automatiskt genererade admin.
Låt oss också lägga till en anpassad metod till den här modellen:
polls/modeller.py
¶import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
Notera tillägget av import datetime
och from django.utils import timezone
, för att referera till Pythons standardmodul datetime
respektive Djangos tidszonsrelaterade verktyg i django.utils.timezone
. Om du inte är bekant med tidszonshantering i Python kan du lära dig mer i time zone support docs.
Spara ändringarna och starta ett nytt interaktivt Python-skal genom att köra python manage.py shell
igen:
# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith="What")
<QuerySet [<Question: What's up?>]>
# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>
# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Question matching query does not exist.
# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>
# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set (defined as "choice_set") to hold the "other side" of a ForeignKey
# relation (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)
# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>
# Create three choices.
>>> q.choice_set.create(choice_text="Not much", votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text="The sky", votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text="Just hacking again", votes=0)
# Choice objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>
# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith="Just hacking")
>>> c.delete()
För mer information om modellrelationer, se Accessing related objects. Mer information om hur du använder dubbla understrykningstecken för att göra fältuppslagningar via API:et finns i Field lookups. För fullständig information om databas-API:et, se vår Database API reference.
Introduktion till Django Admin¶
Filosofi
Att skapa administratörswebbplatser för din personal eller dina kunder för att lägga till, ändra och ta bort innehåll är ett tråkigt arbete som inte kräver mycket kreativitet. Av den anledningen automatiserar Django helt skapandet av administratörsgränssnitt för modeller.
Django skrevs i en nyhetsrumsmiljö, med en mycket tydlig åtskillnad mellan ”innehållsutgivare” och den ”offentliga” webbplatsen. Webbplatsadministratörer använder systemet för att lägga till nyheter, händelser, sportresultat etc., och det innehållet visas på den offentliga webbplatsen. Django löser problemet med att skapa ett enhetligt gränssnitt för webbplatsadministratörer för att redigera innehåll.
Administratören är inte avsedd att användas av webbplatsbesökare. Den är till för webbplatsansvariga.
Skapa en administratörsanvändare¶
Först måste vi skapa en användare som kan logga in på administratörssidan. Kör följande kommando:
$ python manage.py createsuperuser
...\> py manage.py createsuperuser
Ange önskat användarnamn och tryck på Enter.
Username: admin
Du kommer sedan att bli ombedd att ange önskad e-postadress:
Email address: admin@example.com
Det sista steget är att ange ditt lösenord. Du kommer att bli ombedd att ange ditt lösenord två gånger, den andra gången som en bekräftelse på den första.
Password: **********
Password (again): *********
Superuser created successfully.
Starta utvecklingsservern¶
Django-administratörssidan är aktiverad som standard. Låt oss starta utvecklingsservern och utforska den.
Om servern inte är igång startar du den på följande sätt:
$ python manage.py runserver
...\> py manage.py runserver
Öppna nu en webbläsare och gå till ”/admin/” på din lokala domän, t.ex. http://127.0.0.1:8000/admin/. Du bör se administratörens inloggningsskärm:

Eftersom translation är aktiverad som standard, om du anger LANGUAGE_CODE
, kommer inloggningsskärmen att visas på det angivna språket (om Django har lämpliga översättningar).
Ange administratörens webbplats¶
Försök nu att logga in med det superanvändarkonto som du skapade i föregående steg. Du bör se Django-administratörens indexsida:

Du bör se några typer av redigerbart innehåll: grupper och användare. De tillhandahålls av django.contrib.auth
, det autentiseringsramverk som levereras av Django.
Gör röstningsappen ändringsbar i administratören¶
Men var är vår poll-app? Den visas inte på adminindexsidan.
Det är bara en sak kvar att göra: vi måste tala om för administratören att Question
-objekt har ett administratörsgränssnitt. För att göra detta, öppna filen polls/admin.py
och redigera den så att den ser ut så här:
polls/admin.py
¶from django.contrib import admin
from .models import Question
admin.site.register(Question)
Utforska de kostnadsfria adminfunktionerna¶
Nu när vi har registrerat Question
vet Django att den ska visas på adminindexsidan:

Klicka på ”Frågor”. Nu är du på sidan ”Ändra lista” för frågor. På den här sidan visas alla frågor i databasen och du kan välja en fråga för att ändra den. Det finns frågan ”Vad händer?” som vi skapade tidigare:

Klicka på frågan ”Vad händer?” för att redigera den:

Saker att notera här:
Formuläret genereras automatiskt från modellen
Question
.De olika modellfälttyperna (
DateTimeField
,CharField
) motsvarar lämplig HTML-inmatningswidget. Varje fälttyp vet hur den ska visas i Django-admin.Varje
DateTimeField
får gratis JavaScript-genvägar. Datum får en ”Today”-genväg och en popup-kalender, och tider får en ”Now”-genväg och en praktisk popup som listar vanliga inmatade tider.
Längst ner på sidan finns ett par alternativ:
Save – Sparar ändringar och återgår till sidan med ändringslistan för den här typen av objekt.
Spara och fortsätt redigera – Sparar ändringar och laddar om adminsidan för det här objektet.
Save and add another – Sparar ändringar och laddar ett nytt, tomt formulär för den här typen av objekt.
Delete – Visar en bekräftelsesida för radering.
Om värdet för ”Date published” inte stämmer överens med den tidpunkt då du skapade frågan i Tutorial 1, betyder det förmodligen att du har glömt att ange rätt värde för inställningen TIME_ZONE
. Ändra den, ladda om sidan och kontrollera att rätt värde visas.
Ändra ”Datum publicerat” genom att klicka på genvägarna ”Idag” och ”Nu”. Klicka sedan på ”Spara och fortsätt redigera” Klicka sedan på ”Historik” längst upp till höger. Du kommer att se en sida som listar alla ändringar som gjorts i detta objekt via Django-admin, med tidsstämpel och användarnamn för den person som gjorde ändringen:

När du känner dig bekväm med API:et för modeller och har bekantat dig med adminwebbplatsen kan du läsa del 3 av denna handledning för att lära dig hur du lägger till fler vyer i vår polls-app.