Django
Modele
Szukaj…
Wprowadzenie
W podstawowym przypadku modelem jest klasa Python, która jest odwzorowywana na pojedynczą tabelę bazy danych. Atrybuty mapy klas do kolumn w tabeli i instancja klasy reprezentuje wiersz w tabeli bazy danych. Modele dziedziczą po django.db.models.Model
który zapewnia bogate API do dodawania i filtrowania wyników z bazy danych.
Tworzenie pierwszego modelu
Modele są zazwyczaj zdefiniowane w pliku models.py
w podkatalogu aplikacji. Klasa Model
modułu django.db.models
to dobra klasa początkowa do rozszerzenia modeli. Na przykład:
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
Każdy atrybut w modelu reprezentuje kolumnę w bazie danych.
-
title
jest tekstem o maksymalnej długości 100 znaków -
author
jestForeignKey
który reprezentuje związek z innym modelem / tabelą, w tym przypadkuAuthor
(używanym tylko dla celów).on_delete
informuje bazę danych, co zrobić z obiektem, jeśli powiązany obiekt (Author
) zostanie usunięty. (Należy zauważyć, że ponieważ django 1.9on_delete
może być użyty jako drugi argument pozycyjny. W django 2 jest to wymagany argument i zaleca się, aby traktować go jako taki. W starszych wersjach domyślnie będzie toCASCADE
.) -
publish_date
przechowuje datę. Zarównonull
jak iblank
są ustawione naTrue
aby wskazać, że nie jest to pole wymagane (tj. Możesz dodać je później lub pozostawić puste).
Wraz z atrybutami definiujemy metodę __str__
zwraca tytuł książki, która będzie w razie potrzeby używana jako jej string
, a nie domyślny.
Stosowanie zmian w bazie danych (migracje)
Po utworzeniu nowego modelu lub zmodyfikowaniu istniejących modeli konieczne będzie wygenerowanie migracji dla wprowadzonych zmian, a następnie zastosowanie migracji do określonej bazy danych. Można to zrobić za pomocą wbudowanego systemu migracji Django. Korzystanie z narzędzia manage.py
w katalogu głównym projektu:
python manage.py makemigrations <appname>
Powyższe polecenie utworzy skrypty migracji, które są niezbędne w podkatalogu migrations
aplikacji. <appname>
parametru <appname>
spowoduje przetworzenie wszystkich aplikacji zdefiniowanych w argumencie INSTALLED_APPS
settings.py
. Jeśli uznasz to za konieczne, możesz edytować migracje.
Możesz sprawdzić, jakie migracje są wymagane bez faktycznego tworzenia migracji, użyj opcji --dry-run, np .:
python manage.py makemigrations --dry-run
Aby zastosować migracje:
python manage.py migrate <appname>
Powyższe polecenie wykona skrypty migracji wygenerowane w pierwszym kroku i fizycznie zaktualizuje bazę danych.
Jeśli model istniejącej bazy danych zostanie zmieniony, konieczne jest wykonanie następującego polecenia w celu wprowadzenia niezbędnych zmian.
python manage.py migrate --run-syncdb
Django domyślnie utworzy tabelę o nazwie <appname>_<classname>
nazwa klasy <appname>_<classname>
. Czasami nie chcesz go używać. Jeśli chcesz zmienić domyślną nazwę, możesz ogłosić nazwę tabeli, ustawiając db_table
w klasie Meta
:
from django.db import models
class YourModel(models.Model):
parms = models.CharField()
class Meta:
db_table = "custom_table_name"
Jeśli chcesz zobaczyć, jaki kod SQL zostanie wykonany przez określoną migrację, po prostu uruchom następującą komendę:
python manage.py sqlmigrate <app_label> <migration_number>
Django> 1.10
Nowa opcja makemigrations --check
powoduje, że polecenie kończy działanie z niezerowym statusem, gdy wykryte zostaną zmiany modelu bez migracji.
Zobacz Migracje, aby uzyskać więcej informacji na temat migracji.
Tworzenie modelu z relacjami
Relacja wiele do jednego
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()
Najbardziej ogólna opcja. Może być używany wszędzie tam, gdzie chcesz reprezentować związek
Relacja wiele do wielu
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)
Wewnętrznie jest to reprezentowane przez inną tabelę. I ManyToManyField
należy umieścić na modelach, które będą edytowane w formularzu. Np .: Appointment
będzie miało ManyToManyField
nazwie Customer
, Pizza
ma Toppings
i tak dalej.
Relacje wiele do wielu za pomocą klas „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)
W ten sposób możemy zachować więcej metadanych dotyczących relacji między dwoma podmiotami. Jak widać, klient może subskrybować kilka usług za pośrednictwem kilku rodzajów subskrypcji. Jedyną różnicą w tym przypadku jest to, że aby dodać nowe instancje do relacji M2M, nie można użyć metody skrótu pizza.toppings.add(topping)
, zamiast tego należy utworzyć nowy obiekt klasy przelotowej , Subscription.objects.create(client=client, service=service, subscription_type='p')
W innych językach
through tables
są również znane jakoJoinColumn
,Intersection table
lubmapping table
Relacja jeden do jednego
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)
Użyj tych pól, jeśli będziesz mieć tylko związek między dwoma modelami.
Podstawowe zapytania DB Django
Django ORM to potężna abstrakcja, która pozwala przechowywać i pobierać dane z bazy danych bez samodzielnego pisania zapytań SQL.
Załóżmy następujące modele:
class Author(models.Model):
name = models.CharField(max_length=50)
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.ForeignKey(Author)
Zakładając, że dodałeś powyższy kod do aplikacji django i uruchom komendę migrate
(aby tworzona była baza danych). Uruchom powłokę Django przez
python manage.py shell
Spowoduje to uruchomienie standardowej powłoki Pythona, ale z zaimportowanymi odpowiednimi bibliotekami Django, dzięki czemu można bezpośrednio skupić się na ważnych częściach.
Zacznij od zaimportowania właśnie zdefiniowanych modeli (zakładam, że odbywa się to w pliku models.py
)
from .models import Book, Author
Uruchom pierwsze wybrane zapytanie:
>>> Author.objects.all()
[]
>>> Book.objects.all()
[]
Utwórzmy autora i zarezerwuj obiekt:
>>> hawking = Author(name="Stephen hawking")
>>> hawking.save()
>>> history_of_time = Book(name="history of time", author=hawking)
>>> history_of_time.save()
lub użyj funkcji tworzenia, aby utworzyć obiekty modelu i zapisać w jednym wierszu kodu
>>> wings_of_fire = Book.objects.create(name="Wings of Fire", author="APJ Abdul Kalam")
Teraz uruchommy zapytanie
>>> Book.objects.all()
[<Book: Book object>]
>>> book = Book.objects.first() #getting the first book object
>>> book.name
u'history of time'
Dodajmy klauzulę where do naszego wybranego zapytania
>>> Book.objects.filter(name='nothing')
[]
>>> Author.objects.filter(name__startswith='Ste')
[<Author: Author object>]
Aby uzyskać szczegółowe informacje o autorze danej książki
>>> book = Book.objects.first() #getting the first book object
>>> book.author.name # lookup on related model
u'Stephen hawking'
Aby uzyskać wszystkie książki opublikowane przez Stephena Hawkinga (książka odnośnika autora)
>>> hawking.book_set.all()
[<Book: Book object>]
_set
to notacja używana do „wyszukiwania wstecznego”, tzn. gdy pole wyszukiwania znajduje się w modelu Book, możemy użyć book_set
na obiekcie autora, aby uzyskać wszystkie jego / jej książki.
Podstawowy niezarządzany stół.
W pewnym momencie korzystania z Django możesz chcieć wchodzić w interakcje z już utworzonymi tabelami lub z widokami bazy danych. W takich przypadkach nie chcesz, aby Django zarządzał tabelami podczas migracji. Aby to skonfigurować, musisz dodać tylko jedną zmienną do klasy Meta
modelu swojego modelu: managed = False
.
Oto przykład, w jaki sposób można utworzyć niezarządzany model do interakcji z widokiem bazy danych:
class Dummy(models.Model):
something = models.IntegerField()
class Meta:
managed = False
Można to odwzorować na widok zdefiniowany w SQL w następujący sposób.
CREATE VIEW myapp_dummy AS
SELECT id, something FROM complicated_table
WHERE some_complicated_condition = True
Po utworzeniu tego modelu można go używać tak, jak każdego innego modelu:
>>> Dummy.objects.all()
[<Dummy: Dummy object>, <Dummy: Dummy object>, <Dummy: Dummy object>]
>>> Dummy.objects.filter(something=42)
[<Dummy: Dummy object>]
Zaawansowane modele
Model może dostarczyć znacznie więcej informacji niż tylko dane o obiekcie. Zobaczmy przykład i podzielmy go na użyteczne:
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']
Automatyczny klucz podstawowy
Możesz zauważyć użycie self.pk
w metodzie get_absolute_url
. Pole pk
jest aliasem klucza podstawowego modelu. Ponadto django automatycznie doda klucz podstawowy, jeśli go brakuje. To o jedną rzecz mniej do zmartwienia i pozwala ustawić obcy klucz do dowolnych modeli i łatwo je zdobyć.
Bezwzględny adres URL
Pierwsza zdefiniowana get_absolute_url
to get_absolute_url
. W ten sposób, jeśli masz książkę, możesz uzyskać link do niej bez konieczności manipulowania tagiem url, rozdzielczością, atrybutem itp. Wystarczy zadzwonić na book.get_absolute_url
a otrzymasz odpowiedni link. Jako bonus twój obiekt w admin django zyska przycisk „zobacz na stronie”.
Reprezentacja łańcucha
Metoda __str__
pozwala na użycie obiektu, gdy trzeba go wyświetlić. Na przykład w przypadku poprzedniej metody dodanie linku do książki w szablonie jest tak proste, jak <a href="{{ book.get_absolute_url }}">{{ book }}</a>
. Od razu do rzeczy. Ta metoda kontroluje również to, co wyświetla się w menu administratora, na przykład dla klucza obcego.
Dekorator klas umożliwia jednokrotne zdefiniowanie metody zarówno dla __str__
jak i __unicode__
w pythonie 2, nie powodując przy tym problemu w pythonie 3. Jeśli spodziewasz się, że Twoja aplikacja będzie działała na obu wersjach, to jest właśnie ta droga.
Ślimakowe pole
Pole ślimaka jest podobne do pola char, ale akceptuje mniej symboli. Domyślnie tylko litery, cyfry, podkreślenia lub łączniki. Jest to przydatne, jeśli chcesz zidentyfikować obiekt za pomocą ładnej reprezentacji, na przykład w adresie URL.
Klasa Meta
Klasa Meta
pozwala nam zdefiniować o wiele więcej informacji na temat całej kolekcji przedmiotu. Tutaj ustawia się tylko domyślne porządkowanie. Jest to przydatne na przykład w przypadku obiektu ListView. Do sortowania potrzeba idealnie krótkiej listy pól. Książka będzie sortowana najpierw według daty publikacji, a następnie według tytułu, jeśli data jest taka sama.
Inne atrybuty częstotliwości to verbose_name
i verbose_name_plural
. Domyślnie są one generowane na podstawie nazwy modelu i powinny być w porządku. Ale forma liczby mnogiej jest naiwna, po prostu dołącza „s” do liczby pojedynczej, więc w niektórych przypadkach możesz chcieć ustawić ją wyraźnie.
Obliczone wartości
Po pobraniu obiektu modelowego staje się on w pełni zrealizowaną instancją klasy. Jako takie, wszelkie dodatkowe metody są dostępne w formularzach i serializatorach (takich jak Django Rest Framework).
Korzystanie z właściwości Pythona to elegancki sposób reprezentowania dodatkowych wartości, które nie są przechowywane w bazie danych z powodu różnych okoliczności.
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
Podczas gdy w większości przypadków można uzupełniać dane adnotacjami w zestawach zapytań, wartości obliczone jako właściwości modelu są idealne do obliczeń, których nie można ocenić po prostu w ramach zapytania.
Ponadto właściwości, ponieważ są zadeklarowane w klasie python, a nie jako część schematu, nie są dostępne dla zapytań.
Dodanie ciągu reprezentującego model
Aby utworzyć czytelną dla człowieka prezentację obiektu modelu, należy zaimplementować metodę Model.__str__()
(lub Model.__unicode__()
na python2). Ta metoda będzie wywoływana za każdym razem, gdy wywołasz str()
w instancji twojego modelu (włączając na przykład, kiedy model jest używany w szablonie). Oto przykład:
Utwórz model książki.
# 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)
Utwórz instancję modelu i zapisz ją w bazie danych:
>>> himu_book = Book(name='Himu Mama', author='Humayun Ahmed') >>> himu_book.save()
Uruchom
print()
na instancji:>>> print(himu_book) <Book: Book object>
<Book: Book object> , domyślne wyjście, nam nie pomaga. Aby to naprawić, __str__
metodę __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)
Uwaga: dekorator python_2_unicode_compatible
z python_2_unicode_compatible
jest potrzebny tylko, jeśli chcesz, aby kod był zgodny z python 2. Ten dekorator kopiuje metodę __str__
, aby utworzyć metodę __unicode__
. Zaimportuj go z django.utils.encoding
.
Teraz, jeśli ponownie wywołamy funkcję drukowania wystąpieniem książki:
>>> print(himu_book)
Himu Mama by Humayun Ahmed
Dużo lepiej!
Reprezentacja ciągu jest również używana, gdy model jest używany w ModelForm
dla pól ForeignKeyField
i ManyToManyField
.
Modelowe miksy
W tych samych przypadkach różne modele mogą mieć te same pola i te same procedury w cyklu życia produktu. Aby obsłużyć te podobieństwa bez dziedziczenia powtórzeń kodu, można zastosować dziedziczenie. Zamiast dziedziczenia całej klasy, wzorzec projektowania mixin oferuje nam odziedziczenie ( lub niektórzy twierdzą, że obejmują ) niektóre metody i atrybuty. Zobaczmy przykład:
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)
Aby przekształcić model w klasę abstrakcyjną, musisz wspomnieć abstract=True
w wewnętrznej klasie Meta
. Django nie tworzy żadnych tabel dla modeli abstrakcyjnych w bazie danych. Jednak w przypadku modeli Envelope
i Package
odpowiednie tabele zostałyby utworzone w bazie danych.
Ponadto w polach niektóre metody modelowe będą potrzebne w więcej niż jednym modelu. Tak więc metody te można dodać do mixin, aby zapobiec powtórzeniu kodu. Na przykład, jeśli stworzymy metodę ustawiania daty dostawy na PostableMixin
, będzie ona dostępna dla obu jej dzieci:
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()
Metodę tę można zastosować w następujący sposób w przypadku dzieci:
>> envelope = Envelope.objects.get(pk=1)
>> envelope.set_delivery_datetime()
>> pack = Package.objects.get(pk=1)
>> pack.set_delivery_datetime()
UUID Klucz podstawowy
Model domyślnie używa klucza podstawowego z automatycznym zwiększaniem (liczby całkowitej). To da ci sekwencję klawiszy 1, 2, 3.
Różne typy kluczy głównych można ustawić w modelu z niewielkimi zmianami w modelu.
UUID jest uniwersalnym unikalnym identyfikatorem, jest to 32-znakowy losowy identyfikator, którego można użyć jako identyfikatora. Jest to dobra opcja do użycia, gdy nie chcesz, aby sekwencyjne identyfikatory były przypisywane do rekordów w bazie danych. Gdy jest używane w PostgreSQL, przechowuje to typ danych UUID, w przeciwnym razie char (32).
import uuid
from django.db import models
class ModelUsingUUID(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Wygenerowany klucz będzie miał format 7778c552-73fc-4bc4-8bf9-5a2f6f7b7f47
Dziedzictwo
Dziedziczenie między modelami można wykonać na dwa sposoby:
- wspólna klasa abstrakcyjna (patrz przykład „Model mixins”)
- wspólny model z wieloma tabelami
Dziedziczenie wielu tabel utworzy jedną tabelę dla wspólnych pól i jedną na przykład modelu potomnego:
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)
utworzy 2 tabele, jedną dla Place
i jedną dla Restaurant
z ukrytym polem OneToOne
do Place
dla wspólnych pól.
zwróć uwagę, że będzie to wymagało dodatkowego zapytania do tabel miejsc za każdym razem, gdy pobierasz Obiekt restauracji.