Basic Django CRUD using AJAX, JSON, and JQuery – Monolithic Architecture

Welcome in today’s event loop on {{ PLP }}, previously we’ve learned about on How to Install MySQL and phpMyAdmin on Ubuntu 18.04 with NGINX web server. So, for today, you’ll be learning the CRUD which means the basic usage of the Database operations using the AJAXJSON, and JQuery.

In this event loop, we’ll be using the Monolithic Architecture for this Django project, meaning we only use a single tier MySQL database and not having multiple MySQL databases that separate the CRUD operations into smaller but dedicated applications which are called the Micro Services Architecture.

Introduction

The CRUD is the most crucial learning point in building any web-based applications or any other type of applications that need any database operations like creatingreading, updating, and deleting any rows from the database in a much faster method.

So, prepare your self once again and stay focus because this would be exciting and fun learning with {{ PLP }}.

Getting Started

The Asynchronous method that we’ll be using throughout the entire lesson for today is the most preferred ways doing any database operations that will result in a much faster CRUD without sacrificing the safety standard provided by Django framework by itself.

We’ll be using the MySQL Database for this lesson and the web-based interface for MySQL which is the phpMyAdmin to access it over the browser.

This is the essential Asynchronous technique that I personally use to all of the web-based projects that I’m about to share it with you.  So, let’s get started.

Step 1:  Create MySQL Database Connection from settings.py

I assumed that you’re still following the series of tutorials we’ve done, just in case you skip the last guide which is the “Hello World!” app and it’s Ideal Django Project Structures.

By the way, the default set up for DATABASES section from settings.py which uses the sqlite3 standalone database.

1
2
3
4
5
6
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

Now, change this “default” database set up to MySQL database and follow the configuration below.

1
2
3
4
5
6
7
8
9
10
DATABASES = {
  'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'dev_auth',
        'USER': 'db_user_name',
        'PASSWORD': 'db_password_here',
        'HOST': 'localhost',
        'PORT': '3306',
    },
}

In Django DATABASES set up, there is always a “default” database configuration, literary that must be there either it’s empty or not, because Django can connect to multiple different databases like PostgreSQL, SQLITE3, etc.

But, I recommend using the “default” database config as the main Django user authentication storage, meaning all it’s related default tables provided by Django by itself.  Later on, I’ll be discussing this built-in Django User Auth System in our next event loop.

Step 2:  Define a New Class from the Model

Instead of creating tables directly from a database server, Django provided with robust ORM (Object-relational Mapping) technology that we can define the database with table collections directly from Django Models.  Meanwhile, open the file from “dev/myroot/models.py” and copy the code below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from django.conf import settings
from django.db import models


class ContactUs(models.Model):
    full_name = models.CharField(max_length=75)
    email = models.EmailField(max_length=254)
    subject = models.CharField(max_length=75)
    message = models.TextField()
    submitted = models.DateTimeField(auto_now_add=True)
    is_deleted = models.BooleanField(default=False)
    deleted_date = models.DateField(null=True, blank=True)
    deleted_by = models.PositiveIntegerField(default=0)

    class Meta:
        index_together = ["full_name", "is_deleted"]
        db_table = 'dev_contact_us'
        verbose_name = 'Contact Us'
        verbose_name_plural = 'Contact Us'

For this purpose, we’ll be creating a new database table called “dev_contact_us” that would literally be created automatically under the MySQL database name “dev_auth” during the Django migration command execution.

To Explain Further About this class ContactUs(models.Model):

As an illustration, we’re about to create and define a simple database table called “dev_contact_us” with the following table fields below.

full_name = models.CharField(max_length=75)

The full_name field is defined as a “varchar(75)” with a maximum length of 75 characters including white spaces as an equivalent to the MySQL column definition during the Django migration.

email = models.EmailField(max_length=254)

The email field is still a “varchar(254)” even though defined as “EmailField” with a maximum length of 254 characters including white spaces as well but with built-in Django email validation.

subject = models.CharField(max_length=75)

This is same with the full_name column.

message = models.TextField()

This will define as “longtext()” in MySQL that can hold up to
4,294,967,295 or 4GB (232 − 1) characters.

submitted = models.DateTimeField(auto_now_add=True):

Similarly, the submitted column will be translated as “datetime(6)” that can store this type of data “2018-12-13 05:08:31.368806” and defined in Django as “DateTimeField(auto_now_add=True)” means that every time the INSERT statement will auto capture the web server date and time and no need to specify the column name during the insertion of the new row in MySQL.

is_deleted = models.BooleanField(default=False)

While on the other hand, the BooleanField in Django will be translated as “tinyint(1)” with 1=True and 0=False in MySQL and you can specify the default value either True/False value only with the capital letter T or F in Django specifics.

deleted_date = models.DateField(null=True, blank=True)

However, the DateField in Django that defined as nullable and can be empty value as well, meaning it’s not required field before the INSERT and UPDATE CRUD operation.

deleted_by = models.PositiveIntegerField(default=0)

Meanwhile, the PositiveIntegerField in Django means “int(10)” in MySQL that the default value would be zero as we’ve defined it in Django as default=0.  But, we can make this as not a mandatory field by setting it as models.PositiveIntegerField(null=True, blank=True).

index_together = [“full_name”, “is_deleted”]

For instance, under the class Meta configurations are all optional but I recommend to use these options for each table specific set up.  But, most cases we’re using index-able fields in MySQL to make a table much faster in terms of searching thousands of rows and retrieve it.

But you can’t index every column as this has a big impact in storage as well and make UPDATE CRUD operation slow as both existing table and index data version will be affected with the modified row as well.

Besides, have you noticed that the primary key which is the “id” as “int(10)” will be auto create during the Django migration.

db_table = ‘dev_contact_us’

Moreover, the db_table is the actual table name literally that would be auto-created during the Django migration.  So, I recommend that you give an initial table name like e.g “dev_table_name” as this table will be residing at the default database with all the default Django tables as well.

verbose_name = ‘Contact Us’

Likewise, the verbose_name is mainly used for the built-in Django Admin System which this name will be used as a singular name from that built-in automatic back-end system provided by Django by itself.  So, don’t worry, I’ll be discussing this in our future event loop on {{ PLP }}.

verbose_name_plural = ‘Contact Us’

For example, you have a verbose_name = ‘Student’ which is a singular form, but for verbose_name_plural = ‘Students’ which is, of course, a plural name.  So, this is mainly used for the Django Admin System which is the important naming conventions that are readable for humans like us.

Step 3:  Create MySQL Database for “dev_auth” using phpMyAdmin

In the meantime, head over to your phpMyAdmin which is the web-based interface for the MySQL database, so for my case, I access using https://dev.pinoylearnpython.com/phpmyadmin/ and create the dev_auth database name.

Why utf8_general_ci has been selected for the database collation? It’s because, we really need to store other binary characters, special characters, other languages, in general almost all characters we can store it and it will not cause any issues upon doing the CRUD operations and the most important thing is that, utf8_general_ci is much faster approach than any other binary collation.

Now, upload these 2 files to your web server so that we can do the Django Migrations.

Step 4: Install MySQL Client

Furthermore, we need to install the MySQL client and it’s dependencies first before we can be able to connect to the MySQL database.  This is important MySQL Driver for us to be connected between our Django project and the MySQL Database.

1
2
sudo apt-get install libmysqlclient-dev
sudo pip3 install mysqlclient

Next, execute the 2 commands above and if prompt with “Y/n“, type “Y” and press ENTER to proceed, it’s should be smooth installation.  By the way, keep in mind to add this mysqlclient to your requirements.txt to keep track what third-party library we’re using for our Django project.

Step 5:  Execute Django Migration

First of all, upload the “dev/dev/settings.py” and “dev/myroot/models.py” to your web server using FileZilla.

Afterward, we need to access the Web Shell Console with root privileges and execute this Django utility command.  Navigate to your Django project folder first.

1
cd dev

Next, execute this Django command to make a migration document file for the “dev_auth” that serves as a monolith architecture.

1
python3 manage.py makemigrations

When the “makemigrations” has been executed successfully, this would be the result.

1
2
3
4
Migrations for 'myroot':
  myroot/migrations/0001_initial.py
    - Create model ContactUs
    - Alter index_together for contactus (1 constraint(s))

Did you notice something? the Django makemigrations command will automatically detect any changes to all of your app’s models.py and create a new Python file that contains all the table and field structures ready to push to the MySQL database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Generated by Django 2.1.3 on 2018-12-14 03:29

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='ContactUs',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('full_name', models.CharField(max_length=75)),
                ('email', models.EmailField(max_length=254)),
                ('subject', models.CharField(max_length=75)),
                ('message', models.TextField()),
                ('submitted', models.DateTimeField(auto_now_add=True)),
                ('is_deleted', models.BooleanField(default=False)),
                ('deleted_date', models.DateField(blank=True, null=True)),
                ('deleted_by', models.PositiveIntegerField(default=0)),
            ],
            options={
                'verbose_name': 'Contact Us',
                'verbose_name_plural': 'Contact Us',
                'db_table': 'dev_contact_us',
            },
        ),
        migrations.AlterIndexTogether(
            name='contactus',
            index_together={('full_name', 'is_deleted')},
        ),
    ]

The above Python file located at the “myroot/migrations/0001_initial.py” that Django automatically created every time there is a change to each app models.py.

Step 6:  Push the Django Migration to MySQL Database

Now, we’re ready to push the Django migration to MySQL database by executing this command.

1
python3 manage.py migrate

Moreover, the command result would be something like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, myroot, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying myroot.0001_initial... OK
  Applying sessions.0001_initial... OK

Awesome, all successfully migrated and created the default Django tables and our own table name which is the dev_contact_us.  So, access your phpMyAdmin and see the database dev_auth has been populated with tables as we’re expected.

Step 7:  Use global_config.py for Constant Variables to Relay over Django Templates

For this purpose, we use our own custom dev/myroot/global_config.py to relay whatever constant variables we use from the dev/dev/settings.py that is very common for our apps to be used on Django templates.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.conf import settings


def global_settings(request):
    """ Return custom constant global variables to be
    used widely for all of our apps. """

    return{
        'BASE_URL': settings.BASE_URL,
        'SITE_SHORT_NAME': settings.SITE_SHORT_NAME,
        'SITE_FULL_NAME': settings.SITE_FULL_NAME,
        'SITE_YEAR_STARTED': settings.SITE_YEAR_STARTED,
        'SITE_URL_HOME': settings.SITE_URL_HOME,
        'SITE_SLOGAN': settings.SITE_SLOGAN,
    }

Next, open the dev/dev/settings.py and include the global_config.py from the TEMPLATES section. The specific line at the context_processors options, add it at the bottom this new line ‘myroot.global_config.global_settings’,.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'myroot.global_config.global_settings',
            ],
        },
    },
]

Moreover, still from your dev/dev/settings.py and add this common site information as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# *******************************************
# My custom configurations
# *******************************************
# For base url, include "/" at the end
BASE_URL = 'https://dev.pinoylearnpython.com/'

# For app labels section here
APP_LABEL_MYROOT = 'myroot'

# Common Site Information here
SITE_SHORT_NAME = "{{ PLP }}"
SITE_FULL_NAME = "Pinoy Learn Python"
SITE_YEAR_STARTED = "2018"
SITE_URL_HOME = "https://pinoylearnpython.com"
SITE_SLOGAN = SITE_FULL_NAME + " - To Help Filipino Students to Learn Python!"

So now, you can add whatever constant variables as you may require for your Django project.  But, make it sure, that every time you add a constant variable from your settings.py and your intention is to use this over the Django template, you must add that variable to the global_config.py as well.

Django Basic CRUD for Create Statement

Moreover, the Django Basic CRUD for creating or inserting a new row to a database table starts from Step 8 to 12 but with an exceptional case for step number 10 if you’re a new common function to be used in general purpose throughout your Django project. So, let’s do this.

Step 8:  Create a new Python file called fmain.py under dev/myroot/forms/

In addition, create a new form to be used as a container to collect inputs from the user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from django.conf import settings
from django import forms
from django.forms import ModelForm

# Call myroot properties
from myroot.models import ContactUs


class Basic_CRUD_Create_Form(ModelForm):
    """
    Render the basic crud create form
    """

    full_name = forms.CharField(
        widget=forms.TextInput(attrs={'class': 'form-control',
                                      'placeholder': 'Enter your full name',
                                      'maxlength': '75'}))

    email = forms.CharField(
        widget=forms.TextInput(attrs={'class': 'form-control',
                                      'placeholder': 'Enter your working email',
                                      'maxlength': '254'}))

    subject = forms.CharField(
        widget=forms.TextInput(attrs={
            'class': 'form-control', 'placeholder': 'Enter your subject',
            'maxlength': '75'}))

    message = forms.CharField(
        widget=forms.Textarea(attrs={'class': 'form-control textarea',
                                     'placeholder': 'Enter your message'}))

    class Meta:
        model = ContactUs
        fields = ('full_name', 'email', 'subject', 'message')

The Django Form mainly uses to defined user inputs but not necessary if your app doesn’t accept any inputs from the user.  Moreover, the form connects through the Django Model which is the ContactUs with the corresponding fields.

Here, before anything else, always remember to import the mandatory Django built-in libraries before you can use the Django Forms and Models as well.

1
2
from django import forms
from django.forms import ModelForm

The ModelForm is the way Django communicates from the Django Models.

1
2
# Call myroot properties
from myroot.models import ContactUs

And of course, we need to load our own app’s model as well which is the ContactUs model.  By the way, you can separate it with a comma with space in between for multiple models to load.

Step 9:  Create a new View from vmain.py under dev/myroot/views/

Besides, create a new view inside the vmain.py, do you remember in our last event loop on {{ PLP }} about the Hello World app? if yes, create a new view called basic_crud_create_view to implement the insert statement.

1
2
3
4
5
from django.http import JsonResponse

# Call myroot properties
from myroot.forms.fmain import Basic_CRUD_Create_Form
from myroot.views.vfunctions import dict_alert_msg

Import the newly created form which is the Basic_CRUD_Create_Form and our customize function which is the dic_alert_msg as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def basic_crud_create_view(request):
    """Renders the basic crud create operation."""
    if request.method == 'GET':

        # Get contact us form to display
        form = Basic_CRUD_Create_Form()
        return render(request, 'myroot/basic_crud_create.html',
                      {'form': form,
                       'title': "Django Basic CRUD: CREATE New Row with Actual Example",
                       'meta_desc': """Learn Django basic CRUD to create a new rows and store it on dev_contact_us database table - """ + settings.SITE_FULL_NAME})

    data = dict()
    if request.method == 'POST':
        # Get the form data
        form = Basic_CRUD_Create_Form(request.POST)

        if form.is_valid():
            form.save()  # insert new row

            # Return some json response back to the user
            msg = """ Your data has been inserted successfully, thank you! """
            data = dict_alert_msg('True', 'Awesome!', msg, 'success')

        else:

            # Extract form.errors
            msg = None
            msg = [(k, v[0]) for k, v in form.errors.items()]
            data = dict_alert_msg('False', 'Oops, Error', msg, 'error')

        return JsonResponse(data)

By the way, I also imported the Django built-in library which is the JsonResponse so that any server response would be in JSON format as well.

Step 10:  Create a new Python file called vfunction.py under dev/myroot/views

Furthermore, create this vfunction.py Python file to organize all our none CRUD operations so that we can easily re-use any customize functions we’ve and use it accordingly throughout our Django project.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def dict_alert_msg(form_is_valid, alert_title, alert_msg, alert_type):
    """
    Function to call internal alert message to the user with the required
    paramaters: form_is_valid[True/False all small letters for json format],
    alert_title='string', alert_msg='string',
    alert_type='success, error, warning, info'
    """

    data = {
        'form_is_valid': form_is_valid,
        'alert_title': alert_title,
        'alert_msg': alert_msg,
        'alert_type': alert_type
    }
    return data

This dict_alert_msg function is to make our customize structured respond message into Python Dictionary format first before it can convert to a JSON format and then send back to the user’s browser as a form of a pop-up message or things alike.

Step 11:  Add new URL from urls.py

Next, add new URL to serve Django CRUD Create page over the browser so that you can probably test the functionality about inserting a new row to the database table called dev_contact_us.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.contrib import admin
from django.urls import path

# Call myroot properties
import myroot.views.vmain


urlpatterns = [
    path('admin/', admin.site.urls),
    path('helloworld/', myroot.views.vmain.hello_world_view,
         name='helloworld'),
    path('django-basic-crud-create/',
         myroot.views.vmain.basic_crud_create_view,
         name='basic_crud_create'),
]

However, we’re almost done with the Django CRUD Create, just hang on for a while.

Step 12: Create a new Django Template called basic_crud_create.html under myroot\templates\myroot\

To demonstrate, we need to create a simple HTML page and make it accessible over the browser and to test with the actual Django Form as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
{% extends "myroot/layout_home.html" %}
{% load static %}

{% block extra_styles_head %}
  <link rel="stylesheet" href="{% static 'summernote/summernote-bs4.css' %}">
{% endblock %}

{% block page_sub_title %}
<div class="jumbotron">
<div class="container">
<h1 class="display-3">{{ title }}</h1>
Welcome to Django Basic CRUD which is to CREATE or INSERT a new row to MySQL Database table name <b>dev_contact_us</b>.
Submit any sample data to test the Django Forms and how exactly it behaves.

<a class="btn btn-primary btn-lg" href="https://pinoylearnpython.com/basic-django-crud-using-ajax-json-and-jquery-monolithic-architecture/" role="button">Learn more »</a>
<a class="btn btn-secondary btn-lg" href="https://dev.pinoylearnpython.com/django-basic-crud-list/" role="button">Back to Django Manage Table Rows »</a>

</div>
</div>
{% endblock %}

{% block content %}
<div class="container">
<div class="row">
<div class="col-md-12 order-md-1">

                <form id="formContactUs" class="" method="POST">
                    {% csrf_token %}
<div class="form-label-group">
                        {{ form.full_name }}
<label for="id_full_name">Full Name</label></div>
<div class="form-label-group">
                        {{ form.email }}
<label for="id_email">Email</label></div>
<div class="form-label-group">
                        {{ form.subject }}
<label for="id_subject">Subject</label></div>
<div class="form-label-group">
                        {{ form.message }}</div>
</form>
<div class="">
                    <button class="btn btn-lg btn-primary btn-block" onclick="saveForm();" id="btnContactUs">Submit your Inquiry</button></div>
</div>
</div>
</div>
{% endblock %}

{% block scripts %}
<script src="{% static 'assets/js/common/common.js' %}" defer></script>
<script src="{% static 'summernote/summernote-bs4.min.js' %}" defer></script>
<script src="{% static 'sweetalert/sweetalert.js' %}" defer></script>

<script defer><br />
    var BASE_URL = "{{ BASE_URL }}";<br />
    var COMMON_ASSETS_URL = "{% static 'assets/images/' %}";</p>
<p>    function saveForm()<br />
    {<br />
        //Get the form instance<br />
        var $form = $("#formContactUs");<br />
        id_full_name = $("#id_full_name").val();<br />
        id_email = $("#id_email").val();<br />
        id_subject = $("#id_subject").val();<br />
        id_message = $("#id_message").val();</p>
<p>        if (id_full_name === undefined || id_full_name === null || id_full_name ===""){<br />
            swal("Full Name is Required!", "Please enter your full name", "error");<br />
            $("#id_full_name").focus();<br />
            return false;<br />
        }</p>
<p>        if (id_email === undefined || id_email === null || id_email ===""){<br />
            swal("Email is Required!", "Please enter your email", "error");<br />
            $("#id_email").focus();<br />
            return false;<br />
        }</p>
<p>        if (id_subject === undefined || id_subject === null || id_subject ===""){<br />
            swal("Subject is Required!", "Please enter your subject", "error");<br />
            $("#id_subject").focus();<br />
            return false;<br />
        }</p>
<p>        if (id_message === undefined || id_message === null || id_message ===""){<br />
            swal("Message is Required!", "Please enter your message", "error");<br />
            $("#id_message").focus();<br />
            return false;<br />
        }</p>
<p>        $.ajax({<br />
            method: "POST",<br />
            url: BASE_URL+'django-basic-crud-create/',<br />
            data: $form.serialize(),<br />
            cache: false,<br />
            dataType: "json",<br />
            beforeSend: function(){<br />
                //Start displaying button's working animation<br />
                var loadingText = '
<i class="fa fa-circle-o-notch fa-spin"></i> working...';<br />
                if ($("#btnContactUs").html() !== loadingText) {<br />
                    $("#btnContactUs").data('
original-text', $("#btnContactUs").html());<br />
                    $("#btnContactUs").html(loadingText);<br />
                }<br />
            },<br />
            success: function(jResults)<br />
            {<br />
                $("#btnContactUs").html($("#btnContactUs").data('
original-text')); //stop animation and switch back to original text</p>
<p>                if(jResults.alert_type =='
success'){<br />
                    swal(jResults.alert_title, jResults.alert_msg, jResults.alert_type);<br />
                    $form[0].reset(); // reset form data<br />
                    $("#id_message").summernote("reset");<br />
                    $("#id_full_name").focus();<br />
                }<br />
                else {<br />
                    var strErr = jResults.alert_msg + '
';<br />
                    strErr = strErr.split(",").pop();<br />
                    swal(jResults.alert_title, strErr, jResults.alert_type);<br />
                }<br />
            }<br />
        });<br />
    }</p>
<p>    $(document).ready(function()<br />
    {<br />
        $('
#id_message').summernote({<br />
            placeholder: 'Type your message here.',<br />
            tabsize: 2,<br />
            height: 300<br />
        });<br />
    });<br />
</script>
{% endblock %}

Now, I use few open source tools like the SummerNote for our WYSIWYG Editor on Bootstrap, Bootstrap 4, SweetAlert which is our nice pop-up message display, and of course the JQuery AJAX for the Asynchronous method.

Finally, upload all the necessary changes from our development PC to the web browser.  Now, you can play around and try submitting new data by filling up the Django Form using the Django Basic CRUD: CREATE New Row with Actual Example.

1
daphne -b 172.104.190.249 -p 8000 dev.asgi:application

IMPORTANT REMINDER: Always RESTART your daphne asynchronous web service to take effect whatever changes you’ve done only for Python files but for NONE Python files no need for you to restart the daphne web service.

I personally preferred to block any errors at the user’s browser first and prompt with nice pop-up error using SweetAlert to remove unnecessary routing at the web server.  The 2nd validation should be the Django Forms if it’s valid then the CRUD insert statement will then be executed, otherwise, it will prompt with any Django Form error that we also catch and response back to the requesting user bearing the default Django validation error messages.

Step 13:  Display database table rows using DataTables

Earlier, we’ve done inserting a new row in dev_contact_us database table and obviously we want to display it from our Django application, so, basically, we need to create a new Django template to manage the table rows accordingly.

Include the django.contrib.humanize from your settings.py under the INSTALLED_APPS.

1
2
3
4
5
6
7
8
9
10
11
12
# Application definition
INSTALLED_APPS = [
    'myroot',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.humanize',
    'channels',
]

Add new URL to handle the Django Basic CRUD: Manage Table Rows using DataTables HTML page. Then, open the dev/dev/urls.py and insert the new URL to display the new page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from django.contrib import admin
from django.urls import path

# Call myroot properties
import myroot.views.vmain


urlpatterns = [
    path('admin/', admin.site.urls),
    path('helloworld/', myroot.views.vmain.hello_world_view,
         name='helloworld'),

    path('django-basic-crud-create/',
         myroot.views.vmain.basic_crud_create_view,
         name='basic_crud_create'),

    path('django-basic-crud-list/',
         myroot.views.vmain.basic_crud_list_view,
         name='basic_crud_list'),
]

In addition, please open the dev/myroot/views/vmain.py and import it for us to use the Python timezone built-in library.

1
from django.utils import timezone

Next, add a new view from the dev/myroot/views/vmain.py and fetch at least the latest 50 rows from the dev_contact_us table.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def basic_crud_list_view(request):
    """
    This is the main Django Template to display the recent Basic CRUD
    table rows that you can manage using DataTable object.
    """

    if request.method == "GET":

        db_data = ContactUs.objects.filter(
                is_deleted=False
                ).order_by('-id')[:50] # fetch the latest 50 rows

        return render(request, 'myroot/basic_crud_list.html',
                      {
                          'title': 'Django Basic CRUD: Manage Table Rows using DataTables',
                          'meta_desc': 'Learn how to display the database table rows using DataTables.',
                          'db_data': db_data
                       })

Finally, create a new Django Template called
basic_crud_list.html from the dev/myroot/templates/myroot/ location.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
 {% extends "myroot/layout_home.html" %} {% load static %} {% load humanize %} {% load tz %} {% block extra_styles_head %}

<style>
    th.dt-center, td.dt-center { text-align: center; }<br />
</style> {% endblock %} {% block page_sub_title %}
<div class="jumbotron">
<div class="container">
<h1 class="display-3">{{ title }}</h1>
{{ meta_desc }}

<a class="btn btn-primary btn-lg" role="button" href="https://pinoylearnpython.com/basic-django-crud-using-ajax-json-and-jquery-monolithic-architecture/">Learn more »</a>

</div>
</div>
{% endblock %} {% block content %}
<div class="container">
<div class="row">
<div class="col-md-12 order-md-1">
<div class="card-body p-0">
<div class="row">
<div class="col-4 d-none d-sm-block">
<div id="dataTableExportButtons" class="mt-3"></div>
</div>
<div class="col-6 d-none d-sm-block">
<div class="form-group mb-0 pl-2 mt-3 float-right">
<div class="input-group"><button id="daterange-btn" class="btn btn-default" type="button"> <i class="fa fa-calendar"></i> Filter by date range <i class="fa fa-caret-down"></i> </button>
<div class="input-group-append"></div>
</div>
</div>
</div>
<div class="col-2 d-none d-sm-block">
<div class="form-group pl-2 mb-0 mt-3 pr-2 float-right">
<div id="divSearchFile" class="input-group"><input id="fsearch" class="form-control" name="fsearch" type="text" placeholder="Search Records" />
<div class="input-group-append"></div>
</div>
</div>
</div>
{% if db_data %} {% for d in db_data %}{% endfor %} {% endif %}
<table id="tblData" class="table table-bordered table-striped">
<thead>
<tr>
<th>Full Name</th>
<th class="must_hide">Subject</th>
<th class="must_hide">Email</th>
<th class="must_hide">Submitted</th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody id="tbData">
<tr id="tr{{ d.id }}">
<td>{{ d.full_name }}</td>
<td class="must_hide">{{ d.subject|truncatechars:100 }}</td>
<td>{{ d.email }}</td>
<td class="must_hide">{{ d.submitted|localtime|timesince }}</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>{{ d.id }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock %} {% block scripts %} <script src="{% static 'assets/js/common/common.js' %}" defer></script> <script src="{% static 'sweetalert/sweetalert.js' %}" defer></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.2/moment.min.js"></script> <script defer src="{% static 'adminlte3/plugins/daterangepicker/daterangepicker.js' %}"></script>

<!-- dataTable objects -->

<script defer src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script> <script defer src="https://cdn.datatables.net/1.10.16/js/dataTables.bootstrap4.min.js"></script> <script defer src="https://cdn.datatables.net/buttons/1.5.1/js/dataTables.buttons.min.js"></script> <script defer src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.bootstrap4.min.js"></script> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/pdfmake.min.js"></script> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/vfs_fonts.js"></script> <script defer src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.html5.min.js"></script> <script defer src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.print.min.js"></script> <script defer src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.colVis.min.js"></script> <script defer></p>
<p>    var mStartDate = moment().format('YYYY-MM-DD');<br />
    var mEndDate = moment().format('YYYY-MM-DD');</p>
<p>    var BASE_URL = "{{ BASE_URL }}";<br />
    var COMMON_ASSETS_URL = "{% static 'assets/images/' %}";<br />
    var THIS_OBJ = '';</p>
<p>    $(function () {</p>
<p>        var table = $('#tblData').DataTable({<br />
            "paging": true,<br />
            "lengthChange": false,<br />
            "searching": false,<br />
            "ordering": true,<br />
            "info": true,<br />
            "autoWidth": false,<br />
            "columnDefs": [<br />
                { "orderable": false, "targets": [4, 5, 6, 7], "className": 'dt-center' },<br />
                { "visible": false, "targets": 8 }<br />
            ],<br />
            "order": [[ 8, "desc" ]],<br />
            "buttons": [ 'copy', 'excel', 'pdf', 'colvis' ]<br />
        });</p>
<p>        table.buttons().container()<br />
        .appendTo('#dataTableExportButtons');</p>
<p>        //Date range as a button<br />
    $('#daterange-btn').daterangepicker(<br />
      {<br />
        ranges   : {<br />
            'Today'       : [moment(), moment()],<br />
            'Yesterday'   : [moment().subtract(1, 'days'), moment().subtract(1, 'days')],<br />
            'Last 7 Days' : [moment().subtract(6, 'days'), moment()],<br />
            'Last 30 Days': [moment().subtract(29, 'days'), moment()],<br />
            'This Month'  : [moment().startOf('month'), moment().endOf('month')],<br />
            'Last Month'  : [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]<br />
        },<br />
        startDate: moment(),<br />
        endDate  : moment()<br />
      },<br />
        function (start, end)<br />
        {<br />
             mStartDate = start.format('YYYY-MM-DD');<br />
             mEndDate = end.format('YYYY-MM-DD');<br />
             $('#daterange-btn span').html(start.format('MMMM D, YYYY') + ' - ' + end.format('MMMM D, YYYY'))<br />
        }<br />
    )<br />
    });</p>
<p>    $(document).ready(function()<br />
    {<br />
        //Must hide table columns when small screens<br />
        if ($(window).width() < 700){
            $('.must_hide').hide();
        }

    });
</script> {% endblock %}

Therefore, upload all the changes to your web server and then restart your daphne web service once again, then try the actual example that I’ve provided for you so you can test it accordingly.

1
daphne -b 172.104.190.249 -p 8000 dev.asgi:application

Here is the actual example that we’ve just created called Django Basic CRUD: Manage Table Rows using DataTables page.

Step 14: Django Basic CRUD for Delete Statement

Though, literally, I don’t recommend to delete the physical row from the database table once the user triggers the delete functionality of our app but instead, we’ve just updated the row status which is the is_deleted, deleted_date, and the deleted_by fields will be modified for each row that has been marked as deleted.

But, of course, I also included the physical row deletion due to the fact that somehow, it’s required for our Django project to have some kind of database maintenance for some certain period for those marked as deleted rows to be scheduled physical delete statement to free up some unwanted rows. It’s really up to your project requirements and Company IT Policies as well.

Create a new view to marked the selected row as deleted

Now, create a new view under dev/myroot/views/vmain.py and copy the code snippet below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def basic_crud_del_row_view(request):
    """To delete selected row data"""
    data = dict()
    if request.method == 'POST':
        row_id = request.POST.get('row_id')

        # Just update 'is_deleted=False' status of the row only
        ContactUs.objects.filter(
                id=row_id).update(is_deleted=True,
                                  deleted_by=1,
                                  deleted_date=timezone.now())

        # Return some json response back to the user
        msg = """Poof! Your selected data row has been deleted!"""
        data = dict_alert_msg('True', 'Success!', msg, 'success')
        return JsonResponse(data)

Have you noticed something about the deleted_by=1? this is because, for now, we don’t require a user account to access this page and delete the row without any restrictions of some kind. It’s quite straightforward, but don’t worry, I’ll be discussing this in more details as we going deeper with our advanced topic about the Django CRUD.

1
2
# To delete the selected row permanently from the table
ContactUs.objects.filter(id=row_id).delete()

Now, a quick overview of deleting the selected row physically from the database table, execute the above command. For more information about the Django queries, visit their official documentation.

Add new URL for basic_crud_del_row_view

Subsequently, add a new URL for the new view which is the basic_crud_del_row_view when the user triggers the delete icon.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django.contrib import admin
from django.urls import path

# Call myroot properties
import myroot.views.vmain


urlpatterns = [
    path('admin/', admin.site.urls),
    path('helloworld/', myroot.views.vmain.hello_world_view,
         name='helloworld'),

    path('django-basic-crud-create/',
         myroot.views.vmain.basic_crud_create_view,
         name='basic_crud_create'),

    path('django-basic-crud-list/',
         myroot.views.vmain.basic_crud_list_view,
         name='basic_crud_list'),

    path('django_basic_crud_delete/',
         myroot.views.vmain.basic_crud_del_row_view,
         name='basic_crud_delete'),
]

Next, add the Javascript before the $(document).ready(function()

Now, add the JavaScript and the AJAX script when the delete icon triggered by the user with a nice pop-up confirmation provided by the SweetAlert.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function confirmUserDeleteAction(pThis)
    {
        THIS_OBJ = pThis;
        var csrfmiddlewaretoken = Cookies.get('csrftoken');
        var row_id = $(THIS_OBJ).attr("row-id");
       
        //confirm user action
        swal({
            title: "Are you sure?",
            text: "Once deleted, you will not be able to recover this data!",
            icon: "warning",
            buttons: true,
            dangerMode: true,
        })
        .then((willDelete) => {
            if (willDelete) {
                delRow();
            }
        });
    }

    function delRow()
    {
        var csrfmiddlewaretoken = Cookies.get('csrftoken');
        var row_id = $(THIS_OBJ).attr("row-id");

        if (csrfmiddlewaretoken === undefined || csrfmiddlewaretoken === null || csrfmiddlewaretoken === ""){
            swal("CSRF Token is Missing!", "Please refresh this page and try again.", "error");
            return false;
        }

        $.ajax({
            method: "POST",
            url: BASE_URL+'django_basic_crud_delete/',
            data: "csrfmiddlewaretoken="+ csrfmiddlewaretoken +"&row_id="+row_id,
            cache: false,
            dataType: "json",
            success: function(jResults)
            {
                if(jResults.alert_type =='success'){
                    swal(jResults.alert_title, jResults.alert_msg, jResults.alert_type);
                    var table = $('#tblData').DataTable();
                    table.row("#tr"+row_id).remove().draw( false );
                }
                else {
                    var strErr = jResults.alert_msg + '';
                    strErr = strErr.split(",").pop();
                    swal(jResults.alert_title, strErr, jResults.alert_type);
                }
            }
        });
    }

For testing purpose, you may delete a row from Django Basic CRUD: Manage Table Rows using DataTables page and see the actual behavior once you’ve clicked the trash icon.

Step 15: Simple Django Dynamic Page

Since we’ve got the dynamic data that stores from the database table and somehow we’re trying to display this information on a public page so users can engage in a public discussion or what so ever purpose you may have. So, let’s get started on this.

Import the built-in Django library which is the Http404

Now, open the dev/myroot/views/vmain.py and include the code snippet below at the top section of the codes always.

1
from django.http import Http404

Create a new View for the dynamic public page

Still at the same vmain.py file, insert the code below at the bottom or it’s up to our own liking.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def basic_crud_dynamic_public_page_view(request, slug, id):
    """Renders the dynamic public page."""
    if request.method == 'GET':

        # Get match table row by id
        db_data = ContactUs.objects.filter(is_deleted=False, id=id)

        # Use for loop to get the filtered row from a database table
        meta_title = ""
        meta_desc = ""

        for d in db_data:
            meta_title = d.full_name
            meta_desc = d.subject

        if db_data.count():
            return render(request, 'myroot/basic_django_dynamic_public_page.html',
                          {'title': meta_title + " - Django Basic Dynamic Public Page" + ' [' + str(id) + ']',
                           'meta_desc': meta_desc + ' [' + str(id) + ']',
                           'db_data': db_data})
        else:
            raise Http404()

Meanwhile, in our new View, did you noticed about this line at the beginning of the said View.

1
def basic_crud_dynamic_public_page_view(request, slug, id):

We’re accepting 2 parameters which are the slug and the id aside from the
request which is the default parameter from Django.

Also, I’m iterating the row after the Django filter event although it’s only 1 row will fetch but still you need to iterate it using the for loop statement.

1
2
3
4
5
6
7
# Use for loop to get the filtered row from a database table
        meta_title = ""
        meta_desc = ""

        for d in db_data:
            meta_title = d.full_name
            meta_desc = d.subject

By the way, why I need to iterate at the View level at this time?, because I wanted to get few key fields that I need in order for me to display some good information about the meta attributes that need to display the dynamic information such as the title of the page and meta description as well.

Add a new dynamic link from the URLS.py

Now, we need to add a new link with 2 parameters as well, here’s how.

1
2
3
path('<slug:slug>-<int:id>/',
         myroot.views.vmain.basic_crud_dynamic_public_page_view,
         name='basic_crud_dyn_pub_page'),

However, you need to match the URL parameter including the parameter names and the parameter positions in contrast with the View parameters as well so that it will match all the information when the new URL will be accessed by the users.

Finally, to demonstrate the dynamic page, access this page and click on that globe icon in each row so that you’ll know how it works.

Step 16: Django Basic CRUD for Update Statement

Likewise, the Django update statement using our existing form during the CREATE statement, we can re-use it accordingly when we select a row for editing purpose, here’s how.

Create a new View to cater the Edit mode form

Next, we need to create a new view from dev/myroot/views/vmain.py and add the code snippet below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def basic_crud_update_row_view(request, id):
    """Renders the Django edit form page and execute the update statement."""
    if request.method == "GET":

        # Get the selected row information
        db_data = ContactUs.objects.filter(id=id, is_deleted=False)

        if db_data:

            # Edit form data
            edit_data = ContactUs.objects.get(id=id, is_deleted=False)
            formEdit = Basic_CRUD_Create_Form(instance=edit_data)

            return render(request, 'myroot/basic_crud_update.html',
                          {
                              'title': 'Django Basic CRUD Update Statement',
                              'meta_desc': 'This is the actual example on Django basic crud which is the update statement.',
                              'id': id,
                              'formEdit': formEdit
                           })
        else:
            raise Http404()

    data = dict()
    if request.method == "POST":

        # Get the  form modified data
        form_edit = Basic_CRUD_Create_Form(request.POST)
        id = request.POST.get('id')

        if form_edit.is_valid():

            # Check if the row still not deleted
            if ContactUs.objects.filter(id=id, is_deleted=False).exists():

                # Get the form edit instance
                update_data = ContactUs.objects.get(id=id, is_deleted=False)

                # Now, supply the form data to an instance
                form_edit = Basic_CRUD_Create_Form(request.POST, instance=update_data)
                form_edit.save()  # Finally save the form data

                # Return some json response back to the user
                msg = """ Your data has been modified successfully, thank you! """
                data = dict_alert_msg('True', 'Awesome!', msg, 'success')

            else:
                # Return some json response back to the user
                msg = """ The data has no longer existed, the update has been aborted! """
                data = dict_alert_msg('True', 'Update Failed!', msg, 'error')
        else:

            # Extract form.errors
            msg = None
            msg = [(k, v[0]) for k, v in form_edit.errors.items()]
            data = dict_alert_msg('False', 'Oops, Error', msg, 'error')

        return JsonResponse(data)

During the GET Method event, this line of code will catch up with the URL parameter which is the id and try to query from the database table if the matching id exists or not.

1
db_data = ContactUs.objects.filter(id=id, is_deleted=False)

Otherwise, we will throw the 404-page error. However, if the query result with the matching row id and then store it to a Django instance including all the row columns and the row data as well.

1
2
edit_data = ContactUs.objects.get(id=id, is_deleted=False)
formEdit = Basic_CRUD_Create_Form(instance=edit_data)

Moreover, passing the formEdit query set to a Django context so we can display the information to a Django form later on.

Create a new URL for the Edit Form page.

Again, create a new URL to access the edit form with a single parameter which is the id. So, open the dev/dev/urls.py and insert the code snippet below.

1
2
path('basic_crud/<int:id>/change/', myroot.views.vmain.basic_crud_update_row_view,
         name='change_basic_crud'),

The <int:id> is how Django URL pattern to accept only the integer value and it handles internally those validations. Awesome!

Create a new Django Template for the Edit Form page.

Next, create a new HTML page for the Edit Form page which will be located at the dev/myroot/templates/myroot/basic_crud_update.html.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
 {% extends "myroot/layout_home.html" %} {% load static %} {% block extra_styles_head %} {% endblock %} {% block page_sub_title %}
<div class="jumbotron">
<div class="container">
<h1 class="display-3">{{ title }}</h1>
Welcome to Django Basic CRUD which is to UPDATE an existing row to MySQL Database table name <b>dev_contact_us</b>. Modify the sample data to test the Django Forms and how exactly it behaves.

<a class="btn btn-primary btn-lg" role="button" href="https://pinoylearnpython.com/basic-django-crud-using-ajax-json-and-jquery-monolithic-architecture/">Learn more »</a> <a class="btn btn-secondary btn-lg" role="button" href="https://dev.pinoylearnpython.com/django-basic-crud-list/">Back to Django Manage Table Rows »</a>

</div>
</div>
{% endblock %} {% block content %}
<div class="container">
<div class="row">
<div class="col-md-12 order-md-1"><form id="formContactUs" class="" method="POST">{% csrf_token %}
<div class="form-label-group">{{ formEdit.full_name }} <label for="id_full_name">Full Name</label></div>
<div class="form-label-group">{{ formEdit.email }} <label for="id_email">Email</label></div>
<div class="form-label-group">{{ formEdit.subject }} <label for="id_subject">Subject</label></div>
<div class="form-label-group">{{ formEdit.message }}</div>
</form>
<div class=""><button id="btnContactUs" class="btn btn-lg btn-primary btn-block">Update</button></div>
</div>
</div>
</div>
{% endblock %} {% block scripts %} <script src="{% static 'assets/js/common/common.js' %}" defer></script> <script src="{% static 'summernote/summernote-bs4.min.js' %}" defer></script> <script src="{% static 'sweetalert/sweetalert.js' %}" defer></script> <script defer><br />
    var BASE_URL = "{{ BASE_URL }}";<br />
    var COMMON_ASSETS_URL = "{% static 'assets/images/' %}";</p>
<p>    function saveForm()<br />
    {<br />
        //Get the form instance<br />
        var $form = $("#formContactUs");<br />
        id_full_name = $("#id_full_name").val();<br />
        id_email = $("#id_email").val();<br />
        id_subject = $("#id_subject").val();<br />
        id_message = $("#id_message").val();<br />
        var id = "{{ id }}";</p>
<p>        if (id_full_name === undefined || id_full_name === null || id_full_name ===""){<br />
            swal("Full Name is Required!", "Please enter your full name", "error");<br />
            $("#id_full_name").focus();<br />
            return false;<br />
        }</p>
<p>        if (id_email === undefined || id_email === null || id_email ===""){<br />
            swal("Email is Required!", "Please enter your email", "error");<br />
            $("#id_email").focus();<br />
            return false;<br />
        }</p>
<p>        if (id_subject === undefined || id_subject === null || id_subject ===""){<br />
            swal("Subject is Required!", "Please enter your subject", "error");<br />
            $("#id_subject").focus();<br />
            return false;<br />
        }</p>
<p>        if (id_message === undefined || id_message === null || id_message ===""){<br />
            swal("Message is Required!", "Please enter your message", "error");<br />
            $("#id_message").focus();<br />
            return false;<br />
        }</p>
<p>        $.ajax({<br />
            method: "POST",<br />
            url: BASE_URL+'basic_crud/' + id + '/change/',<br />
            data: $form.serialize() + '&id='+id,<br />
            cache: false,<br />
            dataType: "json",<br />
            beforeSend: function(){<br />
                //Start displaying button's working animation<br />
                var loadingText = '
<i class="fa fa-circle-o-notch fa-spin"></i> working...';<br />
                if ($("#btnContactUs").html() !== loadingText) {<br />
                    $("#btnContactUs").data('
original-text', $("#btnContactUs").html());<br />
                    $("#btnContactUs").html(loadingText);<br />
                }<br />
            },<br />
            success: function(jResults)<br />
            {<br />
                $("#btnContactUs").html($("#btnContactUs").data('
original-text')); //stop animation and switch back to original text</p>
<p>                if(jResults.alert_type =='
success'){<br />
                    swal(jResults.alert_title, jResults.alert_msg, jResults.alert_type);<br />
                    $("#id_full_name").focus();<br />
                }<br />
                else {<br />
                    var strErr = jResults.alert_msg + '
';<br />
                    strErr = strErr.split(",").pop();<br />
                    swal(jResults.alert_title, strErr, jResults.alert_type);<br />
                }<br />
            }<br />
        });<br />
    }</p>
<p>    $(document).ready(function()<br />
    {<br />
        $('
#id_message').summernote({<br />
            placeholder: 'Type your message here.',<br />
            tabsize: 2,<br />
            height: 300<br />
        });<br />
    });<br />
</script> {% endblock %}

Did you notice on how we display the row data from the Django form edit mode? here’s how.

1
{{ formEdit.full_name }}

It’s so simple to display the row information to a specified text box or any HTML elements that accepts input from the user. Another important thing to always include the {% csrf_token %} inside the Django form as default protection provided by Django by itself.

Finally, upload all the modified and new files to your web server using the FileZilla.

Now, after all the changes have been uploaded successfully, reload your daphne web service to take effect all the changes to your Django site project.

1
daphne -b 172.104.190.249 -p 8000 dev.asgi:application

Indeed, the Basic Django CRUD is fun and easy learning, now access the Django Basic CRUD: Manage Table Rows using DataTables for our live demo to personally experience our today’s event loop.

You can download the source code from our GitHub repositories at https://github.com/pinoylearnpython/dev and stay tuned for any updates.

In the next event loop on {{ PLP }}.

Congratulations!, if you can display everything we’re discussing here from the basic CREATE, UPDATE, and DELETE statement in Django plus with the basic dynamic public page, that’s great!

For those who’re not able to successfully launch your Basic Django CRUD tutorial or you need more clarifications, don’t worry, leave a comment below and I’m happy to help you to succeed.

See you in the next event loop on the Basic Django Search Queries with Pagination.

That’s all, have fun learning with {{ PLP }}.

To help Filipino students to learn Python programming language with Django to enhance their capabilities in developing robust web-based applications with practical and direct to the point tutorials, step-by-step with actual information that I provided for you. Leave a comment below or email me at [email protected], thank you!