ORM Django

Django dostarcza ci bogate API do operowania na danych umieszczonych w bazie danych. Po stworzeniu modeli możesz przystąpić do tworzenia widoków operujących na danych - dodawanie, pobieranie, edycja i inne operacje. Niniejszy artykuł będzie bazował na takim oto modelu blogów:
class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __unicode__(self):
        return self.name

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

    def __unicode__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateTimeField()
    authors = models.ManyToManyField(Author)

    def __unicode__(self):
        return self.headline
Ulokowanego w mysite/blog/models.py (założenia do przykładu).

Dodawanie Danych

By dodać wpis do bazy danych wystarczy podać dane i wywołać save():
from mysite.blog.models import Blog
b = Blog(name='Blog Andrzeja', tagline='Najnowsze wieści z pola')
b.save()

Jeżeli tabela ma pole AutoField (zwiększające swą wartość, AUTO INCREMENT/SERIAL) to można pobrać id wpisu ale dopiero po jego zapisaniu, gdyż numer ID jest "obliczany" przez bazę a nie przez Django.
b2 = Blog(name='BlogSer', tagline='Przemyślenia o serze')
b2.id     # zwraca None - brak id jeszcze
b2.save()
b2.id     # tutaj dostaniemy id.

Zapisywanie zmian wygląda podobnie. W powyższym przykładzie b2 odnosi się do zapisanego obiektu. By zmienić dane wystarczy:
b2.name = 'Serowo'
b2.save()

Jak django odróżnia dodanie od aktualizacji?

  • Jeżeli klucz główny obiektu (primary key) ma wartość liczbową to Django wykonuje SELECT by sprawdzić czy rekord istnieje.
  • Jeżeli rekord o podanym kluczu istnieje Django wykonuje UPDATE
  • Jeżeli obiekt nie ma zdefiniowanego klucza lub rekord nie istnieje to django wykonuje INSERT

Pobieranie Danych

Pobieranie danych wygląda nieco inaczej. Stosuje się do niego menadżera metody. By pobrać wszystkie wiersze wystarczy:
all_entries = MODEL.objects.all()
all_entries = Blog.objects.all()
Gdzie "MODEL" to nazwa wybranego przez nas modelu. Otrzymamy listę zawierającą rekordy z bazy danych.
By pobrać określone rekordy możemy skorzystać z filter() lub exclude():
Entry.objects.filter(pub_date__year=2006)
Co pobierze rekordy, których pola pub_date zawierają rok 2006

Zagnieżdżanie filtrów

Filtry można zagnieżdżać w taki sposób:
Entry.objects.filter(
    headline__startswith='What').exclude(
        pub_date__gte=datetime.now()).filter(
            pub_date__gte=datetime(2005, 1, 1))
Otrzymamy rekordy w których "headline" zaczyna się od "What" opublikowane (pub_date) między styczniem 2005 a dniem dzisiejszym.

Otrzymany wynik (obiekt QuerySet) można dalej zmieniać a otrzymane na nowo wyniki są nowym obiektem QuerySet niezależnym od wyniku-rodzica:
q1 = Entry.objects.filter(headline__startswith="What")
q2 = q1.exclude(pub_date__gte=datetime.now())
q3 = q1.filter(pub_date__gte=datetime.now())
Obiekty QuerySets nie pobierają danych z bazy przy ich definiowaniu. Dopiero pewne operacje wykonują narzucone zapytanie. Owe czynniki to:
  • Iterowanie: przy pierwszym przejściu zapytanie jest wykonywane
    for e in Entry.objects.all():
        print e.headline
    
  • cięcie tablicy - użycie Pythonowej składni do cięcia tablic z parametrem step spowoduje wykonanie zapytania
  • repl i len - notka - len nie powinno być stosowane do obliczania ilości wyników. Znacznie wydajniejszy jest SELECT COUNT
  • list - wykonanie list() na QuerySet spowoduje wykonanie zapytania i wczytania wszystkiego do pamięci.

Dodatkowe parametry wpływające na wynik

Ograniczanie ilości pobieranych wierszy jest proste:
Entry.objects.all()[:5] # LIMIT 5
Entry.objects.all()[5:10] # OFFSET 5 LIMIT 5
Entry.objects.all()[:10:2] # wykona zapytanie, zwróci co 2 rekord z pierwszych 10
Entry.objects.order_by('headline')[0] # jeden wiersz

Sortowanie wyników określone może być w modelu lecz także w zapytaniu można narzucić określone sortowanie:
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

Za pomocą .distinct() można pobrać niepowtarzalne wiersze (SELECT DISTINCT).

.value() natomiast zwraca ValuesQuerySet i pozwala określić z jakich pól pobierać dane. Oprócz tego ValuesQuerySet jest listą słowników
Blog.objects.values('id', 'name')
Zwróci wszystkie wpisy z bazy danych ale ograniczone do pól "id" i "name".

dates(field, kind, order='ASC')
Zwraca listę niepowtarzalnych dat z pól typu DateField lub DateTimeField. field to nazwa takiego pola, kind to jedno z trzech: year", "month" lub "day" - odpowiednio zwrócą one unikalne lata, unikalne miesiąc/rok i unikalne dzień/miesiąc/rok. order określa kolejność (ASC lub DESC)

select_related() zwraca QuerySet, który dodatkowo podąża za wszystkimi ForeignKey:
e = Entry.objects.select_related().get(id=5)
b = e.blog
Co jest znacznie szybsze niż pobieranie danych "po kolei".

extra(select=None, where=None, params=None, tables=None)
Pozwala na dodanie własnego kodu SQL w pewnych miejscach zapytania. Należy stosować tylko w ostateczności.
count() zliczy ilość wierszy jaka byłaby zwrócona (SELECT COUNT)
delete() kasuje określone wpisy.

Przeszukiwanie pól

Możemy również tworzyć zapytania z LIKE i ILIKE, odpowiednio:
Entry.objects.get(headline__contains='Lennon')
Entry.objects.get(headline__icontains='Lennon')
Oprócz "contains" czy "icontains" mamy do dyspozycji:
  • gt - większe niż
  • gte - większe lub równe niż
  • lt - mniejsze niż
  • lte - mniejsze lub równe
  • in - wartość z możliwych z listy: Entry.objects.filter(id__in=[1, 3, 4])
  • startswith - zaczyna się od
  • istartswith - to samo, nie uwzględnia rozmiaru liter
  • endswith i iendswith - kończy się na, drugi nie uwzględnia wielkości liter
  • range - wartość z przedziału Entry.objects.filter(pub_date__range=(start_date, end_date))
  • year - dla pól daty, określa rok jaki muszą spełniać
  • month, day - odpowiednio miesiąc i dzień
RkBlog

Django, 14 July 2008

Comment article
Comment article RkBlog main page Search RSS Contact