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.