Django
modellen
Zoeken…
Invoering
In het basisgeval is een model van de Python-klasse die wordt toegewezen aan een enkele databasetabel. De kenmerken van de klassenmap voor kolommen in de tabel en een instantie van de klasse vertegenwoordigen een rij in de databasetabel. De modellen nemen over van django.db.models.Model
dat een rijke API biedt voor het toevoegen en filteren van resultaten uit de database.
Uw eerste model maken
Modellen worden meestal gedefinieerd in het bestand models.py
in de models.py
van uw toepassing. De Model
klasse van django.db.models
module is een goed startpunt klasse aan uw modellen uit te breiden uit. Bijvoorbeeld:
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
Elk kenmerk in een model vertegenwoordigt een kolom in de database.
-
title
is een tekst met een maximale lengte van 100 tekens -
author
is eenForeignKey
die een relatie weergeeft met een ander model / tabel, in dit gevalAuthor
(alleen gebruikt voor voorbeelddoeleinden).on_delete
vertelt de database wat te doen met het object als het gerelateerde object (eenAuthor
) wordt verwijderd. (Opgemerkt moet worden dat aangezien django 1.9on_delete
kan worden gebruikt als het tweede positionele argument. In django 2 is het een verplicht argument en het is raadzaam om het onmiddellijk als zodanig te behandelen. In oudere versies zal het standaard naarCASCADE
.) -
publish_date
slaat een datum op. Zowelnull
alsblank
zijn ingesteld opTrue
om aan te geven dat dit geen verplicht veld is (dat wil zeggen dat u het op een later tijdstip kunt toevoegen of leeg kunt laten).
Samen met de attributen definiëren we een methode __str__
deze retourneert de titel van het boek, die indien nodig als string
zal worden gebruikt, in plaats van de standaard.
Wijzigingen toepassen op de database (migraties)
Nadat u een nieuw model hebt gemaakt of bestaande modellen hebt gewijzigd, moet u migraties genereren voor uw wijzigingen en vervolgens de migraties toepassen op de opgegeven database. Dit kan worden gedaan met behulp van het ingebouwde migratiesysteem van Django. manage.py
hulpprogramma manage.py
gebruiken in de hoofdmap van het project:
python manage.py makemigrations <appname>
De bovenstaande opdracht maakt de migratiescripts die nodig zijn in de submappen voor migrations
van uw toepassing. Als u de parameter <appname>
, worden alle toepassingen verwerkt die zijn gedefinieerd in het argument INSTALLED_APPS
van settings.py
. Als u het nodig vindt, kunt u de migraties bewerken.
U kunt controleren welke migraties nodig zijn zonder de migratie daadwerkelijk te creëren met behulp van de optie --dry-run, bijvoorbeeld:
python manage.py makemigrations --dry-run
Om de migraties toe te passen:
python manage.py migrate <appname>
De bovenstaande opdracht voert de migratiescripts uit die in de eerste stap zijn gegenereerd en werkt de database fysiek bij.
Als het model van de bestaande database wordt gewijzigd, is het volgende commando nodig om de nodige wijzigingen aan te brengen.
python manage.py migrate --run-syncdb
Django maakt standaard de tabel met de naam <appname>_<classname>
. Soms wil je het niet gebruiken. Als u de standaardnaam wilt wijzigen, kunt u de tabelnaam aankondigen door de db_table
in te db_table
in de klasse Meta
:
from django.db import models
class YourModel(models.Model):
parms = models.CharField()
class Meta:
db_table = "custom_table_name"
Als u wilt zien welke SQL-code wordt uitgevoerd door een bepaalde migratie, voert u deze opdracht uit:
python manage.py sqlmigrate <app_label> <migration_number>
Django> 1.10
Met de nieuwe optie makemigrations --check
wordt de opdracht makemigrations --check
met een niet- makemigrations --check
wanneer modelwijzigingen zonder migraties worden gedetecteerd.
Zie Migraties voor meer informatie over migraties.
Een model met relaties maken
Veel-op-een-relatie
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()
Meest generieke optie. Kan overal worden gebruikt waar u een relatie wilt vertegenwoordigen
Veel-op-veel-relatie
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)
Intern wordt dit weergegeven via een andere tabel. En ManyToManyField
moet worden geplaatst op modellen die op een formulier worden bewerkt. Bijv .: Appointment
heeft een ManyToManyField
genaamd Customer
, Pizza
heeft Toppings
enzovoort.
Veel-op-veel-relatie met behulp van klassen
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)
Op deze manier kunnen we eigenlijk meer metadata bijhouden over een relatie tussen twee entiteiten. Zoals te zien is, kan een klant op verschillende services worden geabonneerd via verschillende abonnementstypen. Het enige verschil in dit geval is dat om nieuwe instanties aan de M2M-relatie toe te voegen, de sneltoetsmethode pizza.toppings.add(topping)
kan worden gebruikt. In plaats daarvan moet een nieuw object van de doorgaande klasse worden gemaakt, Subscription.objects.create(client=client, service=service, subscription_type='p')
In andere talen staan
through tables
ook bekend alsJoinColumn
,Intersection table
ofmapping table
Eén op één relatie
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)
Gebruik deze velden wanneer u alleen een compositierelatie tussen de twee modellen zult hebben.
Basic Django DB-zoekopdrachten
Django ORM is een krachtige abstractie waarmee u gegevens uit de database kunt opslaan en ophalen zonder zelf sql-vragen te schrijven.
Laten we uitgaan van de volgende modellen:
class Author(models.Model):
name = models.CharField(max_length=50)
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.ForeignKey(Author)
Ervan uitgaande dat u de bovenstaande code aan een django-toepassing hebt toegevoegd en de migrate
uitvoert (zodat uw database wordt gemaakt). Start de Django shell door
python manage.py shell
Dit begint met de standaard python-shell, maar met relevante geïmporteerde Django-bibliotheken, zodat u zich direct kunt concentreren op de belangrijke onderdelen.
Begin met het importeren van de modellen die we zojuist hebben gedefinieerd (ik models.py
dat dit gebeurt in een bestand models.py
)
from .models import Book, Author
Voer uw eerste geselecteerde zoekopdracht uit:
>>> Author.objects.all()
[]
>>> Book.objects.all()
[]
Laten we een auteur en een boekobject maken:
>>> hawking = Author(name="Stephen hawking")
>>> hawking.save()
>>> history_of_time = Book(name="history of time", author=hawking)
>>> history_of_time.save()
of gebruik de functie create om modelobjecten te maken en op te slaan in één regelcode
>>> wings_of_fire = Book.objects.create(name="Wings of Fire", author="APJ Abdul Kalam")
Laten we nu de query uitvoeren
>>> Book.objects.all()
[<Book: Book object>]
>>> book = Book.objects.first() #getting the first book object
>>> book.name
u'history of time'
Laten we een Where-clausule toevoegen aan onze geselecteerde zoekopdracht
>>> Book.objects.filter(name='nothing')
[]
>>> Author.objects.filter(name__startswith='Ste')
[<Author: Author object>]
Voor meer informatie over de auteur van een bepaald boek
>>> book = Book.objects.first() #getting the first book object
>>> book.author.name # lookup on related model
u'Stephen hawking'
Om alle boeken gepubliceerd door Stephen Hawking te krijgen (Lookup-boek van de auteur)
>>> hawking.book_set.all()
[<Book: Book object>]
_set
is de notatie die wordt gebruikt voor "reverse lookups", dwz terwijl het opzoekveld zich op het book_set
, kunnen we book_set
op een book_set
gebruiken om al zijn / haar boeken te krijgen.
Een eenvoudige onbeheerde tafel.
Op een bepaald moment in je gebruik van Django, wil je misschien dat je wilt interageren met tabellen die al zijn gemaakt, of met databaseweergaven. In deze gevallen wilt u niet dat Django de tabellen beheert via zijn migraties. Om dit in te stellen, hoeft u slechts één variabele toe te voegen aan de Meta
klasse van uw model: managed = False
.
Hier is een voorbeeld van hoe u een onbeheerd model kunt maken voor interactie met een databaseweergave:
class Dummy(models.Model):
something = models.IntegerField()
class Meta:
managed = False
Dit kan als volgt worden toegewezen aan een weergave die in SQL is gedefinieerd.
CREATE VIEW myapp_dummy AS
SELECT id, something FROM complicated_table
WHERE some_complicated_condition = True
Nadat u dit model heeft gemaakt, kunt u het gebruiken zoals u elk ander model zou doen:
>>> Dummy.objects.all()
[<Dummy: Dummy object>, <Dummy: Dummy object>, <Dummy: Dummy object>]
>>> Dummy.objects.filter(something=42)
[<Dummy: Dummy object>]
Geavanceerde modellen
Een model kan veel meer informatie bieden dan alleen de gegevens over een object. Laten we een voorbeeld bekijken en het opsplitsen in wat het nuttig is:
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']
Automatische primaire sleutel
Je merkt misschien het gebruik van self.pk
in de methode get_absolute_url
. Het pk
veld is een alias voor de primaire sleutel van een model. Ook zal django automatisch een primaire sleutel toevoegen als deze ontbreekt. Dat is één ding minder om u zorgen over te maken en u de mogelijkheid te geven om een externe sleutel voor alle modellen in te stellen en ze gemakkelijk te krijgen.
Absolute url
De eerste functie die wordt gedefinieerd is get_absolute_url
. Op deze manier, als je een boek hebt, kun je er een link naar krijgen zonder te lummelen met de url-tag, oplossen, kenmerk en dergelijke. Bel eenvoudig book.get_absolute_url
en u krijgt de juiste link. Als bonus krijgt uw object in de django-admin een knop "weergave op site".
Stringvoorstelling
Met een __str__
methode kunt u het object gebruiken wanneer u het wilt weergeven. Met de vorige methode is het toevoegen van een link naar het boek in een sjabloon bijvoorbeeld zo eenvoudig als <a href="{{ book.get_absolute_url }}">{{ book }}</a>
. Direct ter zake. Deze methode bepaalt ook wat wordt weergegeven in de vervolgkeuzelijst voor beheerders, bijvoorbeeld voor een externe sleutel.
Met de __str__
kunt u de methode eenmaal definiëren voor zowel __str__
als __unicode__
op python 2, terwijl u geen problemen veroorzaakt met python 3. Als u verwacht dat uw app op beide versies draait, is dat de beste keuze.
Slakkenveld
Het slakkenveld is vergelijkbaar met een tekenveld, maar accepteert minder symbolen. Standaard alleen letters, cijfers, onderstrepingstekens of koppeltekens. Dit is handig als u een object wilt identificeren met een mooie weergave, bijvoorbeeld in URL.
De Meta-klasse
Met de Meta
klasse kunnen we veel meer informatie definiëren over de hele verzameling items. Hier is alleen de standaardvolgorde ingesteld. Dit is bijvoorbeeld handig bij het ListView-object. Er is een ideale korte lijst met velden nodig om te sorteren. Hier wordt het boek eerst gesorteerd op publicatiedatum en vervolgens op titel als de datum hetzelfde is.
Andere frequentiekenmerken zijn verbose_name
en verbose_name_plural
. Standaard worden ze gegenereerd op basis van de naam van het model en zouden in orde moeten zijn. Maar de meervoudsvorm is naïef en voegt eenvoudig een 's' toe aan het enkelvoud, dus misschien wilt u deze in een bepaald geval expliciet instellen.
Berekende waarden
Nadat een modelobject is opgehaald, wordt het een volledig gerealiseerd exemplaar van de klasse. Als zodanig zijn extra methoden toegankelijk in formulieren en serializers (zoals Django Rest Framework).
Het gebruik van python-eigenschappen is een elegante manier om extra waarden weer te geven die niet zijn opgeslagen in de database vanwege wisselende omstandigheden.
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
Hoewel u in de meeste gevallen gegevens kunt aanvullen met annotaties op uw querysets, zijn berekende waarden als modeleigenschappen ideaal voor berekeningen die niet eenvoudigweg binnen het bereik van een query kunnen worden geëvalueerd.
Bovendien zijn eigenschappen, omdat ze worden gedeclareerd in de python-klasse en niet als onderdeel van het schema, niet beschikbaar om te ondervragen.
Een stringvoorstelling van een model toevoegen
Om een voor mensen leesbare presentatie van een Model.__str__()
moet u de methode Model.__str__()
(of Model.__unicode__()
op python2 implementeren). Deze methode wordt aangeroepen wanneer u str()
aanroept in een exemplaar van uw model (bijvoorbeeld wanneer het model in een sjabloon wordt gebruikt). Hier is een voorbeeld:
Maak een boekmodel.
# 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)
Maak een exemplaar van het model en sla het op in de database:
>>> himu_book = Book(name='Himu Mama', author='Humayun Ahmed') >>> himu_book.save()
Voer
print()
op het exemplaar:>>> print(himu_book) <Book: Book object>
<Boek: boekobject> , de standaarduitvoer, helpt ons niet. Om dit op te lossen, laten we een __str__
methode toevoegen.
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)
Let op: de decorateur python_2_unicode_compatible
is alleen nodig als u wilt dat uw code compatibel is met python 2. Deze decorateur kopieert de __str__
methode om een __unicode__
__str__
methode te maken. Importeer het uit django.utils.encoding
.
Als we de printfunctie nu opnieuw de boekinstantie noemen:
>>> print(himu_book)
Himu Mama by Humayun Ahmed
Veel beter!
De tekenreeksrepresentatie wordt ook gebruikt wanneer het model wordt gebruikt in een ModelForm
voor de velden ForeignKeyField
en ManyToManyField
.
Model mixins
In dezelfde gevallen kunnen verschillende modellen dezelfde velden en dezelfde procedures in de levenscyclus van het product hebben. Om deze overeenkomsten te verwerken zonder codeherhaling kan overerving worden gebruikt. In plaats van een hele klasse te erven, biedt het mixin- ontwerppatroon ons de mogelijkheid om sommige methoden en attributen te erven ( of volgens sommigen inbegrepen ). Laten we een voorbeeld bekijken:
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)
Om van een model een abstracte klasse te maken, moet u abstract=True
in de innerlijke Meta
klasse. Django maakt geen tabellen voor abstracte modellen in de database. Voor de modellen Envelope
en Package
zouden echter overeenkomstige tabellen in de database worden gemaakt.
Verder zijn op de velden enkele modelmethoden nodig bij meer dan één model. Deze methoden kunnen dus worden toegevoegd aan mixins om herhaling van code te voorkomen. Als we bijvoorbeeld een methode maken om de leverdatum in te stellen op PostableMixin
, is deze toegankelijk voor beide kinderen:
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()
Deze methode kan als volgt worden gebruikt bij de kinderen:
>> envelope = Envelope.objects.get(pk=1)
>> envelope.set_delivery_datetime()
>> pack = Package.objects.get(pk=1)
>> pack.set_delivery_datetime()
UUID primaire sleutel
Een model gebruikt standaard een automatische verhogende (geheel getal) primaire sleutel. Dit geeft u een reeks toetsen 1, 2, 3.
Verschillende primaire sleuteltypen kunnen worden ingesteld op een model met kleine aanpassingen aan het model.
Een UUID is een universeel unieke identificatie, dit is een willekeurige identificatie van 32 tekens die als een ID kan worden gebruikt. Dit is een goede optie om te gebruiken als u niet wilt dat opeenvolgende ID's worden toegewezen aan records in uw database. Wanneer gebruikt op PostgreSQL, wordt dit opgeslagen in een uuid datatype, anders in een char (32).
import uuid
from django.db import models
class ModelUsingUUID(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
De gegenereerde sleutel heeft de indeling 7778c552-73fc-4bc4-8bf9-5a2f6f7b7f47
Erfenis
Overerving tussen modellen kan op twee manieren worden gedaan:
- een veel voorkomende abstracte klasse (zie het voorbeeld "Modelmixins")
- een gemeenschappelijk model met meerdere tabellen
De overerving van meerdere tabellen maakt één tabel voor de gemeenschappelijke velden en één voorbeeld per kindmodel:
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)
maakt 2 tabellen, een voor Place
en een voor Restaurant
met een verborgen OneToOne
veld naar Place
voor de gemeenschappelijke velden.
merk op dat dit elke keer dat u een restaurantobject ophaalt een extra zoekopdracht naar de plaatstabellen nodig heeft.