Implemented in Nuxt.js & Django using Docker [CRUD in Axios]

Introduction

Thank you for accessing this article. The poster is new to programming and I'm not sure if this method is ** "optimal" **.

I will use Nuxt for the front and Django for the back end in my next work, so I created this article to understand the whole flow as much as possible. I was able to confirm a similar article up to the environment construction, but I would like to implement CRUD processing as well.

What can I get from this article?

・ Nuxt and Django environment construction using Docker -CRUD implementation with Axios in the above environment (get, post, put, delete, there are few articles dealing with all)

Start immediately

For environment construction using Docker, I referred to the article here. It is an image of environment construction completion. スクリーンショット 2021-01-06 16.07.03.png

Directory structure

.
├─django
│  ├─manage.py
│  ├─qiitaexample
│  │   ├─settings.py
│  │   ├─urls.py
│  │   ├─wsgi.py
│  │   └─__init__.py
│  └─myapp
│      ├─migrations
│      ├─admin.py
│      ├─apps.py
│      ├─models.py
│      ├─renderers.py
│      ├─serializers.py
│      ├─tests.py
│      ├─urls.py
│      ├─views.py
│      └─__init__.py
│
├─docker-compose.yml
│
├─dockerfiles
│  ├─django_docker
│  │   ├─dockerfile
│  │   └─requirements.txt
│  └─nuxt_docker
│      └─dockerfile
│
├─mysql
│  └─conf.d
│
└─nuxt
    └─front
└─Omitted below

File contents required at the initial stage

docker-compose.yml

docker-compose.yml


version: '3'

services:
  db:
    image: mysql:latest
    restart: always
    container_name: mysql
    environment:
      MYSQL_ROOT_PASSWORD: test
      MYSQL_USER: test
      MYSQL_DATABASE: test
      MYSQL_PASSWORD: test
    ports:
      - 3306:3306
    expose:
      - 3306
    volumes:
      - mysqldata:/var/lib/mysql
      - ./mysql/conf.d:/etc/mysql/conf.d
    command: --default-authentication-plugin=mysql_native_password

  web:
    container_name: django
    build: ./dockerfiles/django_docker
    command:
      python3 manage.py runserver 0.0.0.0:8000
    volumes:
      - ./django:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

  front:
    container_name: nuxt
    build: ./dockerfiles/nuxt_docker
    tty: true
    volumes:
      - ./nuxt:/code
    ports:
      - "3000:3000"

volumes:
  mysqldata:

dockerfiles/nuxt_doceker/dockerfile

FROM node:latest

RUN mkdir -p /code

ENV NODE_ENV=development

RUN yarn install
RUN yarn add @nuxtjs/axios

WORKDIR /code

EXPOSE 3000

dockerfiles/django_doceker/dockerfile

FROM python:3.7
ENV PYTHONUNBUFFERED 1

RUN mkdir /code
WORKDIR /code

COPY requirements.txt /code/
RUN pip install --upgrade pip
RUN pip install -r requirements.txt

COPY . /code/

dockerfiles/django_doceker/requriements.txt

requriements.txt


Django
djangorestframework
django-webpack-loader
django-cors-headers
mysqlclient

Django settings

Creating a project

Launch the db container with docker-compose and change from Sqlite to MySQL settings.

> docker-compose up -d db

Next, launch the container with docker-compose. Since this is the first time, we will also build.

> docker-compose up -d --build

Create a Django project. (Project name is arbitrary)

> docker-compose run web django-admin.py startproject qiitaexample .

Next, edit setting.py.

#<DATABASES'default'Rewrite inside>

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'test',
        'USER': 'test',
        'PASSWORD': 'test',
        'HOST': 'db',   
        'PORT': '3306',
    }
}

DB migration will be performed after changing DATABASES.

> docker-compose run web ./manage.py migrate

Then create a superuser.

> docker-compose run web ./manage.py createsuperuser --username admin --email admin@localhost
Password: #You will be asked for a password, so enter it
Password (again): #Enter again
The password is too similar to the username.
This password is too short. It must contain at least 8 characters.
This password is too common. #Warning if password is short
Bypass password validation and create user anyway? [y/N]: y #Enter y
Superuser created successfully.

Start all docker-compose up -d command containers and check the behavior to see if they are working properly.

> docker-compose up -d

> docker ps 

#3 containers should be working
・ Container 1
・ Container 2
・ Container 3

The django development server is listening on port 8000, so Try accessing http: // localhost: 8000/admin. On the screen that appears, enter your Username (admin in this case) and password. Then, two items, Groups and Users, will be displayed.

Creating an app

Now, let's create an app. (Here, create it with the name myapp)

> docker-compose run web ./manage.py startapp myapp

Add myapp to setting.py so that it can be used.

./django/qiitaexample/setting.py


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp', #add to
]

I will add the model to models.py. Here, we will create a model called Student. After adding models.py, a table will be created on MySQL by migrating.

./django/myapp/models.py


from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=100)
    course = models.CharField(max_length=100)
    rating = models.IntegerField()

    def __str__(self):
      return self.name

    class Meta:
        ordering = ['name']

Migrate below. If you added an app, you need make migrations.

> docker-compose run web ./manage.py makemigrations
> docker-compose run web ./manage.py migrate

You can check the created model from the management screen by adding the following to admin.py.

./django/myapp/admin.py


from django.contrib import admin
from .models import Student

admin.site.register(Student)

If you connect to http: // localhost: 8000/admin/again, you can see that MYAPP and Student are increasing. Register the Student appropriately by GUI operation, enter the MySQL container and check it.

> docker exec -it mysql /bin/bash #Enter the MySQL container

> mysql -u test -p #Start MySQL
Enter password: #In the example of this article, you can log in with test

> use test;
> select * from myapp_student;

#If you can confirm< shift + d >Get out of the container with

Build a Django REST framework

Now that Django is ready, it's time to create an API on the Django side. First, add it to setting.py. (The processing required for communication with Nuxt is also described in advance)

./django/qiitaexample/setting.py


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp', 
    'rest_framework', #add to
    'corsheaders', #add to
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware', #add to
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CORS_ORIGIN_WHITELIST = [
    'http://localhost:3000',
]

CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True

By adding'rest_framework', you will be able to use the features of the Django REST framework. Next, manually create new serializer.py and urls.py (under myapp). After creating, add the following contents to serializer.py, urls.py, views.py

./django/myapp/serializers.py


from rest_framework.serializers import ModelSerializer
from .models import Student

class StudentSerializer(ModelSerializer):
  class Meta:
    model = Student
    fields = ['id' , 'name' ,'course' ,'rating']

./django/myapp/views.py


from django.shortcuts import render
from rest_framework.generics import ListAPIView, CreateAPIView,UpdateAPIView,RetrieveAPIView,DestroyAPIView
from rest_framework.permissions import AllowAny
from .models import Student
from .serializers import StudentSerializer

class StudentsViewSet(ListAPIView):
    model = Student
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    permission_classes = (AllowAny, )

class StudentsDetailSet(RetrieveAPIView):
    model = Student
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    permission_classes = (AllowAny, )

class StudentsCreateSet(CreateAPIView):
    model = Student
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    permission_classes = (AllowAny, )

class StudentsUpdateSet(UpdateAPIView):
    model = Student
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    permission_classes = (AllowAny, )

class StudentsDeleteSet(DestroyAPIView):
    model = Student
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    permission_classes = (AllowAny, )

./django/myapp/urls.py


from django.urls import path
from .views import StudentsViewSet, StudentsCreateSet, StudentsUpdateSet,StudentsDetailSet,StudentsDeleteSet

urlpatterns = [
    path('get_myapp/', StudentsViewSet.as_view()),
    path('get_myapp/<int:pk>/', StudentsDetailSet.as_view()),
    path('post_myapp/', StudentsCreateSet.as_view()),
    path('put_myapp/<int:pk>/', StudentsUpdateSet.as_view()),
    path('delete_myapp/<int:pk>/', StudentsDeleteSet.as_view()),
]

Edit urls.py under qiitaexample and enable urls.py under myapp.

from django.contrib import admin
from django.urls import path,include
from myapp import urls

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

Nuxt.js settings

First, go into the nuxt.js container and create a project (project name is arbitrary).

After entering yarn create nuxt-app front, all the choices are basically default values, This time I have selected SPA.

> docker exec -it nuxt /bin/bash

> yarn create nuxt-app front

Before running the development server for confirmation, add it to nuxt.config.js. The following is required to run nuxt.js on docker. Also, enable hot reload when editing from here. You need to specify it explicitly to develop on docker.

Furthermore, I will add the settings of axios. axios is an HTTP client that runs on node.js, and uses this to hit the URL of the API from nuxt.js. Since it is described in the dockerfile of nuxt mentioned above, it is already installed in the container. Add the following to modules and add axios.

./nuxt/front/nuxt.config.js


  server: {
    port: 3000,
    host: '0.0.0.0',  
  },

  watchers: {
    webpack: {
      poll: true
    }
  }


//~Abbreviation~
  modules: [
    '@nuxtjs/axios',
  ],
  axios: {
    baseURL: "http://localhost:8000"
  },
//~Abbreviation~

Now you can see Nuxt.js on docker

> cd front

> yarn run dev

If the initial screen of Nuxt.js appears safely, it is a success for the time being.

CRUD implementation with Axios Nuxt ⇆ Django

From here, I will actually describe the data transfer between Nuxt and Django. Create a new TestForm in the components.

./nuxt/front/components/TestForm.vue


<template>
  <div>
    <div>
      <form @submit.prevent="submitForm(student)">
        <div class="from-group row">
          <input
            type="text"
            class="form-control col-3 mx-2"
            placeholder="Name"
            v-model="student.name"
          />
          <input
            type="text"
            class="form-control col-3 mx-2"
            placeholder="Course"
            v-model="student.course"
          />
          <input
            type="text"
            class="form-control col-3 mx-2"
            placeholder="Rating"
            v-model="student.rating"
          />
          <button class="btn btn-success">Submit</button>
        </div>
      </form>
    </div>
    <div>
      <table class="tabel">
        <thead>
          <th>Name</th>
          <th>Course</th>
          <th>Rating</th>
        </thead>
        <tbody>
          <tr
            v-for="student in students"
            :key="student.id"
            @dblclick="$data.student = student"
          >
            <td>{{ student.name }}</td>
            <td>{{ student.course }}</td>
            <td>{{ student.rating }}</td>
            <td><button @click="deleteStudent(student)">x</button></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      student: {
        name: "",
        course: "",
        rating: "",
      },
      students: [],
    };
  },
  async created() {
    await this.getStudents();
  },
  methods: {
    submitForm(student) {
 //Crete for new POST,Conditional branch to use put method when there is existing data
      if (this.student.id === undefined) {
        this.createStudent();
      } else {
        this.editStudent(student);
      }
    },
    //Get full rights to data
    async getStudents() {
      const url = "/api/get_myapp/";
      const response = await this.$axios.get(url);
      this.students = response.data;
    },
    //New data registration
    async createStudent() {
      await this.getStudents();
      const url = "/api/post_myapp/";
      this.$axios.defaults.xsrfCookieName = "csrftoken";
      this.$axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
      await this.$axios.post(url, {
        name: this.student.name,
        course: this.student.course,
        rating: this.student.rating,
      });
      await this.getStudents();
    },
   //Edit data
    async editStudent(student) {
      await this.getStudents();
      const url = "/api/put_myapp/" + student.id + "/";
      const modify = {
        name: this.student.name,
        course: this.student.course,
        rating: this.student.rating,
      };
      await this.$axios.put(url, modify);
      await this.getStudents();
      this.student = {};
    },
   //Delete data
    async deleteStudent(student) {
      await this.getStudents();
      const url = "/api/delete_myapp/" + student.id + "/";
      const modify = {
        name: this.student.name,
        course: this.student.course,
        rating: this.student.rating,
      };
      await this.$axios.delete(url)
      await this.getStudents();
    },
  },
};
</script>

Read the created TestForm. If you don't need it, you can delete the initial display part in the template.

./nuxt/front/pages/index.vue


//Postscript
  <div>
      <TestForm />
   </div>

--------------------
<script>
import TestForm from '~/components/TestForm.vue'
export default {
  components:{
    TestForm
  }
}
</script>

This completes the CRUD processing code in Aixos. Finally, let's launch all the containers and do yarn run dev in the Nuxt container to check.

> docker-compose up -d

> docker exex -it nuxt  /bin/bash

> cd front

> yarn run dev

in conclusion

Thank you for your hard work. This completes the Nuxt.js ⇆ Django data linkage. Personally, I forgot to write the following code in urls.py when putting with Axios, and I was told 404 not found all the time and struggled a little. When I solved it, I couldn't see the page (get information for each unit) because I didn't set the url of the detail page.

path('get_myapp/<int:pk>/', StudentsDetailSet.as_view()),

** You have to read the error code to determine what is wrong, predict it, and implement it. I thought again **.

As an aside, here are some links that helped me understand DRF. How to customize the Django REST framework-Tutorial supplement How to use DRF generic view

I hope it helps you even a little.

Recommended Posts

Implemented in Nuxt.js & Django using Docker [CRUD in Axios]
Data management using volume in Docker (personal memorandum)
Support out of support in docker environment using centos6
Docker in LXD