This article is a continuation of Last time. The previous article described the conceptual part of the API. This time, I would like to actually build an API using Python and Django. The API created this time is as simple as sending data (from the browser) and saving it in the database, and moving it to any endpoint to display the data in the database. It also builds APIs without using ** Django Rest Framework **. The reason is that I thought that I could understand the convenience of Django Rest framework by building with ** Python code ** without using the framework provided by Django.
Those who know the basic grammar of Python, those who know the basic mechanism of Django. (Django's installation and installation is omitted considerably.)
The first important part in actually creating the API is the ** what kind of data to handle **. Sending data to the API does not mean that it will be stored in the database without permission, but the API will store the data based on the model of the data designed in ** models.py **. So, first, set the ** what kind of data to handle ** part in the model.
models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
timestamp = models.DateField(auto_now_add=True)
def __str__(self):
return self.title
This time, I designed a model that assumes that blog data is handled by API. Data that has a title, the content of the article, and the date and time of posting. If you can write a model, let's go to ** migrate **. Next, register ** Post ** in ** admin.py ** and enter some test data from the admin page. Enter an appropriate value for each item and save it as shown in the figure below.
Now let's display the data saved from the admin page on any endpoint. The first thing to remember is that modern APIs return ** JSON data ** when you send a request. So the return value of the posts function in ** views.py ** is ** JsonResponse **. Other than that, the simple code brings ** QuerySet ** closer to JSON data. Turn the QuerySet with a for statement to make it a dictionary type and put it in the list. From here on, Django will do the work for you, and the dictionary-type data in the Python list will be automatically ** serialized ** into JSON data.
views.py
from .models import Post
def posts(request):
posts = Post.objects.all()
post_list = []
for post in posts:
post_dict = {
'pk': post.pk,
'title': post.title,
'description': post.description,
'timestamp': post.timestamp,
}
post_list.append(post_dict)
return JsonResponse(post_list, safe=False)
# non-To convert dict type data to json data"safe=False"To describe.
Create any endpoint.
urls.py
from application_name.views import posts #add to
urlpatterns = [
path('admin/', admin.site.urls),
path('api/post/', posts), #add to
]
At this point, let's set up a local server once and move to the created endpoint. I didn't need to enter the primal key this time, but it's convenient when checking during testing like this. I was able to confirm the data properly, so it was a success! Next is the POST request.
The role of the API with Django is to store new data based on models.py in the database when it is sent through a ** POST request **. It is difficult to understand even if I write a little, so to explain briefly, the role of the API side is to ** process ** and ** save ** the sent data in a form that is easy to save. So next, I will write the code to process and save.
views.py
#Add the following code. The following if statement is written above the processing of the GET request.
from django.http import HttpResponse
import json
def posts(request):
if request.method == 'POST':
data = json.loads(request.body)
post = Post(
title=data['title'],
description=data['description'],
)
post.save()
return HttpResponse(201)
posts = Post.objects.all()
# ~~The following is omitted~~
** Django ** doesn't have a standard feature to automatically ** deserialize ** (return JSON data to a manageable form) for the JSON data sent, so you need to write the code to deserialize it. You need to write it down. This feature is necessary because it cannot be saved in the database unless it is restored to a data format that can be handled by Python. Put JSON data in an appropriate variable and deserialize (process) using this variable. Also, ** Django ** doesn't have the ability to automatically save data from views, so explicitly write ** post.save ** at the end.
This time I would like to use ** axios ** to send a ** POST request ** to the API. If you want to implement ** ajax ** in your own application, it is recommended to use Javascript's ** axios library **. First, create a page in templates to send a POST request. Add axios to that page using ** cdn **.
main.html
<body>
<p>Test Page</p>
</body>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> #Add with cdn
</html>
Created with ** function based ** in view. Also do the routing.
views.py
def main_page(request):
return render(request, 'main.html', context=None)
urls.py
from application_name.views import main_page #add to
urlpatterns = [
path('admin/', admin.site.urls),
path('', main_page), #Add a route
path('api/post/', posts),
]
Set up a local server and go to the URL where ** main.html ** is displayed. So let's check if the API works properly from the developer tools ** console **. Since the data sent by POST request must be ** JSON data **, make it a dictionary type. Just make it a variable for the sake of clarity as an argument.
console
/*
Create an async function
Send request and make response
I want to await because it takes time to send a request
The API endpoint can be a relative path as the server is already up and running
*/
async function addPost(title, description) {
const response = await axios.post('api/post/', {'title': title, 'description': description})
console.log(response)
}
//Instantiation
addPost('sending from axios', 'some test messages')
However, doing this should return a ** 403 Forbidden ** error statement. When the 403 comes back like this, you can see all the ** requests ** you have sent in your browser's developer tools ** Network **. As you can see in the image below, you can see the part (data) of the payload you were trying to send. Next, if you look at the ** Response ** part, you will find ** html statements ** that you often see. To see this response in your browser for clarity, copy the ** response html statement ** and rewrite it with ** innerHTML **. It's a little hard to explain in words, so take a look at the code below.
console
document.querySelector('body').innerHTML = `Response html statement`
If you run it now, you'll see that Django is getting the error. I will put the error screen below. If you look at the error statement, you can see that ** CSRF verification ** has failed. If you are using form in Django, it says that you can use ** csrf_token **, but this time I am sending data using ajax, so this method can not solve it. To do ajax in Django, use a method you don't normally use. To explain the flow of ** CSRF verification ** in the first place, Django shows that the front side is accessing data from the same site instead of accessing it from another location, and Django receives it. Gives data permissions to the front side. If you think about ** 403 Forbidden ** again with this in mind, you can see that it means "you do not have the authority (permission) to handle data". However, as mentioned earlier, form is not used, so ** csrf_token ** cannot be used. So this time, put csrf_token in ** Cookie ** and paste it in javascript file.
sample.js
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"
Originally, by pasting the above code at the top of the ** js file ** that sends the ajax request, you can make the request using axios have ** cookie ** with csrf_token by default. I will. This should resolve the previous error. Since we will not have any functions other than ajax communication using Javascript this time, create a js file with an arbitrary name and write only the above code. Load (connect) this file with ** main.html ** as shown below, and make it work on the console of the developer tools.
main.html
<body>
<p>Test Page</p>
</body>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="text/javascript" src="{% static 'sample.js' %}"></script> #add to
</html>
When I put a value in the argument in the console and instantiate it as before, this time the status code 201 is returned without any error. In other words, it's a success! For the time being, go to the 'api / post /' endpoint and check if the POSTed data is displayed properly. プライマリーキーが3のデータが先ほど追加したデータなので、ちゃんとGET, POSTリクエストが動いていることが分かります。
First of all, I think the main reason is that using ** Django Rest Framework ** (DRF) greatly reduces the code related to data processing and authentication that we are not dealing with this time. However, I think the next reason is that HTTP verbs such as PUT, PATCH, and DELETE will be available. Normally, ** Django ** ignores these, so you can't use them if you want. ** DRF ** handles other requests easily, so you can maximize the functionality of the API. I think there are many other reasons, but for the time being, I think these two are the major reasons.
How was the content of the article? It also serves as a memorandum of my own, and it has become quite long, so thank you to those who read it to the end. If you have any mistakes or typographical errors, please feel free to point them out. Next time, I would like to create a simple application using an external API.
Recommended Posts