Django – #40 – REST API cz. 15 – Aplikacja Django korzystająca z API DRF – get()

Wprowadzenie.

Wyobraźmy sobie, że skończyliśmy prace nad naszym API lub też API jest stworzone w takim stopniu, że może być wykorzystane przez zewnętrzne aplikacje. W związku z powyższym kolejnym krokiem, jaki warto poznać, jest korzystanie z REST API stworzonego przy pomocy Django Rest Framework, poprzez zwykłą aplikacją w Django. Warto w tym miejscu zaznaczyć, że przy takiej architekturze mamy w pełni oddzieloną warstwę przechowywania danych od warstwy interfejsu użytkownika.

Zakres artykułu.

  • Aplikacja Django korzystająca z API DRF – get()

Aplikacja Django korzystająca z API DRF – get()

W celu uproszczenia zagadnienia, gdzie aplikacja będzie korzystała z REST API, postanowiłem, aby obydwie części należały do tego samego projektu, w związku z czym, odchodzi nam tworzenie nowego projektu Django od nowa.

W pierwszej kolejności stwórzmy szablon html o nazwie base.html. Jeżeli chcecie dowiedzieć się więcej na temat szablonów, to zachęcam do zapoznania się z pozostałymi wpisami o Django, które znajdują się na blogu.

<!doctype html>
<html lang="en">

  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <link rel="shortcut icon" href="#" />
    <title>REST API APP!</title>
  </head>
  <body>
    <div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
      <h5 class="my-0 mr-md-auto font-weight-normal">REST API APP</h5>
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
    {% block content %}{% endblock %}
    </body>
</html>

W drugim kroku stwórzmy kolejny plik html o nazwie home.html, który będzie rozszerzał plik base.html. W pliku tym będę zamieszczał wyniki pracy. Taka struktura szablonów ma celu oddzielenie części kodu, która będzie się zmieniała od części kodu, która jest stała. 

{% extends 'restapiapp/base.html' %}

{% block content %}
  <div class="container">

  </div>
{% endblock %}

Teraz stwórzmy prostą funkcję widoku, która będzie renderowała szablon home.html. i wykorzystywała do tego dane ze słownika context.

def home(request):
    context = {}

    return render(request, 'restapiapp/home.html', context)

Następnie stwórzmy ścieżkę, która połączy adres URL z funkcją nowo stworzonego widoku. 

urlpatterns = [
    path('admin/', admin.site.urls),

    # API
    path('api/authors/', views.AuthorList.as_view()),
    path('api/books/', views.BookList.as_view()),
    path('api/borrows/', views.BorrowList.as_view()),
    path('api/borrows/<int:pk>/', views.BorrowRetrieveDestroy.as_view()),
    path('api/borrows/<int:pk>/return/', views.BorrowReturnBookUpdate.as_view()),
    path('api/borrows/<int:pk>/edit/', views.BorrowRetrieveUpdate.as_view()),

    path('api/user/create', views.UserCreate.as_view()),
    path('api/user/login', views.UserTokenList.as_view()),
    path('api/activate/<int:pk>/<str:s>', views.UserAccountActivation.as_view()),

    # DRF
    path('api-auth/', include('rest_framework.urls')),

    # APP
    path('', views.home, name='home'), # new
]

Na chwilę obecną może sprawdzić, czy strona ładuje się poprawnie.

Gdy wszystko do tej pory działa poprawnie, wówczas możemy przejść do właściwej części tego wpisu. W pierwszej kolejności do pliku zawierającego widoki views.py zaimportujmy moduł requests.

import requests

Do słownika context dodajmy nowy klucz ‘authors‘ i przypiszmy do niego wynik następującej funkcji requests.get(‘http://127.0.0.1:8000/api/authors/’).json(). Pod tym linkiem znajdziecie więcej informacji o module requests

def home(request):
    context = {}

    context['authors'] = requests.get('http://127.0.0.1:8000/api/authors/').json()

    return render(request, 'restapiapp/home.html', context)

Następnie, aby zobaczyć wynik naszego zapytania, w szablonie home.html wywołajmy zmienną authors.

{% extends 'restapiapp/base.html' %}

{% block content %}
  <div class="container">
    {{ authors }}
  </div>
{% endblock %}

Sprawdźmy jak prezentuje się nasza strona.

Jak możemy zobaczyć, dane prezentowane są w formacie json

Teraz idźmy o krok dalej i wykonajmy kolejne zapytanie do serwera API, żebyśmy otrzymali listę książek. Listę tę przypiszmy do klucza w słowniku o nazwie books.

def home(request):
    context = {}

    context['authors'] = requests.get('http://127.0.0.1:8000/api/authors/').json()
    context['books'] = requests.get('http://127.0.0.1:8000/api/books/').json()

    return render(request, 'restapiapp/home.html', context)

Zmodyfikujmy teraz szablon i wyświetlmy zawartość zmiennej books

{% extends 'restapiapp/base.html' %}

{% block content %}
  <div class="container">
    {{ authors }}
    <br><br><br><br>
    {{ books }}
  </div>
{% endblock %}

Sprawdźmy wynik naszej pracy.

Obecnie nie posiadamy już ograniczenia w postacie danych zwracanych tylko z jednego zapytania do API. Oznacza to, że widoki stron / aplikacji mogą zawierać dane z wielu różnych zapytań.

Teraz możecie zadawać sobie pytanie, jak dobrać się do konkretnych danych w ciągu danych w formacie json. Temat ten już był poruszany w poprzednich wpisach, lecz dla przypomnienia napiszę tu jeszcze raz jak to zrobić. 

Jeżeli mamy zwracaną listę, to do konkretnych elementów wystarczy, jak po kropce dodamy liczbę. W ten sposób ograniczymy się do jednego zestawu danych (można powiedzieć do jednego rekordu).

{% extends 'restapiapp/base.html' %}

{% block content %}
  <div class="container">
    {{ authors }}
    <br><br><br><br>
    {{ books }}
    <br><br>
    {{ books.0 }}
  </div>
{% endblock %}

Sprawdźmy, jak teraz wygląda strona.

Efekt jest coraz lepszy, lecz co zrobić, aby wydobyć jedynie tytuł. W tym celu ponownie musimy zastosować kropkę i wpisać interesujący nas klucz, w moim przypadku będzie to title.

{% extends 'restapiapp/base.html' %}

{% block content %}
  <div class="container">
    {{ authors }}
    <br><br><br><br>
    {{ books }}
    <br><br>
    {{ books.0 }}
    <br><br>
    {{ books.0.title }}
  </div>
{% endblock %}

Ponownie sprawdźmy efekt tej modyfikacji.

Obecnie wiemy, jak poruszać się po otrzymanych zbiorach danych. Sprawdźmy teraz, co się stanie, jak będziemy chcieli pobrać listę pozycji wypożyczonych borrows

def home(request):
    context = {}

    context['authors'] = requests.get('http://127.0.0.1:8000/api/authors/').json()
    context['books'] = requests.get('http://127.0.0.1:8000/api/books/').json()
    context['borrows'] = requests.get('http://127.0.0.1:8000/api/borrows/').json()

    return render(request, 'restapiapp/home.html', context)

Zmodyfikujmy odpowiednio plik home.html

{% extends 'restapiapp/base.html' %}

{% block content %}
  <div class="container">
    {{ authors }}
    <br><br><br><br>
    {{ books }}
    <br><br>
    {{ books.0 }}
    <br><br>
    {{ books.0.title }}
    <br><br><br><br>
    {{ borrows }}
  </div>
{% endblock %}

I sprawdźmy, co otrzymamy. 

W tej chwili otrzymaliśmy informację, że nie jesteśmy uwierzytelnieni. Jak pamiętacie z poprzednich wpisów, uwierzytelnianie odbywa się na dwa sposoby. Możemy dodać do aplikacji system logowania, lecz tę funkcjonalność już pokazywałem w poprzednich wpisach a z drugiej strony, gdy stworzymy oddzielną aplikację to metoda logowania, będzie bezużyteczna.

W tym oto momencie przychodzi nam z pomocą uwierzytelnianie przy pomocy Tokena. Stwórzmy słownik o nazwie headers, dla danych wysyłanych w nagłówku zapytania. Zmienną headers zamieśćmy jako kolejny argument metody request.get(), z tym że przypiszmy go do zmiennej o tej samej nazwie. 

def home(request):
    context = {}
    headers = {
        'Authorization': 'Token 000c74d3b3b816d90e19ea42926e5a7f950366cc',
    }

    context['authors'] = requests.get('http://127.0.0.1:8000/api/authors/').json()
    context['books'] = requests.get('http://127.0.0.1:8000/api/books/').json()
    context['borrows'] = requests.get('http://127.0.0.1:8000/api/borrows/').json()
    context['borrows2'] = requests.get('http://127.0.0.1:8000/api/borrows/', headers=headers ).json()

    return render(request, 'restapiapp/home.html', context)

W powyższym fragmencie kodu wartość tokena dla klucza Authorization zahardkodowałem, co jest oczywiście błędną praktyką. Token jest zmienną przypisaną do użytkownika, więc musi się zmienić w zależności, kto korzysta z aplikacji. Ponadto wartość tokena powinna być umieszczona w takim miejscu, aby nikt inny nie mógł jej podejrzeć, ponieważ dzięki temu kluczowi jest w stanie się pod nas podszywać, w zakresie takim, na jaki pozwala API

Standardowo zmodyfikujmy szablon home.html

{% extends 'restapiapp/base.html' %}

{% block content %}
  <div class="container">
    {{ authors }}
    <br><br><br><br>
    {{ books }}
    <br><br>
    {{ books.0 }}
    <br><br>
    {{ books.0.title }}
    <br><br><br><br>
    {{ borrows }}
    <br><br>
    {{ borrows2 }}
  </div>
{% endblock %}

I na koniec sprawdźmy efekt naszej pracy.

Autor artykułu
Dominik Bednarski

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.