Building a CRUD application using Python and Django

Building a CRUD application using Python and Django

Building a CRUD application using Python and Django - Django is a web application framework written in Python. If you want to create the CRUD application using Python and Django, it is enough to ...

Introduction:

I’ve been meaning to write a series on Django which is a web application framework written in Python. To follow this tutorial you don’t need to be a pro in python and have to know it inside-out. Just some basics will get you through it.

Before we start writing applications, we must know a little about what is Django. Django is a web application framework that acts more of an MTV pattern instead of MVC. Think it this way:

  • Model remains model

  • View has been replaced by Templates

  • Controller gets replaced by View

A simple Hello-world application is just a few lines of code in Django! But moving from this simple thing a full fledged application can be a daunting task in itself. There are other concepts that can help proceed further such as ORM, migrations etc that help in building a bigger application. But for this tutorial we’ll be building a simple CRUD( Create, Retrieve, Update and Delete ) application.

To get started with you need to have python and virtualenv installed on your machine. Python is already installed on the linux systems. But you'll need to install virtualenv. To install virtualenv follow the command:

sudo apt-get install python-pip
sudo pip install virtualenv

Application Structure

Before we actually start writing code we need to get a hold of the application structure. We'll first execute several commands that are essential in django project development.

After installing virtualenv, we need to set the environment up.

virtualenv venv

We are setting a virtual environment of the name venv here. Now we need to activate it.

source venv/bin/activate
cd venv

Now that it has been activated. We need to start our project. Feed in the following command to start a project.

pip install django==1.11.8
mkdir app && cd app
django-admin startproject crudapp

The first line installs Django v1.11.8 and creates a directory named app in the parent directory. the third line starts a project named crudapp in the app directory. The directory tree should look like

app
└── crudapp
    ├── crudapp
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── manage.py

We'll see the meaning of each file and what it does one by one. But first, to test if you are going in the right directoion, run the following command.

python manage.py runserver

If you get an output like below then you're doing it right.

Let's see what exactly the different files that we created mean.

  • __init__.py : Acts as an entry point for your python project.

  • settings.py : Describes the configuration of your Django installation and lets Django know which settings are available.

  • urls.py : used to route and map URLs to their views.

  • wsgi.py : contains the configuration for the Web Server Gateway Interface. The Web Server Gateway Interface (WSGI) is the Python platform standard for the deployment of web servers and applications.

Writing the Application

Now this is where we start coding our app. For this operation we'll consider blog post as our entity. We'll be applying CRUD operations to blog posts.

The app in our project will be called blog_posts.

python manage.py startapp blog_posts

This will create the necessary files that we require.

First and foremost create the Model of our application.

# -*- coding: utf-8 -*-
	from __future__ import unicode_literals
from django.db import models


# Create your models here.
class blog_posts(models.Model):
    title = models.CharField(max_length=400)
    tag = models.CharField(max_length=50)
    author = models.CharField(max_length=120)


    def __unicode__(self):
        return self.title


    def get_post_url(self):
        return reverse('post_edit', kwargs={'pk': self.pk})

Now that we have our model ready, we'll need to migrate it to the database.

python manage.py makemigrations
python manage.py migrate

Now we create our Views where we define each of our CRUD definition.

# -- coding: utf-8 --
from future import unicode_literals

from django.shortcuts import render, redirect, get_object_or_404
from django.forms import ModelForm


from blog_posts.models import blog_posts
# Create your views here.


class PostsForm(ModelForm):
    class Meta:
        model = blog_posts
        fields = ['id', 'title', 'author']


def post_list(request, template_name='blog_posts/post_list.html'):
    posts = blog_posts.objects.all()
    data = {}
    data['object_list'] = posts
    return render(request, template_name, data)


def post_create(request, template_name='blog_posts/post_form.html'):
    form = PostsForm(request.POST or None)
    if form.is_valid():
        form.save()
        return redirect('blog_posts:post_list')
    return render(request, template_name, {'form': form})


def post_update(request, pk, template_name='blog_posts/post_form.html'):
    post = get_object_or_404(blog_posts, pk=pk)
    form = PostsForm(request.POST or None, instance=post)
    if form.is_valid():
        form.save()
        return redirect('blog_posts:post_list')
    return render(request, template_name, {'form': form})


def post_delete(request, pk, template_name='blog_posts/post_delete.html'):
    post = get_object_or_404(blog_posts, pk=pk)
    if request.method=='POST':
        post.delete()
        return redirect('blog_posts:post_list')
    return render(request, template_name, {'object': post})

Now that we have our Views, we create mappings to URL in our /crudapp/blog_posts/urls.py file. Make a note that the following is our app specific mappings.

"""
crudapp URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
"""
from django.conf.urls import url
from django.contrib import admin
from blog_posts import views


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', views.post_list, name='post_list'),
    url(r'^new$', views.post_create, name='post_new'),
    url(r'^edit/(?P<pk>\d+)$', views.post_update, name='post_edit'),
    url(r'^delete/(?P<pk>\d+)$', views.post_delete, name='post_delete'),
]

Now we create project specific mappings in /crudapp/crudapp/urls.py.

"""
Crudapp URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
     https://docs.djangoproject.com/en/1.11/topics/http/urls/
 """
from django.conf.urls import url, include
from django.contrib import admin
from crudapp.views import home


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog_posts/', include('blog_posts.urls', namespace='blog_posts')),
    url(r'^$', home, name='home' ),
]

Now almost everything is done and all we need to do is create our templates to test the operations.

Go ahead and create a templates/blog_posts directory in crudapp/blog_posts/.

  • templates/blog_posts/post_list.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
<title>Django CRUD application!</title>
</head>
<body>
<div class="container">
<h1>Blog Post List!</h1>
<ul>
{% for post in object_list %}
<li><p>Post ID: <a href="{% url "blog_posts:post_edit" post.id %}">{{ post.id }}</a></p>
<p>Title: {{ post.title }}</p>
<a href="{% url "blog_posts:post_delete" post.id %}">Delete</a>
</li>
{% endfor %}
</ul>

    &lt;a href="{% url "blog_posts:post_new" %}"&gt;New Blog post entry&lt;/a&gt;
&lt;/div&gt;

</body>
</html>

templates/blogposts/postform.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
<title>Django CRUD application!</title>
</head>
<body>
<div class="container">
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input class="btn btn-primary" type="submit" value="Submit" />
</form>
</div>
</body>
</html>

templates/blogposts/postdelete.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
<title>Django CRUD application!</title>
</head>
<body>
<div class="container">
<form method="post">{% csrf_token %}
Are you sure you want to delete "{{ object }}" ?
<input class="btn btn-primary" type="submit" value="Submit" />
</form>
</div>
</body>
</html>

Now we have all the necessary files and code that we require.

The final project tree should look like following:

crudapp
├── blog_posts
│   ├── admin.py
│   ├── apps.py
│   ├── init.py
│   ├── migrations
│   ├── models.py
│   ├── templates
│   │   └── blog_posts
│   │   ├── post_delete.html
│   │   ├── post_form.html
│   │   └── post_list.html
│   ├── tests.py
│   ├── urls.py
│   ├── views.py
├── crudapp
│   ├── init.py
│   ├── settings.py
│   ├── urls.py
│   ├── views.py
│   ├── wsgi.py
├── db.sqlite3
├── manage.py
└── requirements.txt

Execute python manage.py runserver and voila!! You have your Django app ready.

Originally published by  Nitin Prakash at  zeolearn.com

===================================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Learn More

☞ Complete Python Bootcamp: Go from zero to hero in Python 3

☞ Python for Time Series Data Analysis

☞ Python Programming For Beginners From Scratch

☞ Python Network Programming | Network Apps & Hacking Tools

☞ Intro To SQLite Databases for Python Programming

☞ Ethical Hacking With Python, JavaScript and Kali Linux

☞ Beginner’s guide on Python: Learn python from scratch! (New)

☞ Python for Beginners: Complete Python Programming



Python Django Tutorial | Django Course

Python Django Tutorial | Django Course

🔥Intellipaat Django course: https://intellipaat.com/python-django-training/ 👉This Python Django tutorial will help you learn what is django web development &...

This Python Django tutorial will help you learn what is django web development & application, what is django and introduction to django framework, how to install django and start programming, how to create a django project and how to build django app. There is a short django project as well to master this python django framework.

Why should you watch this Django tutorial?

You can learn Django much faster than any other programming language and this Django tutorial helps you do just that. Our Django tutorial has been created with extensive inputs from the industry so that you can learn Django and apply it for real world scenarios.

Developing Restful APIs with Python, Django and Django Rest Framework

Developing Restful APIs with Python, Django and Django Rest Framework

This article is a definitive guide for starters who want to develop projects with RESTful APIs using Python, Django and Django Rest Framework.

This article is a definitive guide for starters who want to develop projects with RESTful APIs using Python, Django and Django Rest Framework.

Introduction
  • Django is a web framework written in Python
  • Python is an interpreted high-level programming language for general-purpose programming
  • API or Application Programming Interface is a set of rules and mechanisms by which one application or component interacts with the others
  • REST or Representational State Transfer is a software architecture

REST APIs

As described in a dissertion by Roy Fielding,

REST is an "architectural style' that basically exploits the existing technology and protocols of the web.
In simple definition, it is the data representation for a client in the format that is suitable for it.

Hence, RESTful + API is a commonly used terminology for the implementation of such architecture and constraints (eg. in web services).

Here is an example GET request from GitHub's API

$ curl https://api.github.com/users/joshuadeguzman

You will see an output similar to this

{
  "login": "joshuadeguzman",
  "id": 20706361,
  "node_id": "MDQ6VXNlcjIwNzA2MzYx",
  "avatar_url": "https://avatars1.githubusercontent.com/u/20706361?v=4",
  "gravatar_id": "",
  "url": "https://api.github.com/users/joshuadeguzman",
  "html_url": "https://github.com/joshuadeguzman",
  "followers_url": "https://api.github.com/users/joshuadeguzman/followers",
  "following_url": "https://api.github.com/users/joshuadeguzman/following{/other_user}",
  "gists_url": "https://api.github.com/users/joshuadeguzman/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/joshuadeguzman/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/joshuadeguzman/subscriptions",
  "organizations_url": "https://api.github.com/users/joshuadeguzman/orgs",
  "repos_url": "https://api.github.com/users/joshuadeguzman/repos",
  "events_url": "https://api.github.com/users/joshuadeguzman/events{/privacy}",
  "received_events_url": "https://api.github.com/users/joshuadeguzman/received_events",
  "type": "User",
  "site_admin": false,
  "name": "Joshua de Guzman",
  "company": "@freelancer",
  "blog": "https://joshuadeguzman.me",
  "location": "Manila, PH",
  "email": null,
  "hireable": true,
  "bio": "Android Engineer at @freelancer. Building tools for humans.",
  "public_repos": 75,
  "public_gists": 2,
  "followers": 38,
  "following": 10,
  "created_at": "2016-07-28T15:19:54Z",
  "updated_at": "2019-06-16T10:26:39Z"
}

Shown above is a data set in JSON format.

JSON or JavaScript Object Notation is an open-standard file format that uses human-readable text to transmit data objects consisting of attribute–value pairs and array data types.
Other formats include XML, INI, CSV, etc. But today, JSON is widely use for its structure is intuitive, making it comfortable to read and map domain objects no matter what programming language is being used.

Python and Django

Python, according to its creator, Guido van Rossum, is a

high-level programming language, and its core design philosophy is all about code readability and a syntax which allows programmers to express concepts in a few lines of code.
Python uses english like words representation (eg. for methods, reserve keywords and control flow) that makes it easier for any beginner to jump right into it. It also features dynamic type system meaning it verifies the type safety of program at runtime. It also does automatic memory management.

print(5 + 5) # This will result to 10

Django is a high-level Python Web Framework that enables developers to deliver projects on time with clean and pragmatic design.

Its flagship features include a design for fast development, a secure and scalable product.

Quick Django Overview

Django's way of propagating changes to your database schema is by means of its migration modules.

Sample User model

from django.db import models

class User(models.Model):
    first_name = models.CharField(max_length=50)
    middle_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def __str__(self):
        return self.name

If any changes are made on your models, run makemigrations

$ python manage.py makemigrations

Finally, you can synchronize the database with the set of models and migrations

$ python manage.py migrate

REST APIs with Django Rest Framework

DRF or Django REST Framework is a powerful and flexible toolkit for building Web APIs. It helps the developers to not reinvent the wheel by rolling out complex and solid REST API from scratch by themselves. Because when your projects become more and more complex, you will soon realise the need of using DRF or other helpful rest framework.

1. Installation & Project Setup

Create project directory

$ mkdir djangoapi

Install virtualenv via pip

A virtual environment enables a project to have additional libraries or changes in packages within its environment without disturbing global or libraries of other environments.

pip is a package management system used to install and manage software packages written in Python.

$ pip install virtualenv

To create an environment folder in your project's directory

$ cd djangoapi
$ virtualenv venv

To activate the environment

$ source venv/bin/activate

To undo these changes to your path, simply run deactivate. More on virtualenv.

Install django, djangorestframework

$ pip install django
$ pip install djangorestframework

Creating a django project

$ django-admin startproject blog

Running your project

$ python manage.py runserver

System check identified no issues (0 silenced).

You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

August 16, 2018 - 09:58:36
Django version 2.1, using settings 'blog.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

The unapplied migrations refer to the default migration files included when you start a django project.

To synchronize these migration files, simply run migrate

$ python manage.py migrate

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 sessions.0001_initial... OK

The default database in our project is currently set to SQLite named db.sqlite3.

Creating a django project's app

$ cd blog
$ python manage.py startapp posts

The project structure should look like

$ find .
./posts
./posts/migrations
./posts/migrations/__init__.py
./posts/models.py
./posts/__init__.py
./posts/apps.py
./posts/admin.py
./posts/tests.py
./posts/views.py
./db.sqlite3
./blog
./blog/__init__.py
./blog/__pycache__
./blog/__pycache__/settings.cpython-36.pyc
./blog/__pycache__/wsgi.cpython-36.pyc
./blog/__pycache__/__init__.cpython-36.pyc
./blog/__pycache__/urls.cpython-36.pyc
./blog/settings.py
./blog/urls.py
./blog/wsgi.py
./manage.py

2. Model

Each model instance is a definitive source of the information about your data. In general, each model pertains to a single table in your database.

# djangoapi/blog/posts/models.py
from django.db import models

# Create your models here.

class Post(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    is_featured = models.BooleanField(default=False)

    def __str__(self):
        return self.name

__str__ is called by the str() built-in function and by the print statement to compute the "informal" string representation of an object.
If you try running makemigrations, django won't see those changes yet.

$ No changes detected

To solve this, add your posts app to your project's installed apps.

# djangoapi/blog/blog/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'posts' # Add it here
]

To continue with the migration of models

$ python manage.py makemigrations

Migrations for 'posts':
  posts/migrations/0001_initial.py
    - Create model Post

$ python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, posts, sessions
Running migrations:
  Applying posts.0001_initial... OK


3. Serialization

Serializers allow data structure or object state to be translated into a format that can be stored or transmitted and be reconstructed later on.

Create API's serializers.py and views.py files and isolate them like this

# posts/api
posts/api/serializers.py
posts/api/views.py

# posts/migrations
posts/migrations/

# posts
posts/admin.py
posts/apps.py
posts/models.py
posts/tests.py
posts/views.py
# posts/api/serializers.py

from ..models import Post
from rest_framework import serializers

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ('title', 'content', 'is_featured') # if not declared, all fields of the model will be shown

In this tutorial we have used ModelSerializer, more on this.

4. Views

A view function, or view for short, is a Python function that takes a Web request and returns a Web response.

# posts/api/views.py

from ..models import Post
from . import serializers
from rest_framework import generics, status
from rest_framework.response import Response

class PostListView(generics.ListAPIView):
    queryset = Post.objects.all()
    serializer_class = serializers.PostSerializer

As seen above, ListAPIView is used for read-only endpoints to represent a collection of model instances.

In this code snippet, we use generics view methods from the rest_framework, more on this.

5. URLs

This is where we setup our routes or URL paths to our designated views in which we expect specific responses for each.

# posts/urls.py

from django.urls import path
from . import views
from .api import views

urlpatterns = [
    path('', views.PostListView.as_view(), name=None)
]

6. Finalizing Setup

Ensure that the rest_framework is added to our project's apps.

# djangoapi/blog/blog/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework', # Add it here
    'posts'
]

7. Django Admin

Since we haven't setup our POST requests yet, we will be populating the database through django's admin panel.

To do that, create a superuser account admin with password 1234password.

$ python manage.py createsuperuser --email [email protected] --username admin

Password:
Password (again):
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

Register the model in the admin panel.

# posts/admin.py

from django.contrib import admin
from .models import Post

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

That's it. Visit the admin panel and update posts model's records. More on this.

8. Testing our API

$ python manage.py runserver
GET /api/v1/posts/
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

[
    {
        "title": "Example Post #1",
        "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "is_featured": false
    },
    {
        "title": "Example Post #2",
        "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "is_featured": true
    }
]

Great. Now it's time for us to update our views and finish the standard CRUD operations.

9. Adding more views

POST is a method used for creating (sometimes updating) a resource in the database.

# posts/api/views.py

from ..models import Post
from . import serializers
from rest_framework import generics, status
from rest_framework.response import Response

class PostCreateView(generics.CreateAPIView):
    queryset = Post.objects.all()
    serializer_class = serializers.PostSerializer

    def create(self, request, *args, **kwargs):
        super(PostCreateView, self).create(request, args, kwargs)
        response = {"status_code": status.HTTP_200_OK,
                    "message": "Successfully created",
                    "result": request.data}
        return Response(response)

Most often, we separate List and Create view classes when we want to expose a list of data set while easily preventing a certain request to POST or create a resource in the database for that specific List view.

Usecase always varies for apps, you are opt to use ListCreateAPIView or even ViewSets for combining the logic for a set of related views.

Optional: Since we want to display the data in a more systematic way, we override create method and map our inline custom response handler.

Adding more views with methods GET, PATCH, DELETE to handle a specific blog post detail.

class PostDetailView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = serializers.PostSerializer

    def retrieve(self, request, *args, **kwargs):
        super(PostDetailView, self).retrieve(request, args, kwargs)
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        data = serializer.data
        response = {"status_code": status.HTTP_200_OK,
                    "message": "Successfully retrieved",
                    "result": data}
        return Response(response)

    def patch(self, request, *args, **kwargs):
        super(PostDetailView, self).patch(request, args, kwargs)
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        data = serializer.data
        response = {"status_code": status.HTTP_200_OK,
                    "message": "Successfully updated",
                    "result": data}
        return Response(response)

    def delete(self, request, *args, **kwargs):
        super(PostDetailView, self).delete(request, args, kwargs)
        response = {"status_code": status.HTTP_200_OK,
                    "message": "Successfully deleted"}
        return Response(response)

10. Updating URLs

# posts/urls.py

from django.urls import path
from . import views
from .api import views

urlpatterns = [
    path('', views.PostListView.as_view(), name=None),
    path('create/', views.PostCreateView.as_view(), name=None),
    path('<int:pk>/', views.PostDetailView.as_view(), name=None)
]

Now you can send requests to your API via Postman, your app or do a GETrequests from your browser, examples:

POST /api/v1/posts/create/
HTTP 200 OK
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "status_code": 200,
    "message": "Successfully created",
    "result": {
        "csrfmiddlewaretoken": "rnSUN3XOIghnXA0yKghnQgxg0do39xhorYene5ALw3gWGThK5MjG6YjL8VUb7v2h",
        "title": "Creating a resource",
        "content": "Howdy mate!"
    }
}
GET /api/v1/posts/1/
HTTP 200 OK
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "status_code": 200,
    "message": "Successfully retrieved",
    "result": {
        "title": "Sample Post",
        "content": "Sample Post Content",
        "is_featured": false
    }
}

That's it. You have successfully managed to develop RESTful APIs with DRF! Cheers!

Source code

Available on GitHub.