Ricerca…


introduzione

Nel caso base, un modello è una classe Python che esegue il mapping su una singola tabella di database. Gli attributi della mappa della classe alle colonne nella tabella e un'istanza della classe rappresentano una riga nella tabella del database. I modelli ereditano da django.db.models.Model che fornisce una ricca API per aggiungere e filtrare i risultati dal database.

Crea il tuo primo modello

Creare il tuo primo modello

I modelli sono in genere definiti nel file models.py nella sottodirectory dell'applicazione. La classe Model del modulo django.db.models è una buona classe iniziale per estendere i tuoi modelli. Per esempio:

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

Ogni attributo in un modello rappresenta una colonna nel database.

  • title è un testo con una lunghezza massima di 100 caratteri
  • author è un ForeignKey che rappresenta una relazione con un altro modello / tabella, in questo caso Author (usato solo per scopi di esempio). on_delete dice al database cosa fare con l'oggetto se l'oggetto correlato (un Author ) viene cancellato. (Va notato che dal momento che django 1.9 on_delete può essere utilizzato come secondo argomento posizionale, in django 2 è un argomento obbligatorio ed è consigliabile trattarlo come tale immediatamente. Nelle versioni precedenti verrà on_delete come predefinito su CASCADE .)
  • publish_date memorizza una data. Sia null che blank sono impostati su True per indicare che non si tratta di un campo obbligatorio (ovvero è possibile aggiungerlo in un secondo momento o lasciarlo vuoto).

Insieme agli attributi definiamo un metodo __str__ questo restituisce il titolo del libro che verrà usato come rappresentazione di string dove necessario, piuttosto che come predefinito.

Applicazione delle modifiche al database (Migrazioni)

Dopo aver creato un nuovo modello o modificato i modelli esistenti, sarà necessario generare migrazioni per le modifiche e quindi applicare le migrazioni al database specificato. Questo può essere fatto usando il sistema di migrazione integrato di Django. Utilizzo dell'utilità manage.py nella directory root del progetto:

python manage.py makemigrations <appname>

Il comando precedente creerà gli script di migrazione necessari nella sottodirectory delle migrations dell'applicazione. Se si omette il parametro <appname> , verranno elaborate tutte le applicazioni definite nell'argomento INSTALLED_APPS di settings.py . Se lo ritieni necessario, puoi modificare le migrazioni.

È possibile controllare quali migrazioni sono richieste senza effettivamente creare la migrazione utilizzare l'opzione --dry-run, ad esempio:

python manage.py makemigrations --dry-run

Per applicare le migrazioni:

python manage.py migrate <appname>

Il comando precedente eseguirà gli script di migrazione generati nel primo passaggio e aggiornerà fisicamente il database.

Se il modello del database esistente viene modificato, è necessario il seguente comando per apportare le modifiche necessarie.

python manage.py migrate --run-syncdb

Django creerà la tabella con il nome <appname>_<classname> per impostazione predefinita. A volte non vuoi usarlo. Se vuoi cambiare il nome predefinito, puoi annunciare il nome della tabella impostando db_table nella classe Meta :

from django.db import models

class YourModel(models.Model):
    parms = models.CharField()
    class Meta:
        db_table = "custom_table_name"

Se vuoi vedere quale codice SQL verrà eseguito da una certa migrazione, esegui questo comando:

python manage.py sqlmigrate <app_label> <migration_number>

Django> 1.10
La nuova opzione makemigrations --check rende il comando di uscita con uno stato diverso da zero quando vengono rilevate modifiche del modello senza migrazioni.

Vedi Migrazioni per maggiori dettagli sulle migrazioni.

Creare un modello con relazioni

Relazione molti-a-uno

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()

Opzione più generica. Può essere utilizzato ovunque tu voglia rappresentare una relazione

Relazione molti-a-molti

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)

Internamente questo è rappresentato tramite un'altra tabella. E ManyToManyField dovrebbe essere messo su modelli che saranno modificati su un modulo. Ad esempio: l' Appointment avrà un ManyToManyField chiamato Customer , Pizza ha Toppings e così via.

Relazione molti-a-molti utilizzando le classi Through

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)

In questo modo, possiamo effettivamente mantenere più metadati su una relazione tra due entità. Come si può vedere, un cliente può essere abbonato a diversi servizi tramite diversi tipi di abbonamento. L'unica differenza in questo caso è che per aggiungere nuove istanze alla relazione M2M, non è possibile utilizzare il metodo di scelta rapida pizza.toppings.add(topping) , invece, dovrebbe essere creato un nuovo oggetto della classe through , Subscription.objects.create(client=client, service=service, subscription_type='p')

In altre lingue, le through tables sono anche note come JoinColumn , Intersection table o mapping table

Relazione uno-a-uno

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)

Usa questi campi quando avrai sempre una relazione di composizione tra i due modelli.

Query di base su Django DB

Django ORM è un'astrazione potente che ti consente di archiviare e recuperare dati dal database senza dover scrivere query SQL.

Assumiamo i seguenti modelli:

class Author(models.Model):
   name = models.CharField(max_length=50)

class Book(models.Model): 
   name = models.CharField(max_length=50)
   author = models.ForeignKey(Author)

Supponendo che tu abbia aggiunto il codice sopra a un'applicazione django ed esegui il comando migrate (in modo che il tuo database sia creato). Inizia la shell di Django di

python manage.py shell

Questo avvia la shell python standard ma con importate librerie Django rilevanti, in modo che tu possa concentrarti direttamente sulle parti importanti.

Inizia importando i modelli che abbiamo appena definito (presumo che ciò avvenga in un file models.py )

from .models import Book, Author

Esegui la tua prima query di selezione:

>>> Author.objects.all() 
[]
>>> Book.objects.all()
[]

Consente di creare un autore e un oggetto libro:

>>> hawking = Author(name="Stephen hawking")
>>> hawking.save()
>>> history_of_time = Book(name="history of time", author=hawking)
>>> history_of_time.save()

oppure utilizzare la funzione crea per creare oggetti modello e salvare in un codice di linea

>>> wings_of_fire = Book.objects.create(name="Wings of Fire", author="APJ Abdul Kalam")

Ora consente di eseguire la query

>>> Book.objects.all()
[<Book: Book object>]
>>> book = Book.objects.first() #getting the first book object
>>> book.name
u'history of time'

Aggiungiamo una clausola where alla nostra query select

>>> Book.objects.filter(name='nothing')
[]
>>> Author.objects.filter(name__startswith='Ste')
[<Author: Author object>]

Per ottenere i dettagli sull'autore di un determinato libro

>>> book = Book.objects.first() #getting the first book object
>>> book.author.name # lookup on related model
u'Stephen hawking'

Per ottenere tutti i libri pubblicati da Stephen Hawking (libro Lookup dal suo autore)

>>> hawking.book_set.all()
[<Book: Book object>]

_set è la notazione usata per "Reverse lookups" cioè, mentre il campo di ricerca è sul modello Book, possiamo usare book_set su un oggetto autore per ottenere tutti i suoi libri.

Un tavolo non gestito di base.

Ad un certo punto del tuo utilizzo di Django, potresti trovarti a voler interagire con le tabelle che sono già state create o con le viste del database. In questi casi, non vorrai che Django gestisca le tabelle attraverso le sue migrazioni. Per impostare questo, è necessario aggiungere solo una variabile alla classe Meta del modello: managed = False .

Ecco un esempio di come è possibile creare un modello non gestito per interagire con una vista del database:

class Dummy(models.Model):
    something = models.IntegerField()

    class Meta:
       managed = False

Questo può essere associato a una vista definita in SQL come segue.

CREATE VIEW myapp_dummy AS 
SELECT id, something FROM complicated_table 
WHERE some_complicated_condition = True

Una volta creato questo modello, puoi utilizzarlo come faresti con qualsiasi altro modello:

>>> Dummy.objects.all()
[<Dummy: Dummy object>, <Dummy: Dummy object>, <Dummy: Dummy object>]
>>> Dummy.objects.filter(something=42)
[<Dummy: Dummy object>]

Modelli avanzati

Un modello può fornire molte più informazioni rispetto ai soli dati su un oggetto. Vediamo un esempio e scomporlo in ciò che è utile per:

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']

Chiave primaria automatica

Potresti notare l'uso di self.pk nel metodo get_absolute_url . Il campo pk è un alias della chiave primaria di un modello. Inoltre, Django aggiungerà automaticamente una chiave primaria se manca. Questa è una cosa in meno di cui preoccuparsi e ti consente di impostare la chiave esterna per qualsiasi modello e ottenerli facilmente.

URL assoluto

La prima funzione definita è get_absolute_url . In questo modo, se si dispone di un libro, è possibile ottenere un collegamento ad esso senza manipolare il tag url, risolvere, attributo e simili. Chiama semplicemente book.get_absolute_url e ottieni il link giusto. Come bonus, il tuo oggetto nell'amministratore di django otterrà un pulsante "Visualizza sul sito".

Rappresentazione delle stringhe

Avere un metodo __str__ consente di utilizzare l'oggetto quando è necessario visualizzarlo. Ad esempio, con il metodo precedente, aggiungere un link al libro in un modello è semplice come <a href="{{ book.get_absolute_url }}">{{ book }}</a> . Dritto al punto. Questo metodo controlla anche ciò che viene visualizzato nel menu a discesa dell'amministratore, ad esempio per la chiave esterna.

Il decoratore di classi ti consente di definire il metodo una volta sia per __str__ che __unicode__ su python 2 senza causare alcun problema su python 3. Se ti aspetti che la tua app funzioni su entrambe le versioni, questa è la strada da percorrere.

Campo di lumaca

Il campo slug è simile a un campo char ma accetta meno simboli. Per impostazione predefinita, solo lettere, numeri, caratteri di sottolineatura o trattini. È utile se vuoi identificare un oggetto usando una bella rappresentazione, ad esempio nell'URL.

La classe Meta

La classe Meta ci consente di definire molte più informazioni sull'intera collezione di oggetti. Qui è impostato solo l'ordine predefinito. Ad esempio, è utile con l'oggetto ListView. Prende una lista ideale di campo da usare per l'ordinamento. Qui, il libro verrà ordinato prima per data di pubblicazione e poi per titolo se la data è la stessa.

Gli altri attributi di frequenza sono verbose_name e verbose_name_plural . Di default, sono generati dal nome del modello e dovrebbero andare bene. Ma la forma plurale è ingenua, semplicemente aggiungendo un 's' al singolare, quindi potresti volerlo impostare in modo esplicito in alcuni casi.

Valori calcolati

Una volta che un oggetto modello è stato recuperato, diventa un'istanza pienamente realizzata della classe. Di conseguenza, è possibile accedere a qualsiasi metodo aggiuntivo nei moduli e nei serializzatori (come Django Rest Framework).

L'utilizzo delle proprietà python è un modo elegante per rappresentare valori aggiuntivi che non sono memorizzati nel database a causa di circostanze variabili.

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

Mentre nella maggior parte dei casi è possibile integrare dati con annotazioni sui propri querysets, i valori calcolati come proprietà del modello sono ideali per i calcoli che non possono essere valutati semplicemente nell'ambito di una query.

Inoltre, le proprietà, dal momento che sono dichiarate sulla classe python e non come parte dello schema, non sono disponibili per l'esecuzione di query.

Aggiunta di una rappresentazione di stringa di un modello

Per creare una presentazione leggibile dall'uomo di un oggetto modello è necessario implementare il metodo Model.__str__() (o Model.__unicode__() su python2). Questo metodo verrà chiamato ogni volta che si chiama str() su un'istanza del modello (incluso, ad esempio, quando il modello viene utilizzato in un modello). Ecco un esempio:

  1. Crea un modello di libro.

    # 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. Crea un'istanza del modello e salvala nel database:

    >>> himu_book = Book(name='Himu Mama', author='Humayun Ahmed')
    >>> himu_book.save()
    
  3. Esegui print() sull'istanza:

    >>> print(himu_book)
    <Book: Book object>
    

<Book: Book object> , l'output predefinito, non ci è di aiuto. Per risolvere questo problema, aggiungiamo un metodo __str__ .

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)

Nota che il decoratore python_2_unicode_compatible è necessario solo se vuoi che il tuo codice sia compatibile con python 2. Questo decoratore copia il metodo __str__ per creare un metodo __unicode__ . django.utils.encoding da django.utils.encoding .

Ora se chiamiamo nuovamente la funzione di stampa l'istanza del libro:

>>> print(himu_book)
Himu Mama by Humayun Ahmed

Molto meglio!

La rappresentazione della stringa viene anche utilizzata quando il modello viene utilizzato in un campo ModelForm per i campi ForeignKeyField e ManyToManyField .

Model mixins

Negli stessi casi, diversi modelli potrebbero avere gli stessi campi e le stesse procedure nel ciclo di vita del prodotto. Per gestire queste somiglianze senza avere l'ereditarietà di ripetizione del codice potrebbe essere utilizzato. Invece di ereditare un'intera classe, il modello di progettazione mixin ci consente di ereditare ( o alcuni include include ) alcuni metodi e attributi. Vediamo un esempio:

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)

Per trasformare un modello in una classe astratta, dovrai menzionare abstract=True nella sua classe Meta interna. Django non crea tabelle per i modelli astratti nel database. Tuttavia, per i modelli Envelope e Package , le tabelle corrispondenti verranno create nel database.

Inoltre i campi alcuni metodi modello saranno necessari in più di un modello. Quindi questi metodi potrebbero essere aggiunti ai mixin per prevenire la ripetizione del codice. Ad esempio, se creiamo un metodo per impostare la data di consegna su PostableMixin , sarà accessibile da entrambi i suoi figli:

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()

Questo metodo potrebbe essere usato come segue sui bambini:

>> envelope = Envelope.objects.get(pk=1)
>> envelope.set_delivery_datetime()

>> pack = Package.objects.get(pk=1)
>> pack.set_delivery_datetime()

Chiave primaria UUID

Un modello per impostazione predefinita utilizza una chiave primaria con incremento automatico (numero intero). Questo ti darà una sequenza di chiavi 1, 2, 3.

Diversi tipi di chiavi primarie possono essere impostati su un modello con piccole modifiche al modello.

Un UUID è un identificatore univoco universale, si tratta di un identificatore casuale a 32 caratteri che può essere utilizzato come ID. Questa è una buona opzione da utilizzare quando non si desidera che ID sequenziali siano assegnati ai record nel proprio database. Se usato su PostgreSQL, questo memorizza in un tipo di dati uuid, altrimenti in un char (32).

import uuid
from django.db import models

class ModelUsingUUID(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

La chiave generata sarà nel formato 7778c552-73fc-4bc4-8bf9-5a2f6f7b7f47

Eredità

L'ereditarietà tra i modelli può essere effettuata in due modi:

  • una classe astratta comune (vedi l'esempio "Model mixins")
  • un modello comune con più tabelle

L'ereditarietà di più tabelle creerà una tabella per i campi comuni e uno per esempio di modello figlio:

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)

creerà 2 tabelle, una per il Place e una per il Restaurant con un campo OneToOne nascosto da Place per i campi comuni.

si noti che questo richiederà una query aggiuntiva alle tabelle dei luoghi ogni volta che si recupera un oggetto ristorante.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow