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
Django
$ pip install django
$ pip install djangorestframework
$ pip install django-filter
$ pip install django-cors-headers
$ django-admin startproject django_app
$ cd django_app
$ python manage.py startapp to_do
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
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
]
$ python manage.py makemigrations
$ python manage.py migrate
DB uses sqlite which does not require preparation
$ python manage.py createsuperuser
Set the user name, email address, and password appropriately
django_app/to_do/admin.py
from django.contrib import admin
from .models import Todo
@admin.register(Todo)
class Todo(admin.ModelAdmin):
pass
python manage.py runserver
Try accessing and logging in to http: // localhost: 8000 / admin
State before login
State after login
Let's register some todos appropriately from Add of Todos
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
]
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')
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
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)
$ python manage.py runserver
Access http: // localhost: 8000 / api
You should see a screen like this
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
$ npm install -g @angular/cli
$ ng new ng2app
It takes a little time
build
$ cd ng2app
$ ng build
$ ng serve
Try to access http: // localhost: 4200
There should be a screen saying app works!
First of all, simply acquire the API generated by Django REST Framework and implement the function to display it.
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 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 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);
}
}
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>
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 /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'
}
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 { }
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;
}
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
],
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)
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)
}
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 ().
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>
Start the application with the following command
$ ng serve
It should be in the following state
You can add a new todo in the input part
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);
}
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);
}
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)
Start the application with the following command
$ ng serve
It should be in the following state
You can delete todo with the check button
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);
}
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)
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)
Start the application with the following command
$ ng serve
Should behave like the gif at the beginning
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
For the time being, I have a feeling of incorporating user authentication into this application and going around there.
[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