Django Angular 10 CRUD Example – MySQL + Django Rest Framework Tutorial

Django Angular 10 CRUD Example – Fullstack Angular 10 + Django Rest Framework + MySQL Tutorial

Tutorial: Django Angular 10 CRUD Example – Fullstack: Angular 10 + Django Rest Framework + MySQL.

Django is a Python-based free and open-source web framework that follows the model-template-view architectural pattern. Angular is a TypeScript-based open-source web application framework led by the Angular Team at Google. In the tutorial, I introduce how to build a Angular Django CRUD RestAPIs Fullstack Project with POST/GET/PUT/DELETE requests using Django Rest Framework and MySQL database with step by step coding examples.

Related posts:


What will we do?


Overview Tutorial – How to build Django Angular 10 CRUD Example RestAPI with Django RestFramework + MySQL?

Architecture – Angular Django CRUD Application

Overall Design

Here is an overview of Architecture Design for Django Angular 10 CRUD Example with Django Rest Framework and MySQL:

Overall Architecture - Angular Django CRUD Example RestAPIs FullStack Integration
Overall Architecture – Angular Django RestAPIs FullStack Integration

  • We build the backend Python Django Application that provides RestAPIs for POST/GET/PUT/DELETE Customer entities and store them in MySQL/PostgreSQL database.
  • We implement the Angular CRUD Application that uses Angular HTTPClient to interact (call/receive requests) with Django backend’s RestAPIs and display corresponding page views in Browser

Django Application with Django Rest Framework + MySQL

Django RestAPIs Workflow
  • Django Application interacts with MySQL/PostgreSQL database via Model layers.
  • The Views are simply Python functions that take web requests and return web responses.
  • URLs are used to mapping each request with the corresponding views.
Django RestAPIs Workflow Architecture
Django RestAPIs Workflow Architecture
Django Project Structure
Django Project Structure with Django Rest Framework and MySQL
Django Project Structure

The Django RestAPIs project includes 2 folders:

  • djangoLoiZenAiRestAPIs is a main project folder
  • customers is an application folder

Angular Application

Angular Frontend Architecture
Angular Application Architecture
Angular Application Architecture

Angular CRUD Application is designed with 3 main layers:

  • Service Layer is used to define Angular Common Services and HttpClient Services to interact with RestAPIs
  • Component Layer is used to define Angular Components to show views in Browser for interacting with Users
  • Router Layer is used to route URLs mapping with the corresponding Angular Components
Angular Project Structure
Angular Project Structure with CRUD Django rest Framework
Angular Project Structure

Angular CRUD Application defines 3 components, 2 services, 1 routers, and 2 data models:

– Components:

  • add-customer component is used to add a new customer to system
  • list-customer component is used to show all customers on view pages, delete a customer and update a customer
  • message component is used to define a view to show logging message on browser

– Services:

  • customer.service.ts defines POST/GET/PUT/DELETE HTTP requests to Django RestAPIs with the built-in Angular HttpClient.
  • message.service.ts defines an array storage to log all messages when Angular CRUD App running

– Router: app-routing.module.ts defines how to map a corresponding Angular component with an URL.

– Models:

  • customer.ts defines the main data model of our application.
  • message.ts defines the response data model between Django RestAPIs and Angular application.

Goal

Here is a list of goals for Angular 10 Django CRUD Example with Django Rest Framework and MySQL database:

– Add a Customer Entities from Angular Client:

Goal 1 - Add a Customer Entity
Goal 1 – Add a Customer Entity

– List All Customer’s entities:

List All Customer's entities from Angular Client
Goal 2 – List All Customer’s entities from Angular Client

– Details a Customer:

Angular shows details of a Customer entity
Goal 3 – Angular shows details of a Customer entity

– Update a Customer:

Angular 10 CRUD Example Client - Update a Customer Entity
Goal 4 – Angular update a Customer Entity

– Delete a Customer:

Goal 5 - Angular Client delete a Customer entity
Goal 5 – Angular Client delete a Customer entity

Django CRUD RestAPIs – Backend Development

For how to build CRUD RestAPIs with Python Django, I refer all clearly details the step-by-step guidlines from the tutorial:
Django RestAPIs Tutorial – Build Post/Get/Put/Delete request Examples to MySQL/PostgreSQL databases

Create a Django project

In Django, every web application you want to create is called a project
And a project is a sum of applications.
An application is a set of code files relying on the MVT pattern.
– Open a cmd and type the below command to create a djangoLoiZenAiRestAPIs project:

$ django-admin startproject djangoLoiZenAiRestAPIs

Now the project folder djangoLoiZenAiRestAPIs is created with the following structure:

djangoLoiZenAiRestAPIs/
   manage.py
   djangoLoiZenAiRestAPIs/
      __init__.py
      settings.py
      urls.py
      wsgi.py
  • manage.py is used to interact with your project via command line (start the development server, sync db…).
  • __init__.py is a python file that treats this folder as package.
  • settings.py is a project settings file.
  • urls.py defines all url links of your project and the function to call
  • wsgi.py is used when you deploy your project over WSGI

All setting of the Django project is in file djangoLoiZenAiRestAPIs/settings.py.

Check a debug option:

DEBUG = True

This option sets your project is in debug mode or not. Debug mode gives you more information about your project’s error. In the production live build, Never set it to true.

Create Django Customer Application

As mention in above session, a Django project has a set of many applications. Each application is implemented with a specific purpose and can be reused into another project.

For creating a Customer application, we go to the project folder djangoLoiZenAiRestAPIs. Start the cmd: $ python manage.py startapp customers

Django will create a customers folder with the application structure as below:

customers/
   __init__.py
   admin.py
   models.py
   tests.py
   views.py
  • __init__.py is used by python to handle this folder as a package.
  • admin.py is used to hepl us modify the app in the admin interface.
  • models.py is place to store all application models.
  • tests.py is a place to write unit tests.
  • views.py is where to implement application views.

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'

We need to register our customes application with our Django project djangoLoiZenAiRestAPIs by updating INSTALLED_APPS tuple in the settings.py file of Django project:

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',
]

Create Django Customer model

A model class represents table or collection in our DB. Models are defined in the customers/models.py:

from django.db import models 
 
class Customer(models.Model):
    firstname = models.CharField(max_length=70, blank=False, default='')
    lastname = models.CharField(max_length=70, blank=False, default='')
    age = models.IntegerField(blank=False, default=1)
    address = models.CharField(max_length=70, blank=False, default='')
    copyrightby = models.CharField(max_length=70, blank=False, default='')

All Django models must inherit from django.db.models.Model. Customer class has 5 attributes (4 CharField and 1 Integer), those will be the table fields.

Serialize Customer Model

For serialization Python model object to JSON and deserialization Python object from JSON, We create a Serializer class for Customer instances:

– The CustomerSerializer class 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',
                  'firstname',
                  'lastname',
                  'age',
                  'address',
                  'copyrightby')

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

Setup Database Configuration

Setup MySQL database environment for Django project

For Django Rest Framework to work with MySQL database, we have to install Python MySQL Client to work with MySQL database.
In this tutorial, we use pymysql: pip install pymysql.

Once the installation is successful, import this module in djangoLoiZenAIRestApis/__init__.py:

import pymysql
pymysql.version_info = (1, 3, 13, "final", 0)
pymysql.install_as_MySQLdb()

– Now it’s time to configure the MySQL database tuple in settings.py file:

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

Setup PostgreSQL database environment

In case, we want to work with PostgreSQL database, we have to install Python PostgreSQL client.
In the tutorial, we use psycopg2: pip install psycopg2

– Configure again the database tuple in settings.py file:

DATABASES = {
   'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'loizenaidb',
        'USER': 'root',
        'PASSWORD': '12345',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

Migrate Customer Model to database

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 3.0.8 on 2020-07-15 15:43

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')),
                ('firstname', models.CharField(default='', max_length=70)),
                ('lastname', models.CharField(default='', max_length=70)),
                ('age', models.IntegerField(default=1)),
                ('address', models.CharField(default='', max_length=70)),
                ('copyrightby', models.CharField(default='', max_length=70)),
            ],
        ),
    ]

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 MySQL Database, now we can see that a table for Customer model was generated and it’s named customers_customer:

Django RestAPIs - Customers_customer tables schema
Django RestAPIs – Customers_customer tables schema

Configure Django Admin Page

Django provides a ready-to-use user interface for administrative activities. To have it working you need to make sure some modules are imported in the INSTALLED_APPS and MIDDLEWARE_CLASSES tuples of the djangoLoiZenAiRestAPIs/settings.py file.

INSTALLED_APPS make sure you have:

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',
    # CORSq
    'corsheaders',
]

– MIDDLEWARE_CLASSES:

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',  
]

Before running Django server to access the Admin page, we need to initiate the database:
$ python manage.py migrate

Check MySQL database tables:

Django all migration database tables
Django all migration database tables

Use the below command to create a super-user to login Admin page:
$ python manage.py createsuperuser

Django Admin Create Super User
Django Admin Create Super User

Register admin URL in djangoLoiZenAiRestAPIs/urls.py file:

from django.contrib import admin
from django.urls import path

from django.conf.urls import url, include 
 
urlpatterns = [ 
    path('admin/', admin.site.urls),
]

Register Customer model in ./customers/admin.py file:

from django.contrib import admin

# Register your models here.
from customers.models import Customer

# Register your models here.
admin.site.register(Customer)

Testing Django Admin Page

Launch Django server by cmd:

python manage.py runserver

Go to Admin page by URL link http://localhost:8000/admin/:

Django Admin Page
Django Admin Page

Site Administration has 2 parts:

  • AUTHENTICATION AND AUTHORIZATION
  • CUSTOMERS

Authentication and Authorization has Groups and Users pages, where we can add, modify, delete, update a specific user.

Django Administration User Page
Django Administration User Page

Customers is a place to manipulate (create, get, update, delete) all entities of the Customers application.

Add a Entity from Django Administration page
Add a Entity from Django Administration page
Django Administration List All Customers
Django Administration List All Customers

Implement Django Views – Post/Get/Put/Delete requests

Django POST/GET/PUT/DELETE requests

Django “view” is simply a Python function that takes a web request and returns a web response. This response can be the HTML contents of a Web page, or a redirect, or a 404 error, or an XML document, or an image … In the tutorial, we implement Django views that return JSON responses.

Django View Functions
Django View Functions

We create 2 Python function for Django views:

  • def customer_list(request) is used to POST a Customer entity and GET all Customer entities per a request
  • def customer_detail(request, pk) is used to PUT/DELETE a Customer via id key
Django POST request

In the ./customers/views.pycustomer_list(request) for POST/GET Customer entities:

@api_view(['GET', 'POST'])
def customer_list(request)

Here is a list of 4 steps we do to handle a Django POST request:

  1. Parsing a body request then serializing it to transform the JSON data to a Python Object
    customer_data = JSONParser().parse(request)
    customer_serializer = CustomerSerializer(data=customer_data)
  2. Save the serializing object to database:
    customer_serializer.save()
  3. Return a JsonResponse object to a client with a status code
    JsonResponse(response, status=status.HTTP_201_CREATED)
  4. If having any error or exception, return the JSON error object to client
    JsonResponse(error, status=status.HTTP_400_BAD_REQUEST) or
    JsonResponse(exceptionError, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

– Coding Example:

if request.method == 'POST':
try:
	customer_data = JSONParser().parse(request)
	customer_serializer = CustomerSerializer(data=customer_data)
	
	if customer_serializer.is_valid():
		customer_serializer.save()
		print(customer_serializer.data)
		response = {
		   'message': "Successfully Upload a Customer with id = %d" % customer_serializer.data.get('id'),
		   'customers': [customer_serializer.data],
		   'error': "" 
		}
		return JsonResponse(response, status=status.HTTP_201_CREATED)
	else:
		error = {
			'message':"Can Not upload successfully!",
			'customers':"[]",
			'error': customer_serializer.errors
		}
		return JsonResponse(error, status=status.HTTP_400_BAD_REQUEST)
except: 
	exceptionError = {
			'message': "Can Not upload successfully!",
			'customers': "[]",
			'error': "Having an exception!"
		}
	return JsonResponse(exceptionError, status=status.HTTP_500_INTERNAL_SERVER_ERROR);
Django GET request

For getting all Customer entities from database with Django GET request, we do a list of 4 steps as following:

  1. Get all Customer’ entities via Customer model class
  2. Serialize a List of Customer Objects
  3. Return a JsonResponse object with Python dictionary body and http status code
    JsonResponse(response, status=status.HTTP_200_OK);
  4. If having any error or exception, just return a JsonResponse object with a building body for errors and a http status error code:
    JsonResponse(error, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
if request.method == 'GET':
	try:
	  customers = Customer.objects.all()
	  customers_serializer = CustomerSerializer(customers, many=True)

	  response = {
		 'message': "Get all Customers'Infos Successfully",
		 'customers': customers_serializer.data,
		 'error': ""
	  }
	  return JsonResponse(response, status=status.HTTP_200_OK);
	except: 
	  error = {
		'message': "Fail! -> can NOT get all the customers List. Please check again!",
		'customers': "[]",
		'error': "Error"
	  }
	  return JsonResponse(error, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Django PUT request

In ./customers/views.py file, We define a view function customer_detail to do a PUT/DELETE a Customer entity with a given id:

@api_view(['PUT', 'DELETE'])
def customer_detail(request, pk)

For PUT/DELETE a database entity with a given id, firstly we need to check the existing of an entity with the id. If having an DoesNotExist exception throws, we stop processing the request and return a JsonResponse object with a body is an exception’s error and a http status code NOT_FOUND:

try: 
	customer = Customer.objects.get(pk=pk)
except Customer.DoesNotExist:
	exceptionError = {
		'message': "Not found a Customer with id = %s!" % pk,
		'customers': "[]",
		'error': "404 Code - Not Found!"
	}
	return JsonResponse(exceptionError, status=status.HTTP_404_NOT_FOUND) 

For handing a PUT request, we do a list actions with 4 steps:

  1. Parsing the request’s body object
    customer_data = JSONParser().parse(request)
  2. De-serialize the above parsing data:
    customer_serializer = CustomerSerializer(customer, data=customer_data)
  3. Store the object to database by .save() API function:
    customer_serializer.save()
    Return a JsonResponse object with user-defined response:
    response = {
    	'message': "Successfully Update a Customer with id = %s" % pk,
    	'customers': [customer_serializer.data],
    	'error': ""
    }                
    return JsonResponse(response)
  4. If having any exception being throwed, we need to handle it and return an error with 404 status code:
    return JsonResponse(response, status=status.HTTP_400_BAD_REQUEST)

try:
	customer_data = JSONParser().parse(request)
	customer_serializer = CustomerSerializer(customer, data=customer_data)

	if customer_serializer.is_valid(): 
		customer_serializer.save()
		response = {
			'message': "Successfully Update a Customer with id = %s" % pk,
			'customers': [customer_serializer.data],
			'error': ""
		}                
		return JsonResponse(response) 

	response = {
			'message': "Fail to Update a Customer with id = %s" % pk,
			'customers': [customer_serializer.data],
			'error': customer_serializer.errors
		}
	return JsonResponse(response, status=status.HTTP_400_BAD_REQUEST) 
Django DELETE request

Here is the remain code in the view function def customer_detail(request, pk) for DELETE request with a given id:

if request.method == 'GET':
	customer_serializer = CustomerSerializer(customer) 
	response = {
		'message': "Successfully get a Customer with id = %s" % pk,
		'customers': [customer_serializer.data],
		'error': ""
	}
	return JsonResponse(response, status=status.HTTP_200_OK);
	
	...
	
elif request.method == 'DELETE':
	print("Deleting a Customer with id=%s"%pk)
	customer.delete() 
	customer_serializer = CustomerSerializer(customer) 
	response = {
			'message': "Successfully Delete a Customer with id = %s" % pk,
			'customers': [customer_serializer.data],
			'error': ""
		}
	return JsonResponse(response)

Configure Django URLs

We define a set of Rest URL for Customers Application in file ./customers/urls.py:

from django.conf.urls import url 
from customers import views 
 
urlpatterns = [ 
    url(r'^api/customers/$', views.customer_list),
    url(r'^api/customers/(?P<pk>[0-9]+)$', views.customer_detail)
]

We need include the Customer Application’s URL file to project’s urls.py file:

from django.contrib import admin
from django.urls import path

from django.conf.urls import url, include 
 
urlpatterns = [ 
    url(r'^', include('customers.urls')), 
    path('admin/', admin.site.urls),
]

Testing

Testcase 1 – Post a Customer

Post a Customer to Django Server
Post a Customer to Django Server

Testcase 2 – Get All Customers

Django Get all entities from databases
Django Get all entities from databases

Testcase 3 – Update a Customer

Django PUT request
Django PUT request

Testcase 4 – Delete a Customer

Django Delete Request
Django Delete Request
Django Check Database after do CRUD requests
Django Check Database after do CRUD requests

Angular CRUD Application Example – Frontend Development

Angular Application Overview

Angular Application Architecture
Angular Application Architecture

– For more details, we go back to the session: Angular CRUD Design

Create Angular Application

We create Angular CRUD project by commandline: ng new AngularHttpclient.
– Create 3 components AddCustomer, ListCustomers, Message by cmd:


ng g component AddCustomer
ng g component ListCustomers
ng g component Message

– Create 2 Angular services CustomerService, MessageService by cmd:


ng g service customer
ng g service message

– Create 2 models Customer and Message by cmd:

 
ng g class customer;
ng g class message;

Create Customer model

We define Customer class with 5 attributes:

export class Customer {
    id: number;
    firstname: string;
    lastname: string;
    age: number;
    address: string
}

Create Message model

We define Message class as below:

import { Customer } from './customer';

export class Message {
    message: string;
    error: string;
    customers: Customer[];
}

Implement Angular HttpClient Customer Service

For interacting with Backend RestAPIs, we use Angular built-in Httpclient service:

@Injectable({
  providedIn: 'root'
})
export class CustomerService {

  private baseUrl = 'http://localhost:8080/api/customers';

  constructor(private http: HttpClient) { }
  
  ...

To handle Error, we implement a function private handleError(error: HttpErrorResponse):

private handleError(error: HttpErrorResponse) {
  if (error.error instanceof ErrorEvent) {
    // A client-side or network error occurred. Handle it accordingly.
    console.error('An error occurred:', error.error.message);
  } else {
    // The backend returned an unsuccessful response code.
    // The response body may contain clues as to what went wrong,
    console.error(
      `Backend returned code ${error.status}, ` +
      `body was: ${error.error}`);
  }
  // return an observable with a user-facing error message
  return throwError(
    'Something bad happened; please try again later.');
};

Angular HttpClient POST Request – Add new Customer

createCustomer(customer: Customer): Observable<Message> {
    return this.http.post<Message>(`${this.baseUrl}` + `/create`, customer)
                .pipe(
                  retry(3),
                  catchError(this.handleError)
                );
}

– The above function posts a Customer to SpringBoot backend server at URL http://localhost:8080/api/customers/create
retry(3) is used to retry a failed request up to 3 times

Angular HttpClient GET Request – Retrieve Customers

– Retrieve all Customers from SpringBoot backend at RestAPI by a GET request at URL http://localhost:8080/api/customers/retrieveinfos.

retrieveAllCustomers(): Observable {
    return this.http.get(`${this.baseUrl}` + `/retrieveinfos`)
                  .pipe(
                    retry(3),
                    catchError(this.handleError)
                  );
}

Angular HttpClient PUT Request – Modify a Customer

– Update a Customer with Angular Httpclient by a PUT request at URL:
http://localhost:8080/api/customers/updatebyid/{id}

updateCustomer(customer: Customer): Observable<Message> {
    return this.http.put<Message> (`${this.baseUrl}` + `/updatebyid/` + customer.id, customer)
      .pipe(
          retry(3),
          catchError(this.handleError)
        );
}

Angular HttpClient DELETE Request – Delete a Customer

– Delete a Customer by a given id with Angular Httpclient by a DELETE request at URL:
http://localhost:8080/api/customers/deletebyid/{id}:


deleteCustomer(id: number): Observable<Message> {
    return this.http.delete<Message>(`${this.baseUrl}` + `/deletebyid/` + id)
          .pipe(
            retry(3),
            catchError(this.handleError)  
          );
}

Implement Message Service

For tracking the proccessing of each proccessing in Angular CRUD Application, We implement a Message service to store tracing-logs message then display them on Html.

The message.service.ts has an string array messages to store tracing-log messages and 2 functions: add(message: string) and clear()

– Coding:


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

@Injectable({
  providedIn: 'root'
})
export class MessageService {
  messages: string[] = [];

  add(message: string) {
    this.messages.push(message);
  }

  clear(){
    this.messages = [];
  }
}

Implement Component: Add Customer

AddCustomerComponent is used to post a new Customer to SpringBoot backend server.
– We have 2 parts:

  • add-customer.component.ts file
  • add-cusomer.component.html file

Implement add-customer.component.ts

import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';
import { Message } from '../message';
import { MessageService } from '../message.service';

@Component({
  selector: 'app-add-customer',
  templateUrl: './add-customer.component.html'
})
export class AddCustomerComponent implements OnInit {
  customer: Customer;
  /**
   * Constructing Http Customer Service
   * @param customerService 
   */
  constructor(private customerService: CustomerService,
                private messageService: MessageService) { }

  ngOnInit(): void {
    this.customer = new Customer();
  }

  /**
   * Store a Customer to backend server
   */
  save() {
    this.customerService.createCustomer(this.customer)
          .subscribe((message: Message) => {
            console.log(message);
            let customer = message.customers[0];
            let msg = "Success -> Post a Customer: " 
                + "<ul>"
                    + "<li>id: " + customer.id + "</li>"  
                    + "<li>firstname: " + customer.firstname + "</li>"
                    + "<li>lastname: " + customer.lastname + "</li>"
                    + "<li>age: " + customer.age + "</li>"
                    + "<li>address: " + customer.address + "</li>"
                + "</ul>";

            this.messageService.add(msg);
          }, error => {
            console.log(error);
            let msg = "Error! -> Action Posting a Customer:" 
                      + "<ul>"
                        + "<li>id = " + this.customer.id + "</li>"  
                        + "<li>firstname = " + this.customer.firstname + "</li>"
                        + "<li>lastname = " + this.customer.lastname + "</li>"
                        + "<li>age = " + this.customer.age + "</li>"
                        + "<li>address = " + this.customer.address + "</li>"
                      + "</ul>";

            this.messageService.add(msg);
          });
  }

  reset(){
    this.customer = new Customer();
  }

  /**
   * Function handles form submitting
   */
  onSubmit() {
    this.save();
    this.reset();
  }
}

Implement add-customer.component.html view

<h2>Create Customer</h2>
<div>
  <form (ngSubmit)="onSubmit()"> 
    <!-- First name -->   
    <div class="form-group">
      <label for="firstname">First Name:</label>
      <input type="text" class="form-control" placeholder="Enter Firstname" 
                id="firstname" required [(ngModel)]="customer.firstname" name="firstname">
    </div>
    <!-- Last name -->
    <div class="form-group">
        <label for="lastname">Last Name:</label>
        <input type="text" class="form-control" placeholder="Enter Lastname" 
                  id="lastname" required [(ngModel)]="customer.lastname" name="lastname">
    </div>  
    <!-- Address -->
    <div class="form-group">
        <label for="address">Address:</label>
        <input type="text" class="form-control" placeholder="Enter Address" 
                  id="address" required [(ngModel)]="customer.address" name="address">
    </div>        
    
    <!-- Age -->
    <div class="form-group">
      <label for="age">Age</label>
      <input type="number" class="form-control" placeholder="Enter Age" 
                  id="age" required [(ngModel)]="customer.age" name="age">
    </div>
 
    <button type="submit" class="btn btn-success">Submit</button>
  </form>
</div>
<app-message></app-message>

Implement Component: List Customers

ListCustomersComponent has 4 main functions:

  • Show all Customers
  • Show details a Customers
  • Delete a Customer
  • Update a Customer

Implement list-customers.component.ts

import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { MessageService } from '../message.service';
import { CustomerService } from '../customer.service';
import { Message } from '../message';

@Component({
  selector: 'app-list-customers',
  templateUrl: './list-customers.component.html'
})
export class ListCustomersComponent implements OnInit {

  customers: Array<Customer> = [];
  showCustomer: Customer;
  isSelected: boolean = false;
  deletedCustomer: Customer;
  returnedMessage: string;

  constructor(private customerService: CustomerService,
                private messageService: MessageService) { }

  setCustomerDetails(customer: Customer){
    this.isSelected=!this.isSelected;
    if(this.isSelected){
      this.showCustomer = customer;
    }else{
      this.showCustomer = undefined;
    }
  }

  /**
   * Set deletedCustomer and reset returnedMessage = undefined
   * @param deleteCustomer
   */
  prepareDeleteCustomer(deleteCustomer: Customer){
    //assign delete-Customer
    this.deletedCustomer = deleteCustomer;
    // reset returned-Message
    this.returnedMessage = undefined;
  }

  /**
   * Delete a Customer by ID
   */
  deleteCustomer(){

    console.log("--- Access delelteCustomer() function");

    this.customerService.deleteCustomer(this.deletedCustomer.id)
                      .subscribe((message: Message) => {
                          console.log(message);
                          // remove a deletedCustomer from customers list on view
                          this.customers = this.customers.filter(customer => {
                            return customer.id != this.deletedCustomer.id;
                          })

                          // set a showing message in delete modal
                          this.returnedMessage = message.message;

                          // just reset showCustomer for not showing on view
                          this.showCustomer = undefined;

                          // add the delete message to message app for showing
                          this.messageService.add(message.message);
                        },
                        (error) => {
                          console.log(error);
                          let errMsg: string = "Error! Details: " + error;
                          this.messageService.add(errMsg);
                        });
  }

  /**
   * Update Customer function
   */
  updateCustomer() {
    this.customerService.updateCustomer(this.showCustomer)
                      .subscribe((message: Message) => {
                        console.log(message);
                        // update customers list
                        this.customers.map(x => {
                          if(x.id == this.showCustomer.id){
                            x = this.showCustomer;
                          }
                        });

                        let msg: string = "Update Successfully! -> New Customer's properties: <br>"
                                          + "<ul>"
                                            + "<li>" + "id: " + this.showCustomer.id + "</li>"
                                            + "<li>" + "firstname: " + this.showCustomer.firstname + "</li>"
                                            + "<li>" +  "lastname: " + this.showCustomer.lastname + "</li>"
                                            + "<li>" +  "age: " + this.showCustomer.age + "</li>"
                                            + "<li>" +  "address: " + this.showCustomer.address + "</li>"
                                          + "</ul>";
                        this.messageService.add(msg);
                      }
                      , (error) => {
                        console.log(error);
                        let errMsg = "Update Fail ! Error = " + error;
                        this.messageService.add(errMsg);
                      });
  }

  /**
   * Retrieve all Customer from Backend
   */
  retrieveAllCustomers() {
    this.customerService.retrieveAllCustomers()
                  .subscribe((message: Message) => {
                    console.log(message);
                    this.customers = message.customers;
                  }
                  , (error) => {
                    console.log(error);
                  });
  }

  ngOnInit(): void {
    this.retrieveAllCustomers();
  }
}

Implement list-customers.component.html

<div *ngIf="customers.length">
    <h3>Customers</h3>
    <br>
    <table class="table table-hover table-sm">
        <thead class="thead-dark">
          <tr>
            <th>Id</th>
            <th>Firstname</th>
            <th>Address</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
            <tr *ngFor="let customer of customers">
              <td>
                <button type="button" class="btn btn-primary" (click)="setCustomerDetails(customer)">
                  {{customer.id}}
                </button>
              </td>
              <td>{{customer.firstname}}</td>
              <td>{{customer.address}}</td>
              <td>
                <button type="button" class="btn btn-danger" 
                  data-toggle="modal" data-target="#delete-modal" 
                                (click)=prepareDeleteCustomer(customer) >&times;</button>
              </td>
            </tr>
        </tbody>
    </table>
</div>

<!-- The Modal -->
<div class="modal fade" id="delete-modal">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
    
    <!-- Modal Header -->
    <div class="modal-header">
      <h4 class="modal-title">Delete!</h4>
      <button type="button" class="close" data-dismiss="modal">&times;</button>
    </div>
    
    <!-- Modal body -->
    <div class="modal-body">
        <div *ngIf="deletedCustomer">
          <p [hidden] = "returnedMessage">
            Do you want delete a customer with id = {{deletedCustomer.id}}
          </p>
          <p [hidden] = "!returnedMessage">
            {{returnedMessage}}
          </p>
        </div>
    </div>
    
    <!-- Modal footer -->
    <div class="modal-footer">
      <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
      <button [hidden] = "returnedMessage" type="button" class="btn btn-danger" (click)="deleteCustomer()">Delete</button>
    </div>				
    </div>
  </div>
  </div>

<div *ngIf="showCustomer">
    <h3>Update Customer</h3>
    <form (ngSubmit)="updateCustomer()"> 
        <!-- ID -->   
        <div class="form-group">
          <label for="id">Id:</label>
          <input type="numer" class="form-control"
                    id="id" required [(ngModel)]="showCustomer.id" name="id" disabled>
        </div>      
        <!-- First name -->   
        <div class="form-group">
          <label for="firstname">First Name:</label>
          <input type="text" class="form-control" placeholder="Enter Firstname" 
                    id="firstname" required [(ngModel)]="showCustomer.firstname" name="firstname">
        </div>
        <!-- Last name -->
        <div class="form-group">
            <label for="lastname">Last Name:</label>
            <input type="text" class="form-control" placeholder="Enter Lastname" 
                      id="lastname" required [(ngModel)]="showCustomer.lastname" name="lastname">
        </div>  
        <!-- Address -->
        <div class="form-group">
            <label for="address">Address:</label>
            <input type="text" class="form-control" placeholder="Enter Address" 
                      id="address" required [(ngModel)]="showCustomer.address" name="address">
        </div>        
        
        <!-- Age -->
        <div class="form-group">
          <label for="age">Age</label>
          <input type="number" class="form-control" placeholder="Enter Age" 
                      id="age" required [(ngModel)]="showCustomer.age" name="age">
        </div>
     
        <button type="submit" class="btn btn-success">Update</button>
      </form>
</div>
<app-message></app-message>


<script>
  let pathname = window.location.pathname;
  if(pathname == ""){
      $(".nav .nav-item a:first").addClass("active");
      $(".nav .nav-item a:last").removeClass("active");
  } else if (pathname == "/customers") {
      $(".nav .nav-item a:last").addClass("active");
      $(".nav .nav-item a:first").removeClass("active");
  }
  alert("ok");
</script>

Implement Component: Message Component

MessageComponent is used to show all tracing-log messages in html view.

Implement message.component.ts

import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';

@Component({
  selector: 'app-message',
  templateUrl: './message.component.html'
})
export class MessageComponent {
  constructor(public messageService: MessageService) {}
}

Implement message.component.html

<div *ngIf="messageService.messages.length">
    <h3>Messages</h3>
    <button type="button" class="btn btn-secondary" (click)="messageService.clear()">clear</button>
    <br>
    <ol>
      <li *ngFor='let message of messageService.messages'>
        <div [innerHTML]="message">
        </div>
      </li>
    </ol>
  </div>

Configure App Routing Module

To handle the navigation from one view to the next, you use the Angular router. The router enables navigation by interpreting a browser URL as an instruction to change the view.

The following command uses the Angular CLI to generate a basic Angular app with an app routing module, called AppRoutingModule, which is an NgModule where you can configure your routes.

ng new routing-app --routing

How to Define a route? -> There are three fundamental building blocks to creating a route.

1. Import the AppRoutingModule into AppModule and add it to the imports array.
The Angular CLI performs this step for you. However, if you are creating an app manually or working with an existing, non-CLI app, verify that the imports and configuration are correct.


import { AppRoutingModule } from './app-routing.module';
...

@NgModule({
  declarations: [
	...
  ],
  imports: [
	...
    AppRoutingModule,
    ...
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

2. Define your routes in your Routes array

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AddCustomerComponent } from './add-customer/add-customer.component';
import { ListCustomersComponent } from './list-customers/list-customers.component';

const routes: Routes = [
  { 
    path: '', 
    component: AddCustomerComponent 
  },
  { 
    path: 'customers', 
    component: ListCustomersComponent 
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

3. Add your routes to your application.
In the index.html file, we add below html code for navigating URL:


<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
  <ul class="navbar-nav">
    <li class="nav-item" id="li_add_customer">
      <a class="nav-link" href="">Add Customer</a>
    </li>
    <li class="nav-item" id="li_list_customers">
      <a class="nav-link" href="/customers">List Customers</a>
    </li>
  </ul>
</nav>

Next, update your component template to include . This element informs Angular to update the application view with the component for the selected route. So in main component,open app.component.ts file, add the tag:


<router-outlet></router-outlet>

Modify index.html view page

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>AngularHttpclient</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
  <!-- jQuery library -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <!-- Popper JS -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
  <!-- Latest compiled JavaScript -->
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</head>
<body>
  <div class="container" >  
    <div class="col-sm-5" style="background-color: #ffffcc; margin:10px;padding:10px; border-radius: 5px">
      <nav class="navbar navbar-expand-sm bg-primary navbar-dark">
        <ul class="navbar-nav">
          <li class="nav-item" id="li_add_customer">
            <a class="nav-link" href="">Add Customer</a>
          </li>
          <li class="nav-item" id="li_list_customers">
            <a class="nav-link" href="/customers">List Customers</a>
          </li>
        </ul>
      </nav>
      <app-root></app-root>
    </div>
  </div>
  <script>
    $(document).ready(function() {
      (function(){
        let pathname = window.location.pathname;
        if(pathname == "/"){
            $("#li_add_customer").addClass("active");
            $("#li_list_customers").removeClass("active");
        } else if (pathname == "/customers") {
            $("#li_list_customers").addClass("active");
            $("#li_add_customer").removeClass("active");
        } 
      })();
    });
  </script>
</body>
</html>

Django Angular CRUD Example Testing

Now we define a set of testcases for the tutorial: “Django Angular 10 CRUD Example – Fullstack: Angular 10 + Django Rest Framework + MySQL Tutorial”.

Testcase 1 – Add New Customer

Testcase 1 - Angular Client post a entity
Testcase 1 – Angular Client post a entity
Testcase 1 - Angular Client POST request
Testcase 1 – Angular Client POST request

Testcase 2 – Retrieve All Customers

Testcase 2 - Angular Client - Get All entities from Django Server
Testcase 2 – Angular Client – Get All entities from Django Server
Testcase 2 - Angular Client get all entities
Testcase 2 – Angular Client get all entities

Testcase 3 – Update a Customer

Testcase 3 - Angular Client - Update a Customer Entity
Testcase 3 – Angular Client – Update a Customer Entity
Testcase 3 - Angular Client update customer with id = 2
Testcase 3 – Angular Client update customer with id = 2

Testcase 4 – Delete a Customer

Testcase 4 - Angular Client - Delete a Customer with id = 3
Testcase 4 – Angular Client – Delete a Customer with id = 3
Testcase 4 - Angular Client Delete a Customer Successfully
Testcase 4 – Angular Client Delete a Customer Successfully

Sourcecode

Here are sourcecodes for “Django Angular 10 CRUD Example – Fullstack: Angular 10 + Django Rest Framework + MySQL Tutorial”:

– Django CRUD RestAPIs – Backend Development:

djangoLoiZenAiRestAPIs

GitHub sourcecode for Django Angular 10 CRUD Example with Django Rest Framework an MySQL database:

Python Django RestAPIs – Github

– Angular CRUD Application – Frontend Development

– Download Source:

AngularCrudApplication

– GIT Source for Angular 10 Django Rest Framework with MySQL CRUD example:

AngularCRUDApplication – GIT HUB

Further Reading

Related posts:


One thought on “Django Angular 10 CRUD Example – MySQL + Django Rest Framework Tutorial”

Leave a Reply

Your email address will not be published. Required fields are marked *