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

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 en ForeignKey som representerar ett förhållande till en annan modell / tabell, i detta fall Author (används endast till exempel). on_delete berättar databasen vad man ska göra med objektet om det relaterade objektet (en Author ) ska raderas. (Det bör noteras att eftersom django 1.9 on_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 som CASCADE .)
  • publish_date lagrar ett datum. Både null och blank ä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 en JoinColumn , Intersection table eller mapping 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:

  1. 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)
    
  2. Skapa en instans av modellen och spara den i databasen:

    >>> himu_book = Book(name='Himu Mama', author='Humayun Ahmed')
    >>> himu_book.save()
    
  3. 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.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow