[PYTHON] Create an API with Django

Introduction

Hey, guys! Hello, good evening, or good night!

Hi, it ’s me. This is Takurinton.

This time it's like making an API with Django and hitting it. As a background, there was a problem that I could not implement unless I did something like that in a certain subject of our university, so I feel like I will do it a little. Well, it was pretty hard because I was from a state of ignorance. It's a free task, so it's completely self-destructive (; ∀ ;)

Prerequisites

Time required

The total time required was about 6 hours including the time to google. Please by the guide.

background

I made it by remodeling the EC site that was originally made by business consignment. But I shouldn't destroy the site that is being updated, so I thought it would be easy to create another environment in Docker and create a virtual environment with docker-compose. Whether you use Docker or not, I think that only the database will change, so I will omit that part (I will write the details on my personal blog soon)

environment

OS macOS Chatalina version 10.15.4
language Python, Java
Framework Django REST framework

Technology used

Click here for a link to the entire Django REST framework (https://www.django-rest-framework.org/)

create! !! !!

Constitution

Originally, the database and logic were solid, so there was no need for any maintenance.

The API created this time gets the order information. As an image

--User's email address --The store you ordered --List of ordered items and photos

I want to get the hit, so I implemented it accordingly.

The database related to this is as follows.

models.py


class CustomUserManager(UserManager):
    use_in_migrations = True
    def _create_user(self, email, password=None, zip_code=None, address1=None, address2=None, address3=None,  **extra_fields):
        if not email:
            raise ValueError('Email is mandatory and no cover')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        phone_number_regex = RegexValidator(regex=r'^[0-9]+$', message = ("Tel Number must be entered in the format: '09012345678'. Up to 15 digits allowed."))
        phone_number = models.CharField(validators=[phone_number_regex], max_length=15, verbose_name='phone number')
        zip_code = models.CharField(max_length=8)
        address1 = models.CharField(max_length=40)
        address2 = models.CharField(max_length=40)
        address3 = models.CharField(max_length=40, blank=True)
        user.save(using=self._db)
        return email



    def create_user(self, request_data, **kwargs):
        if not request_data['email']:
            raise ValueError('Users must have an email address.')

        user = self.model(
            email=request_data['email'],
            first_name=request_data['first_name'], 
            last_name=request_data['last_name'], 
            # password=request_data['password'], 
            zip_code=request_data['zip_code'],
            address1=request_data['address1'], 
            address2=request_data['address2'], 
            address3=request_data['address3'], 
        )

        user.set_password(request_data['password'])
        user.save(using=self._db)
        return user

    def create_superuser(self, email, phone_number=None, password=None, zip_code=None, address1=None, address2=None, address3=None,  **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')
        return self._create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    #username = models.CharField(_('username'), max_length=20, unique=True)
    email = models.EmailField(_('email address'), unique=True)
    first_name = models.CharField(_('first name'), max_length=30)
    last_name = models.CharField(_('last name'), max_length=150)
    zip_code = models.CharField(max_length=8)
    address1 = models.CharField(max_length=40)
    address2 = models.CharField(max_length=40)
    address3 = models.CharField(max_length=40, blank=True)
    phone_number_regex = RegexValidator(regex=r'^[0-9]+$', message = ("Tel Number must be entered in the format: '09012345678'. Up to 15 digits allowed."))
    phone_number = models.CharField(validators=[phone_number_regex], max_length=15, verbose_name='phone number', null=True, blank=True)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_(
            'Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = CustomUserManager()
    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []


    def user_has_perm(self, user, perm, obj):
        return _user_has_perm(user, perm, obj)

    def has_perm(self, perm ,obj=None):
        return _user_has_perm(self, perm, obj=obj)
    
    def has_module_perms(self, app_label):
        return self.is_staff

    def get_short_name(self):
        return self.first_name

    class Meta:
        # db_table = 'api_user'
        swappable = 'AUTH_USER_MODEL'

class Company(models.Model):
    name = models.CharField(max_length=255)
    introduction = models.TextField(max_length=65536)
    postal_code = models.CharField(max_length=8)
    company_image = models.ImageField()
    homepage = models.CharField(max_length=255, null=True, blank=True)
    images = models.BooleanField(verbose_name='', default=False)
    place = models.CharField(max_length=255)

    def __str__(self):
        return str(self.name)


class Product(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)
    name = models.CharField(max_length=255)
    contents = models.CharField(max_length=255)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    product_image = models.ImageField()
    option = models.CharField(max_length=255, null=True, blank=True, default=None)
    price = models.IntegerField()
    def __str__(self):
        return str(self.name)

class Cart(models.Model):
    cart_id = models.IntegerField(null=True, blank=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
    is_active = models.BooleanField(default=True)
    pub_date = models.DateTimeField(null=True, blank=True)

    def __str__(self):
        return str(self.cart_id)



class UserInfomation(models.Model):
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE, blank=True, null=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
    day = models.CharField(max_length=255, default=None)
    time = models.CharField(null=True, blank=True, max_length=255,  default=None)
    status = models.BooleanField(default=False, null=True, blank=True)
    total = models.IntegerField(null=True)
    remark = models.TextField(max_length=65535, null=True, blank=True)
    pub_date = models.DateTimeField(default=now)

    def __str__(self):
        return str(self.user)


class OrderItems(models.Model):
    user = models.ForeignKey(UserInfomation, on_delete=models.CASCADE)
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE, blank=True, null=True)
    item = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True)
    number = models.IntegerField(null=True)
    price = models.IntegerField(null=True)
    total = models.IntegerField(null=True)

--User table extends custom user model -Defines some functions to get each permission --company is one-to-many with product --user is one-to-many with cart, but only one active cart --User Infomation contains confirmed orders --OrderItems contains User information

There are other tables, but this time I will omit them because I can do it with just this.

I will make

Preparation

First, if you have not installed the rest framework, install it.

pip install djangorestframework

Next, add the following settings to settings.py.

settings.py


...  

INSTALLED_APPS = [
    ...
    'rest_framework',
]

...



JWT_AUTH = {
    'JWT_VERIFY_EXPIRATION': False, #Token persistence
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
}


REST_FRAMEWORK = { 
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),  
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),  
    'NON_FIELD_ERRORS_KEY': 'detail',
    'TEST_REQUEST_DEFAULT_FORMAT': 'json', 
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend', 
    ), 
}



Login function

Here, we will create a function to log in to a user who already exists. This time, we will authenticate all requests. This is required because only logged-in users can operate it. First, create an endpoint.

project_name/urls.py


from django.conf.urls import url

from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
    url(r'^login_/', obtain_jwt_token),
    ..., 
]

The login function is completed by adding this endpoint. It is a premise that you are building a database. obtain_jwt_token will return a token for authentication when accessing this endpoint.

I would like to log in as an existing user. I will use ipython in the experimental stage (because it is easy)

In [1]: import requests
In [2]: import json
In [3]: data = {'email': '[email protected]', 'password': 'hogehoge'}
In [4]: r = requests.post('localhost:8000/login_/', data=data)
In [5]: print(r.json())
Out[5]: {'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo5LCJ1c2VybmFtZSI6ImIxODA2NDI5QGdtYWlsLmNvbSIsImV4cCI6MTU5NDc5OTMwNCwiZW1haWwiOiJiMTgwNjQyOUBnbWFpbC5jb20ifQ.Torhy69ZyKMOOxQUUv3Ebn9V6wqSwUlsQUD5IPUaDJA'}

A long JSON will be returned like this. As a caveat at this time, Python has something like json.dumps (dict) that converts the dictionary to JSON, but if you do that, an error will occur. The data passed here is dict type data. Also, please keep this returned token as you will use it later. The data used when logging in is the primary key email address and password. If you don't know what it is, read the code in models.py.

As an aside, in Python requests, you can put a body in data and a header in headers. By using r.json (), you can convert the response to dict type and convert it to an easy-to-use form. requests and json are pretty dense. It's dense. It's dense.

Custom JSON

Next, I will return the JSON customized for myself. Personally, I stumbled here.

First, Serializer and Generic views I thought it would be better to implement it using / generic-views / #genericapiview), but apparently this is a custom JSON (these can only return fields that exist in any single table). I can't seem to return, so I changed the method. It took me a long time to get to the current method, so that's my reflection.

If you want to return custom JSON, it seems that you do not need a serializer etc., so I will write it directly in views.py. The endpoint is described in urls.py.

urls.py



from django.urls import path
from . import views


urlpatterns = [
...
path('get_shop_view', views.ShopView.as_view()), #Add this
]

views.py



from django.http import HttpResponse
from rest_framework import generics

class ShopView(generics.ListCreateAPIView):
    # list(self, request, *args, **kwargs)Will be able to generate custom JSON by using
    def list(self, request, *args, **kwargs):
        #List of JSON to finally return
        return_list = list()
        try:
            user_info = UserInfomation.objects.all() #Get all users with purchase experience
            for i in user_info:
                order = OrderItems.objects.filter(user=i) #Specify user
                shop = order[0].item.company.name #Specify company

                #Get product list
                order_items = dict()
                for i in order:
                    order_items[i.item.name] = {
                                                'price': i.item.price, #price
                                                'images': str(i.item.product_image) #Image URL
                                                } 


                total = sum([i.total for i in order]) #total fee. Get the total of the list generated by the list comprehension

                date_time_ = UserInfomation.objects.get(cart=i.cart)
                date_time = date_time_.day + date_time_.time #Get the desired delivery time
                
                #Put these data in the whole list
                return_list.append(
                    {
                        'user': str(i.user),   #User's email address
                        'shop': shop,          #The store you ordered
                        'order': order_items,  #List of ordered products
                        'total': total,        #total fee
                        'datetime': date_time, #Desired delivery time
                    }
                )    
        #Returns an empty list if an error occurs
        except:
            pass

        return Response(
            return_list
        )

After creating so far, I will send a get request to the endpoint created earlier

In [1]: {'Content-Type': 'application/json',
 'Authorization': 'JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo5LCJ1c2VybmFtZSI6ImIxODA2NDI5QGdtYWlsLmNvbSIsImV4cCI6MTU5NDc5OTMwNCwiZW1haWwiOiJiMTgwNjQyOUBnbWFpbC5jb20ifQ.Torhy69ZyKMOOxQUUv3Ebn9V6wqSwUlsQUD5IPUaDJA'}
In[2]: order_list = requests.get('http://localhost:8000/get_shop_view', headers=headers)
In [3]: order_list.json()
Out[3]:
[{'user': '[email protected]',
  'shop': 'Store B',
  'order': {'hamburger': {'price': 1000,
    'images': 'IMG_4145_ykxb9h'}},
  'total': 1000,
  'datetime': 'July 14, 2020 Now'},
 {'user': '[email protected]',
  'shop': 'Store B',
  'order': {'hamburger': {'price': 1000,
    'images': 'IMG_4145_ykxb9h'},
   'curry': {'price': 2,
    'images': '11967451898714_y6tgch'}},
  'total': 2002,
  'datetime': 'July 14, 2020 Now'},
 {'user': '[email protected]',
  'shop': 'Store B',
  'order': {'stew': {'price': 11111,
    'images': 'IMG_4900_oyb5ny'},
   'hamburger': {'price': 1000,
    'images': 'IMG_4145_ykxb9h'},
   'coffee': {'price': 199,
    'images': '54490_jawqyl'},
   'curry': {'price': 2,
    'images': '11967451898714_y6tgch'},
   'pancake': {'price': 100,
    'images': 'tweet_p7chgi'},
   'Takurinton': {'price': 100,
    'images': 'npyl13'}},
  'total': 24220,
  'datetime': 'July 16, 2020 Now'}]

This is the one I had put into the cart in advance, but it came back safely! !! !! happy~! !! !! It took about 5 hours and 45 minutes so far. .. ..

Try hitting with Java

Since the report is Java, I wanted to be able to hit it in Java, so I googled it and it was surprisingly easy. I'm a little happy because I thought it was a difficult language. .. .. LOL

Test.java


import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandler;
import java.nio.charset.StandardCharsets;
 
public class Test{
	public static void main(String[] args){
		try {
			HttpRequest request = HttpRequest
					.newBuilder(URI.create("http://localhost:8000/get_shop_view"))
                    .header("Content-Type", "application/json")
                    .header("Authorization", "JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo5LCJ1c2VybmFtZSI6ImIxODA2NDI5QGdtYWlsLmNvbSIsImV4cCI6MTU5NDc5OTMwNCwiZW1haWwiOiJiMTgwNjQyOUBnbWFpbC5jb20ifQ.Torhy69ZyKMOOxQUUv3Ebn9V6wqSwUlsQUD5IPUaDJA")
                    .GET()
					.build();
 
			BodyHandler<String> bodyHandler = HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8);
			HttpResponse<String> response = HttpClient.newBuilder().build().send(request, bodyHandler);
            
            String body = response.body();
            System.out.println(body);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

The code is still long, but it's acceptable. It seems to be a stance of writing in HttpRequest. You can use the same token in the header that you got earlier. I don't really understand it, but ...

Compile and run.

(base) Hogehoge:working takurinton$ javac Test.java
(base) Hogehoge:working takurinton$ java Test
[{'user': '[email protected]',
  'shop': 'Store B',
  'order': {'hamburger': {'price': 1000,
    'images': 'IMG_4145_ykxb9h'}},
  'total': 1000,
  'datetime': 'July 14, 2020 Now'},
 {'user': '[email protected]',
  'shop': 'Store B',
  'order': {'hamburger': {'price': 1000,
    'images': 'IMG_4145_ykxb9h'},
   'curry': {'price': 2,
    'images': '11967451898714_y6tgch'}},
  'total': 2002,
  'datetime': 'July 14, 2020 Now'},
 {'user': '[email protected]',
  'shop': 'Store B',
  'order': {'stew': {'price': 11111,
    'images': 'IMG_4900_oyb5ny'},
   'hamburger': {'price': 1000,
    'images': 'IMG_4145_ykxb9h'},
   'coffee': {'price': 199,
    'images': '54490_jawqyl'},
   'curry': {'price': 2,
    'images': '11967451898714_y6tgch'},
   'pancake': {'price': 100,
    'images': 'tweet_p7chgi'},
   'Takurinton': {'price': 100,
    'images': 'npyl13'}},
  'total': 24220,
  'datetime': 'July 16, 2020 Now'}]

I will return safely. With React and Vue, it gets more annoying because of the browser, but with Java it was easy because it was completed on the command line.

Summary

Actually, there are a lot of things such as user creation, user information acquisition, store information acquisition, product list that the store has, etc. other than what I wrote here. Since there are many parts around here that depend on one table, it is faster to create by specifying fields than custom, so it is important to use them properly. I'm tired of touching Java, so I'll leave it here today. LOL

Also, I was able to implement it in about 6 hours, but it seems to be a long journey because I still have to play with it more and more.

It's hard to get all the tasks in the online class with coronavirus! Do you want to test! College students who are thinking about it! Let's study together as much as the coronavirus and overcome this situation! I'll do my best for another 2 weeks! !!

Recommended Posts

Create an API with Django
Create an update screen with Django Updateview
[Python] Quickly create an API with Flask
Create an API server quickly with Python + Falcon
Create an environment with virtualenv
Create a homepage with django
Qiita API Oauth with Django
Load Django modules with an interpreter
Create Awaitable with Python / C API
[AWS] Create API with API Gateway + Lambda
Create an Excel file with Python3
Create API using hug with mod_wsgi
Create an age group with pandas
Note: Send an email with Django
GraphQL API with graphene_django in Django
Create a file uploader with Django
I'm trying to create an authentication / authorization process with Django
Create an authentication feature with django-allauth and CustomUser in Django
Create a web API that can deliver images with Django
Create a Todo app with Django ① Build an environment with Docker
Create a social integration API for smartphone apps with Django
Create an application by classifying with Pygame
Create RESTful APIs with Django Rest Framework
Automatically create Python API documentation with Sphinx
Create an image processing viewer with PySimpleGUI
Create an alias for Route53 to CloudFront with the AWS API
Browse an existing external database with Django
Internationalization with django
Quickly create an excel file with Python #python
Create an application using the Spotify API
Create an environment for Django x Apache x mod_wsgi with Vagrant (Ubuntu 16.04)
Create your first app with Django startproject
CRUD with Django
Create an add-in-enabled Excel instance with xlwings
Create an English word app with python
Create Page / Todo Block with Notion API
Create a REST API to operate dynamodb with the Django REST Framework
Create an upgradeable msi file with cx_Freeze
[AWS SAM] Create API with DynamoDB + Lambda + API Gateway
Create an academic society program with combinatorial optimization
Create an image composition app with Flask + Pillow
Create a dashboard for Network devices with Django!
Create Nginx + uWSGI + Python (Django) environment with docker
Create an image with characters in python (Japanese)
Create a one-file hello world application with django
How to create a Rest Api in Django
Authenticate Google with Django
Django 1.11 started with Python3.6
Upload files with Django
Create a Django schedule
Output PDF with Django
Markdown output with Django
Create an API to convert PDF files to TIF images with FastAPI and Docker
Use Gentelella with django
Twitter OAuth with Django
Getting Started with Django 1
Send email with Django
File upload with django
Create an app that works well with people's reports using the COTOHA API
Extrude with Fusion360 API
Create games with Pygame