In this tutorial, we show you Angular 6 Http Client & Django Server example that uses Django to do CRUD with PostgreSQL (including finder method) and Angular 6 as front-end technology to make request and receive response.
Related Post: Django RestApis example – GET/POST/PUT/DELETE requests to PostgreSQL database
Technologies
– Django 2.1
– Angular 6
– RxJS 6
– PostgreSQL 9.5
Project Overview
1. Django Server
With this system, we can use Angular Client to work with PostgreSQL Database via Django Server which has APIs:
- GET
api/customers/
: get all customers - GET
api/customers/[id]
: get a customer byid
- GET
api/customers/age/[age]
: find all customers byage
- POST
api/customers/
: save a customer - PUT
api/customers/[id]
: update a customer byid
- DELETE
api/customers/[id]
: delete a customer byid
- DELETE
api/customers/
: delete all customers
2. Angular 6 Client
The image below shows overview about Angular Components that we will create:
Django RestApi server
Project structure
There are several folders and files in our Django project:
– customers/apps.py: declares CustomersConfig
class (subclass of the django.apps.AppConfig
) that represents our Django app and its configuration.
– gkzRestApi/settings.py: configures settings for the Django project, including INSTALLED_APPS
list with Django REST framework and Customers Application.
– customers/models.py: defines Customer
data model class (subclass of the django.db.models.Model
).
– migrations/0001_initial.py: is generated by makemigrations
command, includes the code to create the Customer
model, will be run by migrate
to generate PostgreSQL database table for Customer
model.
– customers/serializers.py: declares CustomerSerializer
class (subclass of rest_framework.serializers.ModelSerializer
) for Customer
instances to manage serialization to JSON and deserialization from JSON.
– customers/views.py: contains methods to process HTTP requests and produce HTTP responses (using CustomerSerializer
).
– customers/urls.py: defines urlpatterns
to be matched with request functions in the views.py.
– gkzRestApi/urls.py: defines root URL configurations that includes the URL patterns declared in customers/urls.py.
Setup Django RestApi project
Install Django REST framework
Django REST framework works on top of Django and helps us to build RESTful Web Services flexibly. To install this package, run command:
pip install djangorestframework
Create RestApi project
Create Django project named gkzRestApi with command:
django-admin startproject gkzRestApi
Install Python PostgreSQL adapter
We have to install Python PostgreSQL adapter to work with PostgreSQL database.
In this tutorial, we use psycopg2: pip install psycopg2
.
Setup PostgreSQL Database engine
Open gkzRestApi/settings.py and change declaration of DATABASES
:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'testdb', 'USER': 'postgres', 'PASSWORD': '123', 'HOST': '127.0.0.1', 'PORT': '5432', } }
Create Customers App
Run following commands to create new Django App named customers:
– cd gkzRestApi
– python manage.py startapp customers
Open customers/apps.py, we can see CustomersConfig
class (subclass of the django.apps.AppConfig
) that represents our Django app and its configuration:
from django.apps import AppConfig class CustomersConfig(AppConfig): name = 'customers'
Add Django Rest framework & RestApi App to Django project
Open gkzRestApi/settings.py, find INSTALLED_APPS
, then add:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # Django REST framework 'rest_framework', # Customers application 'customers.apps.CustomersConfig', ]
Add CORS Configurations
Inside gkzRestApi/settings.py, add:
INSTALLED_APPS = [ ... # CORS 'corsheaders', ] 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', # CORS 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', ] CORS_ORIGIN_ALLOW_ALL = False CORS_ORIGIN_WHITELIST = ( 'localhost:4200', )
Implement Django RestApi App
Data Model
Create Data Model
customers/models.py
from django.db import models class Customer(models.Model): name = models.CharField(max_length=70, blank=False, default='') age = models.IntegerField(blank=False, default=1) active = models.BooleanField(default=False)
Run initial migration for data model
Run following Python script:
python manage.py makemigrations customers
We can see output text:
Migrations for 'customers': customers\migrations\0001_initial.py - Create model Customer
It indicates that the customers/migrations/0001_initial.py file includes code to create Customer
data model:
# Generated by Django 2.1.7 on 2019-03-07 01:28 from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='Customer', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(default='', max_length=70)), ('age', models.IntegerField(default=1)), ('active', models.BooleanField(default=False)), ], ), ]
The generated code defines a subclass of the django.db.migrations.Migration
. It has an operation for creating Customer
model table. Call to migrations.CreateModel()
method will create a table that allows the underlying database to persist the model.
Run the following Python script to apply the generated migration:
python manage.py migrate customers
The output text:
Operations to perform: Apply all migrations: customers Running migrations: Applying customers.0001_initial... OK
Check PostgreSQL Database, now we can see that a table for Customer
model was generated and it’s named customers_customer:
Create Serializer class
We need a Serializer
class for Customer
instances to manage serialization to JSON and deserialization from JSON.
– This CustomerSerializer
will inherit from rest_framework.serializers.ModelSerializer
superclass.
– ModelSerializer
class automatically populates a set of default fields and default validators, we only need to specify the model class.
Now, under customers package, create serializers.py file:
from rest_framework import serializers from customers.models import Customer class CustomerSerializer(serializers.ModelSerializer): class Meta: model = Customer fields = ('id', 'name', 'age', 'active')
Meta
inner class declares 2 attributes:
– model
: specifies the model related to the serializer
– fields
: specifies a tuple of field names that we want to include in the serialization
Create API Views
Open customers/views.py file and declare two functions:
– customer_list()
: get list of customers, save a new customer, delete all customers
– customer_detail()
: get/update/delete customer by ‘id’
– customer_list_age()
: find all customers by ‘age’
from django.shortcuts import render from django.http import HttpResponse from django.http.response import JsonResponse from django.views.decorators.csrf import csrf_exempt from rest_framework.parsers import JSONParser from rest_framework import status from customers.models import Customer from customers.serializers import CustomerSerializer @csrf_exempt def customer_list(request): if request.method == 'GET': customers = Customer.objects.all() customers_serializer = CustomerSerializer(customers, many=True) return JsonResponse(customers_serializer.data, safe=False) # In order to serialize objects, we must set 'safe=False' elif request.method == 'POST': customer_data = JSONParser().parse(request) customer_serializer = CustomerSerializer(data=customer_data) if customer_serializer.is_valid(): customer_serializer.save() return JsonResponse(customer_serializer.data, status=status.HTTP_201_CREATED) return JsonResponse(customer_serializer.errors, status=status.HTTP_400_BAD_REQUEST) elif request.method == 'DELETE': Customer.objects.all().delete() return HttpResponse(status=status.HTTP_204_NO_CONTENT) @csrf_exempt def customer_detail(request, pk): try: customer = Customer.objects.get(pk=pk) except Customer.DoesNotExist: return HttpResponse(status=status.HTTP_404_NOT_FOUND) if request.method == 'GET': customer_serializer = CustomerSerializer(customer) return JsonResponse(customer_serializer.data) elif request.method == 'PUT': customer_data = JSONParser().parse(request) customer_serializer = CustomerSerializer(customer, data=customer_data) if customer_serializer.is_valid(): customer_serializer.save() return JsonResponse(customer_serializer.data) return JsonResponse(customer_serializer.errors, status=status.HTTP_400_BAD_REQUEST) elif request.method == 'DELETE': customer.delete() return HttpResponse(status=status.HTTP_204_NO_CONTENT) @csrf_exempt def customer_list_age(request, age): customers = Customer.objects.filter(age=age) if request.method == 'GET': customers_serializer = CustomerSerializer(customers, many=True) return JsonResponse(customers_serializer.data, safe=False) # In order to serialize objects, we must set 'safe=False'
Route Urls to Views functions
Create urls.py in customers folder, now we will define urlpatterns
to be matched with request functions in the views.py.
from django.conf.urls import url from customers import views urlpatterns = [ url(r'^customers/$', views.customer_list), url(r'^customers/(?P[0-9]+)$', views.customer_detail), url(r'^customers/age/(?P [0-9]+)/$', views.customer_list_age), ]
Now we must include above URL patterns in root URL configurations.
Open gkzRestApi/urls.py, replace the code:
from django.conf.urls import url, include urlpatterns = [ url(r'^api/', include('customers.urls')), ]
Angular Client
Project Structure
We have:
– 4 components: customers-list, customer-details, create-customer, search-customer.
– 3 modules: FormsModule, HttpClientModule, AppRoutingModule.
– customer.ts: class Customer (id, firstName, lastName)
– customer.service.ts: Service for Http Client methods
– app-routing.module.ts: Routing configuration
Setup Angular Project
Create Angular Project
Run command: ng new AngularDjango
.
Create Service & Components
On Project folder, run commands below:
– ng g s customer
– ng g c create-customer
– ng g c customer-details
– ng g c customers-list
– ng g c search-customers
On each Component selector, delete app-
prefix, then change tslint.json rules
– "component-selector"
to false.
App Module
Check app.module.ts file, we can see Angular Components & Service are added automatically.
Now we need to import FormsModule
, HttpClientModule
for using form submission and HTTP requests to Django server. We also import AppRoutingModule
for routing (will be created later in this tutorial).
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { CreateCustomerComponent } from './create-customer/create-customer.component'; import { CustomerDetailsComponent } from './customer-details/customer-details.component'; import { CustomersListComponent } from './customers-list/customers-list.component'; import { SearchCustomersComponent } from './search-customers/search-customers.component'; import { AppRoutingModule } from './app-routing.module'; @NgModule({ declarations: [ AppComponent, CreateCustomerComponent, CustomerDetailsComponent, CustomersListComponent, SearchCustomersComponent ], imports: [ BrowserModule, FormsModule, AppRoutingModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Implement Angular Client App
Data Model
Create new file named customer.ts:
export class Customer { id: number; name: string; age: number; active: boolean; }
Data Service
This service uses Angular HttpClient
object to make get/post/put/delete request to Django Rest Api Server.
customer.service.ts
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CustomerService { private baseUrl = 'http://localhost:8000/api/customers'; constructor(private http: HttpClient) { } getCustomer(id: number): Observable<Object> { return this.http.get(`${this.baseUrl}/${id}`); } createCustomer(customer: Object): Observable<Object> { return this.http.post(`${this.baseUrl}/`, customer); } updateCustomer(id: number, value: any): Observable<Object> { return this.http.put(`${this.baseUrl}/${id}`, value); } deleteCustomer(id: number): Observable<any> { return this.http.delete(`${this.baseUrl}/${id}`); } getCustomersList(): Observable<any> { return this.http.get(`${this.baseUrl}/`); } getCustomersByAge(age: number): Observable<any> { return this.http.get(`${this.baseUrl}/age/${age}/`); } deleteAll(): Observable<any> { return this.http.delete(`${this.baseUrl}/`); } }
Components
List of Customers
This component calls Data service’s getCustomersList()
function, then shows the result as Customer
list. It also has deleteCustomers()
function that calls Data service’s deleteAll()
function to delete all customers.
customers-list/customers-list.component.ts
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { CustomerService } from '../customer.service'; import { Customer } from '../customer'; @Component({ selector: 'customers-list', templateUrl: './customers-list.component.html', styleUrls: ['./customers-list.component.css'] }) export class CustomersListComponent implements OnInit { customers: Observable; constructor(private customerService: CustomerService) { } ngOnInit() { this.reloadData(); } deleteCustomers() { this.customerService.deleteAll() .subscribe( data => { console.log(data); this.reloadData(); }, error => console.log('ERROR: ' + error)); } reloadData() { this.customers = this.customerService.getCustomersList(); } }
customers-list/customers-list.component.html
<h1>Customers</h1> <div *ngFor="let customer of customers | async" style="width: 300px;"> <customer-details [customer]='customer'></customer-details> </div> <div> <button type="button" class="button btn-danger" (click)='deleteCustomers()'>Delete All</button> </div>
This template embeds customer-details
component that we will implement right here.
Customer Details
customer-details/customer-details.component.ts
import { Component, OnInit, Input } from '@angular/core'; import { CustomerService } from '../customer.service'; import { Customer } from '../customer'; import { CustomersListComponent } from '../customers-list/customers-list.component'; @Component({ selector: 'customer-details', templateUrl: './customer-details.component.html', styleUrls: ['./customer-details.component.css'] }) export class CustomerDetailsComponent implements OnInit { @Input() customer: Customer; constructor(private customerService: CustomerService, private listComponent: CustomersListComponent) { } ngOnInit() { } updateActive(isActive: boolean) { this.customerService.updateCustomer(this.customer.id, { name: this.customer.name, age: this.customer.age, active: isActive }) .subscribe( data => { console.log(data); this.customer = data as Customer; }, error => console.log(error)); } deleteCustomer() { this.customerService.deleteCustomer(this.customer.id) .subscribe( data => { console.log(data); this.listComponent.reloadData(); }, error => console.log(error)); } }
As you can see, this component receives input Customer
data object from parent component (CustomersListComponent
), then apply the information on Data service’s functions: updateCustomer()
and deleteCustomer()
..
customer-details/customer-details.component.html
<div *ngIf="customer"> <div> <label>Name: </label> {{customer.name}} </div> <div> <label>Age: </label> {{customer.age}} </div> <div> <label>Active: </label> {{customer.active}} </div> <span class="button is-small btn-primary" *ngIf='customer.active' (click)='updateActive(false)'>Inactive</span> <span class="button is-small btn-primary" *ngIf='!customer.active' (click)='updateActive(true)'>Active</span> <span class="button is-small btn-danger" (click)='deleteCustomer()'>Delete</span> <hr /> </div>
Create Customer
This component uses Data service’s createCustomer()
function to save Customer information to the database.
create-customer/create-customer.component.ts
import { Component, OnInit } from '@angular/core'; import { Customer } from '../customer'; import { CustomerService } from '../customer.service'; @Component({ selector: 'create-customer', templateUrl: './create-customer.component.html', styleUrls: ['./create-customer.component.css'] }) export class CreateCustomerComponent implements OnInit { customer: Customer = new Customer(); submitted = false; constructor(private customerService: CustomerService) { } ngOnInit() { } newCustomer(): void { this.submitted = false; this.customer = new Customer(); } save() { this.customerService.createCustomer(this.customer) .subscribe( data => { console.log(data); this.submitted = true; }, error => console.log(error)); this.customer = new Customer(); } onSubmit() { this.save(); } }
create-customer/create-customer.component.html
<h3>Create Customer</h3> <div [hidden]="submitted" style="width: 300px;"> <form (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" id="name" required [(ngModel)]="customer.name" name="name"> </div> <div class="form-group"> <label for="age">Age</label> <input type="text" class="form-control" id="age" required [(ngModel)]="customer.age" name="age"> </div> <button type="submit" class="btn btn-success">Submit</button> </form> </div> <div [hidden]="!submitted"> <h4>You submitted successfully!</h4> <button class="btn btn-success" (click)="newCustomer()">Add</button> </div>
Search Customers
This component uses Data services’s getCustomersByAge()
function as finder method to find customers by age
search-customers/search-customers.component.ts
import { Component, OnInit } from '@angular/core'; import { Customer } from '../customer'; import { CustomerService } from '../customer.service'; @Component({ selector: 'search-customers', templateUrl: './search-customers.component.html', styleUrls: ['./search-customers.component.css'] }) export class SearchCustomersComponent implements OnInit { age: number; customers: Customer[]; constructor(private dataService: CustomerService) { } ngOnInit() { this.age = 0; } private searchCustomers() { this.customers = []; this.dataService.getCustomersByAge(this.age) .subscribe(customers => this.customers = customers); } onSubmit() { this.searchCustomers(); } }
search-customers/search-customers.component.html
<h3>Find By Age</h3> <div style="width: 300px;"> <form (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="lastname">Age</label> <input type="text" class="form-control" id="age" required [(ngModel)]="age" name="age"> </div> <div class="btn-group"> <button type="submit" class="btn btn-success">Submit</button> </div> </form> </div> <ul> <li *ngFor="let customer of customers"> <h4>{{customer.id}} - {{customer.name}} {{customer.age}}</h4> </li> </ul>
Add Router
app-routing.module.ts
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CustomersListComponent } from './customers-list/customers-list.component'; import { CreateCustomerComponent } from './create-customer/create-customer.component'; import { SearchCustomersComponent } from './search-customers/search-customers.component'; const routes: Routes = [ { path: '', redirectTo: 'customer', pathMatch: 'full' }, { path: 'customer', component: CustomersListComponent }, { path: 'add', component: CreateCustomerComponent }, { path: 'findbyage', component: SearchCustomersComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
And AppComponent HTML for routing:
app.component.html
<div style="padding: 20px;"> <h1 style="color: blue">ozenero.com</h1> <h3>Angular - Django App</h3> <nav> <a routerLink="customer" class="btn btn-primary active" role="button" routerLinkActive="active">Customers</a> <a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a> <a routerLink="findbyage" class="btn btn-primary active" role="button" routerLinkActive="active">Search</a> </nav> <router-outlet></router-outlet> </div>
Run & Check Results
– Run Django server with command: python manage.py runserver
.
– Run Angular App with command: ng serve
.
– Open browser for url http://localhost:4200/
:
Add Customer:
Check database after saving Customers:
Show Customers:
Click on Active button to update Customer status:
Check database after updating:
Search Customers by Age:
Delete a Customer:
Check database after deleting a Customer:
Delete All Customers:
Now the table is empty:
Source Code
– Django-RestApi-server-PostgreSQL
– AngularDjango-PostgreSQL