[PYTHON] Create a Todo app with Django REST Framework + Angular

After studying, I implemented the API with Django REST Framework and tried to make a Todo application using the API with Angular2, so a memo

It is a state of what was made todo_django_angular.gif

environment

Django

Django setup

Installation of required libraries

$ pip install django
$ pip install djangorestframework
$ pip install django-filter
$ pip install django-cors-headers

Creating a project

$ django-admin startproject django_app

Creating an application

$ cd django_app
$ python manage.py startapp to_do

Creating a model

Simply use a Model with only the title and creation date and time

django_app/to_do/models.py


from django.db import models

class Todo(models.Model):
    title = models.CharField(max_length=140, blank=False)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

Reflect the application in settings.py

django_app/django_app/settings.py


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

migration

$ python manage.py makemigrations
$ python manage.py migrate

DB uses sqlite which does not require preparation

Create admin user

$ python manage.py createsuperuser

Set the user name, email address, and password appropriately

admin site setup

django_app/to_do/admin.py


from django.contrib import admin

from .models import Todo

@admin.register(Todo)
class Todo(admin.ModelAdmin):
    pass

Check admin site

python manage.py runserver

Try accessing and logging in to http: // localhost: 8000 / admin

State before login スクリーンショット 2017-05-01 16.11.01.png

State after login スクリーンショット 2017-05-01 16.11.43.png

Let's register some todos appropriately from Add of Todos

Configure the Django REST Framework to implement the API

Loading REST Framework

django_app/django_app/settings.py


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'to_do',
    'rest_framework', #Postscript
]

Serializer definition

Create serializer.py in django_app / to_do

django_app/to_do/serializer.py


from rest_framework import serializers

from .models import Todo

class TodoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'created_at')

View definition

Edit views.py in django_app / to_do

django_app/to_do/views.py


from django.shortcuts import render
import django_filters
from rest_framework import viewsets, filters

from .models import Todo
from .serializer import TodoSerializer

from rest_framework.decorators import api_view

class TodoViewSet(viewsets.ModelViewSet):
    queryset = Todo.objects.all().order_by('-created_at')
    serializer_class = TodoSerializer

URL pattern definition

django_app/django_app/urls.py


from django.conf.urls import url, include //Add include
from django.contrib import admin

from to_do.urls import router as to_do_router //Postscript

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/', include(to_do_router.urls)), //Postscript
]

The urls.py on the application side is not created at first, so create it

django_app/to_do/urls.py


from rest_framework import routers
from .views import TodoViewSet

router = routers.DefaultRouter()
router.register(r'todo', TodoViewSet)

API operation check

$ python manage.py runserver

Access http: // localhost: 8000 / api

You should see a screen like this スクリーンショット 2017-05-01 15.13.38.png

XMLHTML permission settings

Write the settings to make the prepared API accessible from Angular in settings.py

django_app/django_app/settings.py


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'to_do',
    'rest_framework',
    'corsheaders', //Postscript
]

MIDDLEWARE = [
    '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',
    'django.middleware.locale.LocaleMiddleware', //Postscript
    'corsheaders.middleware.CorsMiddleware', //Postscript
]

~ Omitted ~

//Postscript
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True

Angular

Install Angular CLI

$ npm install -g @angular/cli

Angular setup

Creating a project

$ ng new ng2app

It takes a little time

build

$ cd ng2app
$ ng build

Operation check

$ ng serve

Try to access http: // localhost: 4200 There should be a screen saying app works!

Display the Todo list

First of all, simply acquire the API generated by Django REST Framework and implement the function to display it.

Model definition

Create a folder called models in / ng2app / src / app Create a file called todo.model.ts in it

/ng2app/src/app/models/todo.model.ts


export class Todo {
  id: number;
  title: string;
}

Django's model defined created_at, but since it is unnecessary on the front side, only id and title are defined

Create service

Create a folder called services in / ng2app / src / app Create a file called todo.service.ts in it

Here, we will implement the movement of getting the API and passing the data.

typescript:/ng2app/src/app/services/todo.service.ts


import { Injectable } from "@angular/core";
import { Http, Headers } from '@angular/http';
import 'rxjs/add/operator/toPromise';

import { Todo } from '../models/todo.model';


@Injectable()
export class TodoService {
  todo: Todo[] = [];
  private Url = `http://127.0.0.1:8000/api/todo/`
  private headers = new Headers({'Content-Type': 'application/json'});

  constructor(
    private http: Http
  ){}

  //Get all todos
  getAllTodo(): Promise<Todo[]> {
    return this.http
      .get(this.Url)
      .toPromise()
      .then(response => response.json() as Todo[])
      .catch(this.handleError)
  }
}

Create component

Create a folder called components in / ng2app / src / app Create a file called todo-list.component.ts in it

typescript:/ng2app/src/app/components/todo-list.component.ts


import { Component,Input } from '@angular/core';
import { Router, ActivatedRoute, Params }   from '@angular/router';

import { TodoService } from '../services/todo.service';
import { Todo } from '../models/todo.model';

@Component({
  selector: 'todo-list',
  templateUrl: '../templates/todo-list.component.html',
  styleUrls: ['../static/todo-list.component.css']
})
export class TodoListComponent {
  todos: Todo[] = [];

  constructor(
    private todoService: TodoService,
  ){}
  ngOnInit(): void {
    this.todoService.getAllTodo()
      .then(todos => this.todos = todos);
  }
}

Creating html

Create a folder called templates in / ng2app / src / app Create todo-list.component.html in it

html:/ng2app/src/app/templates/todo-list.component.html


<div class="container">
    <div class="todo-list row">
      <div *ngFor="let todo of todos" class="col-sm-8 col-sm-offset-2">
         <div class="panel panel-default">
            <div class="panel-body">
              <span from="name">{{todo.title}}</span>
          </div>
      </div>
    </div>
</div>

Routing settings

Create app-routing.module.ts in / ng2app / src / app

typescript:/ng2app/src/app/app-routing.module.ts


import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { TodoListComponent }    from './components/todo-list.component';

const routes: Routes = [
  { path: '',  component: TodoListComponent }
];
@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

If you access http: // localhost: 4200, you can go to TodoListComponent.

Edit app.component

Edit /ng2app/src/app/app.component.ts as follows

typescript:/ng2app/src/app/app.component.ts


import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <h1 class="text-center">
      <span class="title">{{ title }}</span>
      <p class="sub-title">{{ subtitle }}</p>
    </h1>
    <router-outlet></router-outlet>
    `,
  styles: [
    '.title { color: #ee6e73;}',
    '.sub-title { font-size: small; }'
  ],
})
export class AppComponent {
  title = 'Simple Todo';
  subtitle = 'Angular2 + Django Rest Framework'
}

Loading various modules

Edit /ng2app/src/app/app.module.ts

typescript:/ng2app/src/app/app.module.ts


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppRoutingModule }   from './app-routing.module';
import { AppComponent } from './app.component';
import { TodoListComponent }      from './components/todo-list.component';
import { TodoService } from './services/todo.service';

@NgModule({
  declarations: [
    AppComponent,
    TodoListComponent,
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    AppRoutingModule
  ],
  providers: [TodoService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Place css file

Create a folder called static in / ng2app / src / app Create todo-list.component.css For the time being, describe as follows

/ng2app/src/app/static/todo-list.component.css


.todo-list {
  padding-top: 10px;
}
.todo {
  min-height: 130px;
}

.btn-circle {
  width: 30px;
  height: 30px;
  text-align: center;
  padding: 6px 0;
  font-size: 12px;
  line-height: 1.428571429;
  border-radius: 15px;
}

.add-todo {
  margin-top: 10px;
}

Install bootstrap module

Do the following

$ npm install --save bootstrap ng2-bootstrap

Edit the styles part of /ng2app/.angular-cli.json (note that it is a hidden file)

"styles": [
        "styles.css",
        "../node_modules/bootstrap/dist/css/bootstrap.css", //Postscript
      ],

Operation check

Start the application with the following command

$ ng serve

When you access http: // localhost: 4200 /, you should see something like the following. (If you have registered todo from Django's Admin) スクリーンショット 2017-05-01 16.57.51.png

Added the function to create a new Todo

Edit service

Added the following in TodoService of todo.service.ts

/ng2app/src/services/todo.service.ts


  //Behavior at the time of addition
  create(todo: Todo): Promise<Todo> {
    return this.http
      .post(this.Url, JSON.stringify(todo), {headers: this.headers})
      .toPromise()
      .then(res => res.json())
      .catch(this.handleError);
 }
 
  //Get one of the latest added todos
  getNewTodo(): Promise<Todo> {
    return this.http
      .get(this.Url+"?limit=1")
      .toPromise()
      .then(res => res.json().results)
      .catch(this.handleError)
  } 

Editing component

Add the following in TodoListComponent of todo.component.ts

/ng2app/src/components/todo.component.ts


export class TodoListComponent {
  todos: Todo[] = []; 
  newtodos: Todo[] = []; //Postscript
  @Input() todo: Todo = new Todo(); //Postscript

~ Omitted ~

  //Behavior when the save button is pressed
  save(): void {
    this.todoService
      .create(this.todo)
      .then(data => {this.getNewTodo()});
    this.todo = new Todo();
  }

  //Behavior to call the latest one
  getNewTodo(): void {
    this.todoService
      .getNewTodo()
      .then(res => {this.pushData(res)});
  }

  //Push data to newtodos to pass to html
  pushData(data: Todo): void {
    this.newtodos.unshift(data);
  }
}

When a new todo is added, save () executes create in TodoService and POSTs the new todo. After that, execute getNewTodo in TodoService with getNewTodo () and call the latest one (= POSTed todo). The one called is stored in newtodos by pushData ().

Editing html

Edit todo-list.component.html

/ng2app/src/templates/todo-list.component.html


<div class="container">
  <!--from here-->
  <div class="center">
    <div class="row">
      <div class="col-sm-8 col-sm-offset-2">
        <input [(ngModel)]="todo.title"
          id="input_text"
          type="text"
          length="140"
          class="form-control add-todo"
          placeholder="add-todo"
          (keydown.enter)="save()"
        >
        <button (click)="save()" class="btn btn-success pull-right add-todo">Add</button>
      </div>
    </div>
  </div>
  <div class="newtodo-list row" style="margin-top:10px;">
    <div *ngFor="let newtodo of newtodos" class="col-sm-8 col-sm-offset-2">
        <div class="panel panel-default">
          <div class="panel-body">
            <span from="name">{{newtodo[0].title}}</span>
          </div>
        </div>
    </div>
  </div>
  <hr class="col-sm-8 col-sm-offset-2">
  <!--So far-->
    <div class="todo-list row">
      <div *ngFor="let todo of todos" class="col-sm-8 col-sm-offset-2">
         <div class="panel panel-default">
            <div class="panel-body">
              <span from="name">{{todo.title}}</span>
          </div>
      </div>
    </div>
</div>

Operation check

Start the application with the following command

$ ng serve

It should be in the following state スクリーンショット 2017-05-01 17.20.19.png

You can add a new todo in the input part

Added the ability to remove Todo

Edit service

Added the following in TodoService of todo.service.ts

/ng2app/src/services/todo.service.ts


  //Behavior when deleting
  delete(id: number): Promise<void> {
    const url = `${this.Url}${id}/`;
    return this.http
      .delete(url, {headers: this.headers})
      .toPromise()
      .then(() => null)
      .catch(this.handleError);
  }

Editing component

Add the following in TodoListComponent of todo.component.ts

/ng2app/src/components/todo.component.ts


  //Behavior when the delete button is pressed
  delete(id): void {
    this.todoService
      .delete(id);
  }

Editing html

Edit todo-list.component.html

/ng2app/src/templates/todo-list.component.html


<div class="container">
    <div class="center">
      <div class="row">
        <div class="col-sm-8 col-sm-offset-2">
          <input [(ngModel)]="todo.title"
            id="input_text"
            type="text"
            length="140"
            class="form-control add-todo"
            placeholder="add-todo"
            (keydown.enter)="save()"
          >
          <button (click)="save()" class="btn btn-success pull-right add-todo">Add</button>
        </div>
      </div>
    </div>
    <div class="newtodo-list row" style="margin-top:10px;">
      <div *ngFor="let newtodo of newtodos" class="col-sm-8 col-sm-offset-2">
        <div [style.display]="!newtodo.hideElement ? 'inline':'none'">
          <div class="panel panel-default">
            <div class="panel-body">
              <span from="name">{{newtodo[0].title}}</span>
              <button (click)="delete(newtodo[0].id) || newtodo.hideElement=true"
                type="button"
                class="btn btn-success btn-circle pull-right"
              >
                <i class="glyphicon glyphicon-ok"></i>
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
    <hr class="col-sm-8 col-sm-offset-2">
    <div class="todo-list row">
      <div *ngFor="let todo of todos" class="col-sm-8 col-sm-offset-2">
        <div [style.display]="!todo.hideElement ? 'inline':'none'">
          <div class="panel panel-default">
            <div class="panel-body">
              <span from="name">{{todo.title}}</span>
              <button (click)="delete(todo.id) || todo.hideElement=true"
                type="button"
                class="btn btn-success
                btn-circle pull-right"
              >
                <i class="glyphicon glyphicon-ok"></i>
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
</div>

Add a button, call the delete method when the button is pressed & the element is set to display = none (I don't think it's cool here so I want to fix it)

Operation check

Start the application with the following command

$ ng serve

It should be in the following state スクリーンショット 2017-05-01 17.36.41.png

You can delete todo with the check button

Added the ability to edit Todo

Edit service

Added the following in TodoService of todo.service.ts

/ng2app/src/services/todo.service.ts


  //Behavior at the time of update
  update(todo: Todo): Promise<Todo> {
    const url = `${this.Url}${todo.id}/`;
    return this.http
      .put(url, JSON.stringify(todo), {headers: this.headers})
      .toPromise()
      .then(res => res.json())
      .catch(this.handleError);
  }

Editing component

Add the following in TodoListComponent of todo.component.ts

/ng2app/src/components/todo.component.ts


  //Behavior when updating todo
  update(id: number, title: string): void {
    let todo = {
      id: id,
      title: title
    }
    this.todoService.update(todo);
  }

(I want to fix it because it's not so cool here either)

Editing html

Edit todo-list.component.html

/ng2app/src/templates/todo-list.component.html


<div class="container">
    <div class="center">
      <div class="row">
        <div class="col-sm-8 col-sm-offset-2">
          <input [(ngModel)]="todo.title"
            id="input_text"
            type="text"
            length="140"
            class="form-control add-todo"
            placeholder="add-todo"
            (keydown.enter)="save()"
          >
          <button (click)="save()" class="btn btn-success pull-right add-todo">Add</button>
        </div>
      </div>
    </div>
    <div class="newtodo-list row" style="margin-top:10px;">
      <div *ngFor="let newtodo of newtodos" class="col-sm-8 col-sm-offset-2">
        <div [style.display]="!newtodo.hideElement ? 'inline':'none'">
          <div class="panel panel-default">
            <div class="panel-body">
              <span *ngIf="!newtodo.isEdit" (click)="newtodo.isEdit=true" from="name">{{newtodo[0].title}}</span>
              <input *ngIf="newtodo.isEdit"
                (focusout)="newtodo.isEdit=false || update(newtodo[0].id, newtodo[0].title)"
                [(ngModel)]="newtodo[0].title"
                id="input_text"
                type="text"
                length="140"
                style="border:none; width:70%"
              >
              <button (click)="delete(newtodo[0].id) || newtodo.hideElement=true"
                type="button"
                class="btn btn-success btn-circle pull-right"
              >
                <i class="glyphicon glyphicon-ok"></i>
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
    <hr class="col-sm-8 col-sm-offset-2">
    <div class="todo-list row">
      <div *ngFor="let todo of todos" class="col-sm-8 col-sm-offset-2">
        <div [style.display]="!todo.hideElement ? 'inline':'none'">
          <div class="panel panel-default">
            <div class="panel-body">
              <span *ngIf="!todo.isEdit" (click)="todo.isEdit=true" from="name">{{todo.title}}</span>
              <input *ngIf="todo.isEdit"
                (focusout)="todo.isEdit=false || update(todo.id, todo.title)"
                [(ngModel)]="todo.title"
                id="input_text"
                type="text"
                length="140"
                style="border:none; width:70%"
              >
              <button (click)="delete(todo.id) || todo.hideElement=true"
                type="button"
                class="btn btn-success
                btn-circle pull-right"
              >
                <i class="glyphicon glyphicon-ok"></i>
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
</div>

If you click on the todo string, it will switch to the input element, and if it loses focus from the input, update will be executed. (This is also cool (ry)

Operation check

Start the application with the following command

$ ng serve

Should behave like the gif at the beginning

that's all

I'm a super beginner, so it's better to do this, right? If there is something like that, please point it out.

Also, the source is here. Pull-Request welcome

from now on

For the time being, I have a feeling of incorporating user authentication into this application and going around there.

Referenced

[Implementing API at explosive speed using Django REST Framework --Qiita] (http://qiita.com/kimihiro_n/items/86e0a9e619720e57ecd8) I made a Todo app with Rails 5 + Angular2 + TypeScript. --DC4

Recommended Posts

Create a Todo app with Django REST Framework + Angular
Create a Todo app with the Django REST framework
Create a Todo app with Django ③ Create a task list page
Create a Todo app with Django ⑤ Create a task editing function
Create a Todo app with Django ① Build an environment with Docker
Create RESTful APIs with Django Rest Framework
Create a REST API to operate dynamodb with the Django REST Framework
Create a homepage with django
Create a Todo app with Django ④ Implement folder and task creation functions
Django REST framework with Vue.js
Login with django rest framework
Create APIs around user authentication with Django REST Framework
[Django] Use MessagePack with Django REST framework
Create a file uploader with Django
CRUD GET with Nuxt & Django REST Framework ②
Web App Development Practice: Create a Shift Creation Page with Django! (Shift creation page)
Create a GUI app with Python's Tkinter
CRUD POST with Nuxt & Django REST Framework
CRUD GET with Nuxt & Django REST Framework ①
Create a simple web app with flask
Create your first app with Django startproject
Web App Development Practice: Create a Shift Creation Page with Django! (Introduction)
CRUD PUT, DELETE with Nuxt & Django REST Framework
How to develop a cart app with Django
Create a dashboard for Network devices with Django!
Django REST framework A little useful to know.
How to create a multi-platform app with kivy
Create a one-file hello world application with django
A simple to-do list created with Python + Django
How to create a Rest Api in Django
Until you create a new app in Django
Implementing authentication in Django REST Framework with djoser
Create a Django schedule
Django REST framework basics
Django Rest Framework Tips
Create Django Todo list
Try to create a Todo management site using WebSocket with Django (Swamp Dragon)
I tried to create a table only with Django
I want to create an API that returns a model with a recursive relationship in the Django REST Framework
More new user authentication methods with Django REST Framework
Create a native GUI app with Py2app and Tkinter
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 1 ~
When you want to filter with Django REST framework
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 2 ~
Deploy a Django app made with PTVS on Azure
To myself as a Django beginner (1) --Create a project app--
To myself as a Django beginner (4) --Create a memo app--
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 3 ~
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 4 ~
[CRUD] [Django] Create a CRUD site using the Python framework Django ~ 5 ~
Create a django environment with docker-compose (MariaDB + Nginx + uWSGI)
Implement hierarchical URLs with drf-nested-routers in Django REST framework
Web App Development Practice: Create a Shift Creation Page with Django! (Write a base template)
Web App Development Practice: Create a Shift Creation Page with Django! (Authentication system processing)
Web App Development Practice: Create a Shift Creation Page with Django! (Experiment on admin page)
[Go language] Create a TUI app with Elm Architecture Create a slightly rich ToDo app with bubble tea
Todo app with django-bootstrap-modal-forms plugin
Create an API with Django
Create a (simple) REST server
Create ToDo List [Python Django]
Create a Django login screen