Django
modeller
Sök…
Introduktion
I grundfallet är en modell Python-klass som kartlägger till en enda databastabell. Klassens kartattribut till kolumner i tabellen och en instans av klassen representerar en rad i databastabellen. Modellerna ärver från django.db.models.Model
som ger ett rikt API för att lägga till och filtrera resultat från databasen.
Skapa din första modell
Modeller definieras vanligtvis i filen models.py
under din underkatalog för applikationer. Model
klassen django.db.models
modulen är en bra startklass att utöka dina modeller från. Till exempel:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey('Author', on_delete=models.CASCADE, related_name='authored_books')
publish_date = models.DateField(null=True, blank=True)
def __str__(self): # __unicode__ in python 2.*
return self.title
Varje attribut i en modell representerar en kolumn i databasen.
-
title
är en text med en maximal längd på 100 tecken -
author
är enForeignKey
som representerar ett förhållande till en annan modell / tabell, i detta fallAuthor
(används endast till exempel).on_delete
berättar databasen vad man ska göra med objektet om det relaterade objektet (enAuthor
) ska raderas. (Det bör noteras att eftersom django 1.9on_delete
kan användas som det andra positionsargumentet. I django 2 är det ett nödvändigt argument och det är tillrådligt att behandla det som sådant omedelbart. I äldre versioner kommer det att fungera somCASCADE
.) -
publish_date
lagrar ett datum. Bådenull
ochblank
är inställt påTrue
att indikera att det inte är ett obligatoriskt fält (dvs. du kan lägga till det vid ett senare tillfälle eller lämna det tomt.)
Tillsammans med attributen definierar vi en metod __str__
detta returnerar titeln på boken som kommer att användas som string
behov, snarare än standard.
Tillämpa ändringarna i databasen (Migrations)
När du har skapat en ny modell eller modifierat befintliga modeller måste du generera migreringar för dina ändringar och sedan tillämpa migreringarna i den angivna databasen. Detta kan göras genom att använda Djangos inbyggda migrationssystem. Använda verktyget manage.py
när du är i projektets rotkatalog:
python manage.py makemigrations <appname>
Ovanstående kommando skapar migrationsskript som är nödvändiga under migrations
underkatalog för din applikation. Om du utelämnar parametern <appname>
kommer alla applikationer som definieras i INSTALLED_APPS
argumentet för settings.py
att behandlas. Om du finner det nödvändigt kan du redigera migreringarna.
Du kan kontrollera vilka migreringar som krävs utan att faktiskt skapa migreringen genom att använda alternativet - torka-körning, t.ex.
python manage.py makemigrations --dry-run
Så här använder du migreringarna:
python manage.py migrate <appname>
Ovanstående kommando kommer att köra migrationsskript som genererats i det första steget och fysiskt uppdatera databasen.
Om modellen för befintlig databas ändras krävs följande kommando för att göra nödvändiga ändringar.
python manage.py migrate --run-syncdb
Django skapar som standard tabellen med namnet <appname>_<classname>
. Någon gång vill du inte använda det. Om du vill ändra standardnamnet kan du meddela db_table
genom att ställa in db_table
i klassen Meta
:
from django.db import models
class YourModel(models.Model):
parms = models.CharField()
class Meta:
db_table = "custom_table_name"
Om du vill se vilken SQL-kod som kommer att köras med en viss migrering, kör bara det här kommandot:
python manage.py sqlmigrate <app_label> <migration_number>
Django> 1.10
Det nya makemigrations --check
gör att kommandot avslutas med en status som inte är noll när modelländringar utan migrationer upptäcks.
Se Migreringar för mer information om migreringar.
Skapa en modell med relationer
Många-till-en-relation
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=50)
#Book has a foreignkey (many to one) relationship with author
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
publish_date = models.DateField()
Mest generiskt alternativ. Kan användas var du än vill representera en relation
Förhållande mellan många och många
class Topping(models.Model):
name = models.CharField(max_length=50)
# One pizza can have many toppings and same topping can be on many pizzas
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
Internt representeras detta via ett annat bord. Och ManyToManyField
bör läggas på modeller som kommer att redigeras på ett formulär. Exempel: Appointment
kommer att ha en ManyToManyField
heter Customer
, Pizza
har Toppings
och så vidare.
Förhållande mellan många och många med hjälp av klasser
class Service(models.Model):
name = models.CharField(max_length=35)
class Client(models.Model):
name = models.CharField(max_length=35)
age = models.IntegerField()
services = models.ManyToManyField(Service, through='Subscription')
class Subscription(models.Model):
client = models.ForeignKey(Client)
service = models.ForeignKey(Service)
subscription_type = models.CharField(max_length=1, choices=SUBSCRIPTION_TYPES)
created_at = models.DateTimeField(default=timezone.now)
På så sätt kan vi faktiskt behålla fler metadata om ett förhållande mellan två enheter. Som framgår kan en klient prenumereras på flera tjänster via flera prenumerationstyper. Den enda skillnaden i detta fall är att för att lägga till nya instanser till M2M förhållande, kan man inte använda snabbmetoden pizza.toppings.add(topping)
, i stället, ett nytt objekt av det genom klassen ska skapas, Subscription.objects.create(client=client, service=service, subscription_type='p')
På andra språk är
through tables
också kända som enJoinColumn
,Intersection table
ellermapping table
En-mot-en-relation
class Employee(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
spouse = models.OneToOneField(Spouse)
class Spouse(models.Model):
name = models.CharField(max_length=50)
Använd dessa fält när du bara någonsin kommer att ha ett kompositionsförhållande mellan de två modellerna.
Grundläggande Django DB-frågor
Django ORM är en kraftfull abstraktion som låter dig lagra och hämta data från databasen utan att själv skriva sqlfrågor.
Låt oss anta följande modeller:
class Author(models.Model):
name = models.CharField(max_length=50)
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.ForeignKey(Author)
Förutsatt att du har lagt till koden ovan till en django-applikation och kör migrate
kommandot (så att din databas skapas). Starta Django-skalet
python manage.py shell
Detta startar det vanliga pythonskalet men med relevanta Django-bibliotek importerade, så att du direkt kan fokusera på de viktiga delarna.
Börja med att importera de modeller vi just har definierat (jag antar att detta görs i en models.py
)
from .models import Book, Author
Kör din första utvalda fråga:
>>> Author.objects.all()
[]
>>> Book.objects.all()
[]
Låter skapa en författare och ett bokobjekt:
>>> hawking = Author(name="Stephen hawking")
>>> hawking.save()
>>> history_of_time = Book(name="history of time", author=hawking)
>>> history_of_time.save()
eller använd skapa- funktion för att skapa modellobjekt och spara i en radkod
>>> wings_of_fire = Book.objects.create(name="Wings of Fire", author="APJ Abdul Kalam")
Nu kan vi köra frågan
>>> Book.objects.all()
[<Book: Book object>]
>>> book = Book.objects.first() #getting the first book object
>>> book.name
u'history of time'
Låt oss lägga till en var-klausul till vår utvalda fråga
>>> Book.objects.filter(name='nothing')
[]
>>> Author.objects.filter(name__startswith='Ste')
[<Author: Author object>]
För att få information om författaren till en given bok
>>> book = Book.objects.first() #getting the first book object
>>> book.author.name # lookup on related model
u'Stephen hawking'
För att få alla böcker utgivna av Stephen Hawking (uppslagbok av författaren)
>>> hawking.book_set.all()
[<Book: Book object>]
_set
är notationen som används för "Omvända sökningar", dvs medan uppslagningsfältet finns på Book-modellen, kan vi använda book_set
på ett författarobjekt för att få alla hans / hennes böcker.
En grundläggande obestämd tabell.
Vid någon tidpunkt när du använder Django kan du finna att du vill interagera med tabeller som redan har skapats eller med databasvyer. I dessa fall vill du inte att Django ska hantera tabellerna genom dess migrering. För att konfigurera detta måste du bara lägga till en variabel i din modell Meta
: managed = False
.
Här är ett exempel på hur du kan skapa en okontrollerad modell för att interagera med en databasvy:
class Dummy(models.Model):
something = models.IntegerField()
class Meta:
managed = False
Detta kan mappas till en vy definierad i SQL enligt följande.
CREATE VIEW myapp_dummy AS
SELECT id, something FROM complicated_table
WHERE some_complicated_condition = True
När du har skapat den här modellen kan du använda den på samma sätt som alla andra modeller:
>>> Dummy.objects.all()
[<Dummy: Dummy object>, <Dummy: Dummy object>, <Dummy: Dummy object>]
>>> Dummy.objects.filter(something=42)
[<Dummy: Dummy object>]
Avancerade modeller
En modell kan ge mycket mer information än bara informationen om ett objekt. Låt oss se ett exempel och dela upp det till vad det är användbart för:
from django.db import models
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class Book(models.Model):
slug = models.SlugField()
title = models.CharField(max_length=128)
publish_date = models.DateField()
def get_absolute_url(self):
return reverse('library:book', kwargs={'pk':self.pk})
def __str__(self):
return self.title
class Meta:
ordering = ['publish_date', 'title']
Automatisk primärnyckel
Du kanske märker användningen av self.pk
i metoden get_absolute_url
. pk
fältet är ett alias till den primära nyckeln för en modell. Dessutom lägger django automatiskt till en primär nyckel om den saknas. Det är en mindre sak att oroa dig och låta dig ställa in utländsk nyckel till alla modeller och få dem enkelt.
Absolut url
Den första funktionen som definieras är get_absolute_url
. På det här sättet, om du har en bok, kan du få en länk till den utan att fikla med url-taggen, lösa, attribut och liknande. Ring bara book.get_absolute_url
så får du rätt länk. Som en bonus kommer ditt objekt i django-admin att få en knapp "Visa på webbplatsen".
Strängrepresentation
Med en __str__
metod kan du använda objektet när du behöver visa det. Till exempel, med den föregående metoden är det lika enkelt att lägga till en länk till boken i en mall som <a href="{{ book.get_absolute_url }}">{{ book }}</a>
. Rakt på sak. Denna metod styr också vad som visas i rullgardinsmenyn admin, till exempel för utländsk nyckel.
Klassdekoratören låter dig definiera metoden en gång för både __str__
och __unicode__
på python 2 och orsakar inga problem på python 3. Om du förväntar dig att din app ska köras på båda versionerna är det vägen att gå.
Snigelfält
Snigelfältet liknar ett kolfält men accepterar färre symboler. Som standard är det bara bokstäver, siffror, understreck eller bindestreck. Det är användbart om du vill identifiera ett objekt med hjälp av en fin representation, till exempel i url.
Metaklassen
Meta
låter oss definiera mycket mer information om hela varusamlingen. Här är bara standardbeställningen inställd. Det är användbart med ListView-objektet till exempel. Det tar en idealisk kort lista med fält att använda för sortering. Här kommer boken att sorteras först efter publiceringsdatum och sedan efter titel om datumet är detsamma.
Andra verbose_name
är verbose_name
och verbose_name_plural
. Som standard genereras de från modellens namn och borde vara bra. Men pluralformen är naiv, bara lägg till ett 's' på singularet så du kanske vill ställa det uttryckligen i något fall.
Beräknade värden
När ett modellobjekt har hämtats, blir det en fullt realiserad instans av klassen. Som sådant kan alla ytterligare metoder nås i formulär och serienummer (som Django Rest Framework).
Att använda pythonegenskaper är ett elegant sätt att representera ytterligare värden som inte lagras i databasen på grund av olika omständigheter.
def expire():
return timezone.now() + timezone.timedelta(days=7)
class Coupon(models.Model):
expiration_date = models.DateField(default=expire)
@property
def is_expired(self):
return timezone.now() > self.expiration_date
Även om du i de flesta fall kan komplettera data med anteckningar på dina frågeställningar, är beräknade värden som modellegenskaper idealiska för beräkningar som inte kan utvärderas helt enkelt inom ramen för en fråga.
Dessutom är egenskaper, eftersom de deklareras i pytonklassen och inte som en del av schemat, inte tillgängliga att fråga mot.
Lägga till en strängrepresentation av en modell
För att skapa en läsbar presentation av ett modellobjekt måste du implementera Model.__str__()
-metoden (eller Model.__unicode__()
på python2). Den här metoden kommer att kallas när du ringer str()
på en instans av din modell (inklusive till exempel när modellen används i en mall). Här är ett exempel:
Skapa en bokmodell.
# your_app/models.py from django.db import models class Book(models.Model): name = models.CharField(max_length=50) author = models.CharField(max_length=50)
Skapa en instans av modellen och spara den i databasen:
>>> himu_book = Book(name='Himu Mama', author='Humayun Ahmed') >>> himu_book.save()
Kör
print()
på förekomsten:>>> print(himu_book) <Book: Book object>
<Bok: Bokobjekt> , standardutdata, hjälper oss inte. För att fixa detta, låt oss lägga till en __str__
metod.
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.CharField(max_length=50)
def __str__(self):
return '{} by {}'.format(self.name, self.author)
Observera att python_2_unicode_compatible
dekoratören endast behövs om du vill att din kod ska vara kompatibel med python 2. Denna dekoratör kopierar __str__
metoden för att skapa en __unicode__
metod. Importera det från django.utils.encoding
.
Om vi nu kallar utskriftsfunktionen kommer bokinstansen igen:
>>> print(himu_book)
Himu Mama by Humayun Ahmed
Mycket bättre!
Strängrepresentationen används också när modellen används i ModelForm
för ForeignKeyField
och ManyToManyField
.
Model mixins
I samma fall kan olika modeller ha samma fält och samma procedurer i produktens livscykel. För att hantera dessa likheter utan att ha arv från kodupprepning kan man använda. Istället för att ärva en hel klass erbjuder mixin designmönster oss att ärva ( eller vissa säger inkludera ) vissa metoder och attribut. Låt oss se ett exempel:
class PostableMixin(models.Model):
class Meta:
abstract=True
sender_name = models.CharField(max_length=128)
sender_address = models.CharField(max_length=255)
receiver_name = models.CharField(max_length=128)
receiver_address = models.CharField(max_length=255)
post_datetime = models.DateTimeField(auto_now_add=True)
delivery_datetime = models.DateTimeField(null=True)
notes = models.TextField(max_length=500)
class Envelope(PostableMixin):
ENVELOPE_COMMERCIAL = 1
ENVELOPE_BOOKLET = 2
ENVELOPE_CATALOG = 3
ENVELOPE_TYPES = (
(ENVELOPE_COMMERCIAL, 'Commercial'),
(ENVELOPE_BOOKLET, 'Booklet'),
(ENVELOPE_CATALOG, 'Catalog'),
)
envelope_type = models.PositiveSmallIntegerField(choices=ENVELOPE_TYPES)
class Package(PostableMixin):
weight = models.DecimalField(max_digits=6, decimal_places=2)
width = models.DecimalField(max_digits=5, decimal_places=2)
height = models.DecimalField(max_digits=5, decimal_places=2)
depth = models.DecimalField(max_digits=5, decimal_places=2)
För att göra en modell till en abstrakt klass måste du nämna abstract=True
i dess inre Meta
. Django skapar inga tabeller för abstrakta modeller i databasen. Men för modellerna Envelope
och Package
skulle motsvarande tabeller skapas i databasen.
Vidare kommer fälten att behöva vissa modellmetoder på mer än en modell. Således kan dessa metoder läggas till mixins för att förhindra kodrepetition. Om vi till exempel skapar en metod för att ställa in leveransdatum till PostableMixin
kommer det att vara tillgängligt från båda dess barn:
class PostableMixin(models.Model):
class Meta:
abstract=True
...
...
def set_delivery_datetime(self, dt=None):
if dt is None:
from django.utils.timezone import now
dt = now()
self.delivery_datetime = dt
self.save()
Denna metod kan användas som följande på barnen:
>> envelope = Envelope.objects.get(pk=1)
>> envelope.set_delivery_datetime()
>> pack = Package.objects.get(pk=1)
>> pack.set_delivery_datetime()
UUID Primär nyckel
En modell kommer som standard att använda en primärnyckel för automatisk inkrementering (heltal). Detta ger dig en sekvens av tangenterna 1, 2, 3.
Olika primära nyckeltyper kan ställas in på en modell med små ändringar av modellen.
En UUID är en universellt unik identifierare, detta är 32 karaktärs slumpmässiga identifierare som kan användas som ID. Detta är ett bra alternativ att använda när du inte vill att sekvens-ID ska tilldelas poster i din databas. När den används på PostgreSQL lagras detta i en udat datatyp, annars i en char (32).
import uuid
from django.db import models
class ModelUsingUUID(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Den genererade nyckeln kommer att vara i formatet 7778c552-73fc-4bc4-8bf9-5a2f6f7b7f47
Arv
Arv mellan modeller kan göras på två sätt:
- en vanlig abstrakt klass (se exempel "Model mixins")
- en vanlig modell med flera tabeller
Arvet med flera tabeller skapar en tabell för de vanliga fälten och ett exempel per barnmodell:
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)
kommer att skapa 2 bord, en för Place
och en för Restaurant
med en dold OneToOne
fältet Place
för de gemensamma områdena.
Observera att detta kommer att behöva en extra fråga till platstabellerna varje gång du hämtar ett restaurangobjekt.