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.

Maak je eerste model

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 een ForeignKey die een relatie weergeeft met een ander model / tabel, in dit geval Author (alleen gebruikt voor voorbeelddoeleinden). on_delete vertelt de database wat te doen met het object als het gerelateerde object (een Author ) wordt verwijderd. (Opgemerkt moet worden dat aangezien django 1.9 on_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 naar CASCADE .)
  • publish_date slaat een datum op. Zowel null als blank zijn ingesteld op True 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 als JoinColumn , Intersection table of mapping 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:

  1. 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)
    
  2. 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()
    
  3. 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.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow