Python Django CRUD API with MySQL and Django Rest Framework

Python Django CRUD API with MySQL and Django Rest Framework

Python

Step-by-Step Tutorial: Creating a Python Django CRUD API with MySQL and Django Rest Framework;

Are you ready to dive into the world of building powerful REST APIs with Python Django and MySQL? In this comprehensive tutorial, we'll guide you through the process of setting up a Django project to connect with a MySQL database, defining data models, and performing CRUD operations using the Django Rest Framework.

Here's a sneak peek at what you'll learn:

  1. Setting up Django to Connect with MySQL Database: We'll walk you through the necessary steps to configure your Django project to establish a connection with a MySQL database. You'll learn how to seamlessly integrate the two technologies for efficient data management.

  2. Defining Data Models and Migrating to MySQL: Data models are the backbone of any application. We'll show you how to define your data models in Django and migrate them to the MySQL database. This ensures that your data is organized and stored properly.

  3. Processing HTTP Requests with Django Rest Framework: Django Rest Framework is a powerful tool for building RESTful APIs. You'll discover how to leverage its capabilities to handle HTTP requests, making your API robust and scalable.

  4. Performing CRUD Operations with MySQL: CRUD operations (Create, Retrieve, Update, Delete) are essential for any API. We'll guide you through the process of implementing these operations using MySQL as your database. You'll learn how to create, retrieve, update, and delete blog posts, as well as perform custom searches based on specific criteria.

By the end of this tutorial, you'll have a solid understanding of how to build a fully functional CRUD API using Python Django, MySQL, and Django Rest Framework. Get ready to take your web development skills to the next level!

So, grab your favorite beverage, buckle up, and let's embark on this exciting journey together. Get ready to unleash the power of Python Django and MySQL in creating robust REST APIs. Let's get started!

Introduction.

In this tutorial, we will show you how to create a Python 3/Django CRUD with MySQL example that uses Django Rest Framework for building Rest Apis.

Prerequisites

  • Python 3.6 or higher
  • Django 3.1 or higher
  • MySQL 8 or higher

Setup

    1. Installing Django: The first step is to install Django itself. Open your command prompt or terminal and run the following command:
    pip install django

    This will install Django and allow you to leverage its extensive features and functionalities.

    1. Installing Django Rest Framework: To create REST APIs, we need to install the Django Rest Framework. Run the following command in your command prompt or terminal:
    pip install djangorestframework

    The Django Rest Framework provides a comprehensive toolkit for building robust APIs quickly and efficiently.

    1. Installing Django CORS Headers: By default, Django projects have security measures in place that block requests from different domains. To disable this security feature and allow cross-origin requests, we'll install the Django CORS Headers module. Run the following command:
    pip install django-cors-headers

    This module will enable your Django project to handle requests from various domains seamlessly.

    1. Creating Your Django Project: Now it's time to create your Django project. Navigate to the desired folder where you want to create your project using the command prompt or terminal. Then, run the following command:
    django-admin startproject django_rest
    cd django_rest

    Replace django_rest with the desired name for your project. This command will generate the necessary files and folders to get your project up and running.

    1. Exploring Important Project Files: Let's take a closer look at some of the important files in your newly created Django project. These files play a crucial role in defining the structure and functionality of your application. Here are a few key files you should be familiar with:
    • manage.py: This file serves as the entry point for executing various Django management commands. You'll use this file to perform tasks like running the development server, applying migrations, and more.

    • settings.py: The settings.py file contains all the configuration settings for your Django project. This is where you define your database connection, installed apps, middleware, and other project-specific settings.

    • urls.py: The urls.py file defines the URL patterns for your project. You'll specify which views should handle different URLs and map them accordingly.

    By understanding these files and their roles within your Django project, you'll have a solid foundation for building your REST APIs.

    Now that you have the necessary modules installed and a basic understanding of the important project files, you're ready to embark on your Django journey. Stay tuned for the next steps as we dive deeper into creating powerful REST APIs with Django.

To see your Django project in action, follow these simple steps:

python manage.py runserver

The app is now running in the port 8080


Lets copy  the url and open in the browser.

What you see on the screen is the default template that comes with every Django project.

Create an App 

To create a new app for your Django project, use the following command:

python manage.py startapp BlogPost

Add The following in the Settings.py File

INSTALLED_APPS = [
    ........,
    'rest_framework',
    'corsheaders',
    'BlogPost.app.BlogpostConfig'

]

CORS_ORIGIN_ALLOW_ALL =True

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ......
]

Note: In production, it's recommended to whitelist specific domains instead of allowing all.

Creating Models

In BlogPost/models.py, we start by creating the User model to store the user details.

from django.db import models

# Create your models here.
class Blog(models.Model):
    id = models.AutoField(primary_key=True)
    slug = models.SlugField(max_length=255,unique=True)
    blogtext = models.TextField()
    blogtitle = models.CharField(max_length=255)
    blogtopic = models.CharField(max_length=255)
    blogauthor = models.CharField(max_length=255)
    blogdate = models.DateTimeField()
    bloglikes = models.IntegerField(default=0)
    blogdislikes = models.IntegerField(default=0)
    blogimage = models.TextField(blank=True, null=True)  # Add the blogimage field

    class Meta:
        ordering = ['-blogdate']

    def __str__(self):
        return self.blogtitle
   

class Comments(models.Model):
    comment_id = models.AutoField(primary_key=True)
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    comment_text = models.TextField()
    comment_author = models.CharField(max_length=255)
    comment_date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.comment_author} on {self.blog.blogtitle}"


Setting Up the Database

We are going to use the MySQL.

Create a Database in Your MySQL

create database my_blog_post; 

To connect to MySQL from your Python Django app, you need to install the database adapter called pymysql. Here's how you can do it:

  1. Open your command prompt or terminal.

  2. Type the following command and press Enter to install pymysql:

    pip install pymysql

    This will download and install the pymysql package in your Python environment.

  3. Once pymysql is installed, you need to add the database details in your Django project's settings.py file. Open the settings.py file in your text editor.

  4. Look for the DATABASES setting in the settings.py file. It should be a dictionary containing the database configuration.

  5. Update the DATABASES dictionary with the following details for MySQL:

    import pymysql
    pymysql.install_as_MySQLdb()
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'my_blog_post',
            'USER':'<your_mysql_username>',
            'PASSWORD':'<your_mysql_password>',
            'HOST':'<your_mysql_host>',
            'PORT':'<your_mysql_port>'
        }
    }

    Replace <your_mysql_username>, <your_mysql_password>, <your_mysql_host>, and <your_mysql_port> with your actual MySQL database information.

  6. Save the settings.py file.

Now your Python Django app is configured to connect to MySQL using the pymysql database adapter. Make sure you have a MySQL server running with the specified database details.

Migrations

Migrations provide a way of updating your database schema every time your models change, without losing data.

Create an initial migration for our Blog and Comment model, and sync the database for the first time

python manage.py makemigrations BlogPost

Executing this command will generate a migration file that describes the changes to be made in the database based on the changes in your models. This migration file acts as a blueprint for the database schema changes.

You can then apply these migrations to the database using the migrate command:

python manage.py migrate

This will actually apply the changes defined in the migration file to the database.

Serializers

To create serializers for your Django models, you can define a serializers.py file in your app directory.

from rest_framework import serializers
from BlogPost.models import Blog,Comments

class BlogSerializer(serializers.ModelSerializer):
    blogimage = serializers.CharField(allow_blank=True, required=False)

    class Meta:
        model = Blog
        fields = ['id', 'slug', 'blogtext', 'blogtitle', 'blogtopic', 'blogauthor', 'blogdate', 'bloglikes', 'blogdislikes', 'blogimage']


class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comments
        fields = ['comment_id', 'blog', 'comment_text', 'comment_author', 'comment_date']

CreateView

This code represents a collection of views in a Django app that handle various API endpoints related to a blogging platform. Let's go through each view and its functionality:

  1. UsersAPIView: This view is a GET request that returns the authenticated user's information. It requires authentication and permission to access.

  2. blogApiGet: This view is a GET request that returns all the blog posts available in the database. It uses the BlogSerializer to serialize the blog posts' data.

  3. blogApiPost: This view is a POST request used to create a new blog post. It expects the blog post data in JSON format and validates it using the BlogSerializer. If the data is valid, the blog post is saved, and a success message is returned. If there are validation errors or the data is invalid, an error message is returned.

  4. blogDetailApi: This view handles the GET, PUT, and DELETE requests for a specific blog post identified by its slug. If a GET request is made, it returns the serialized data of the requested blog post. For a PUT request, it updates the blog post with the provided data. For a DELETE request, it deletes the blog post from the database.

  5. blogLikeApi: This view handles the PUT request to update the likes and dislikes count of a blog post. The blog post is identified by its ID. The request data should contain the updated counts, and the blog post is updated accordingly.

  6. add_comment: This view handles the POST request to add a comment to a specific blog post identified by its slug. The comment data is expected in JSON format and is validated using the CommentSerializer. If the data is valid, the comment is saved, and a success message is returned.

  7. get_comments: This view handles the GET request to retrieve all the comments for a specific blog post identified by its slug. The comments are filtered based on the blog post and serialized using the CommentSerializer.

  8. SaveFile: This view handles the file upload functionality. It expects a file to be included in the request's FILES data with the key "file". The file is saved using Django's default storage, and a success message with the saved file's name is returned.

Note that some decorators are used in the code, such as csrf_exempt, api_view, permission_classes, and authentication_classes, to handle authentication, permissions, and API functionality.

Remember to import the necessary modules and models, such as logging, render, get_object_or_404, JSONParser, Response, and the appropriate serializers and models from your own app.

Ensure that you have a clear understanding of the purpose and functionality of each view before implementing them in your Django project.

import logging
from django.shortcuts import render, get_object_or_404
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import IsAuthenticated
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rest_framework.exceptions import ValidationError


from django.http.response import JsonResponse
from rest_framework.decorators import (
    authentication_classes,
    permission_classes,
    api_view,
)

from BlogPost.models import Blog, Comments
from BlogPost.serializers import BlogSerializer, CommentSerializer
from django.core.files.storage import default_storage


logger = logging.getLogger(__name__)


@csrf_exempt
@api_view(["GET"])
@permission_classes([IsAuthenticated])
@authentication_classes([BasicAuthentication])
def UsersAPIView(request):
    try:
        return JsonResponse({"status": "success", "message": str(request.user)}, status=200)
    except ValidationError as e:
        return JsonResponse({"status": "error", "message": str(e)}, status=200  )



@csrf_exempt
@api_view(["GET"])
def blogApiGet(request, id=0):
    blogs = Blog.objects.all()
    blog_serializer = BlogSerializer(blogs, many=True)
    logger.info("GET request received for blogs")
    return JsonResponse({"status": "success", "data": blog_serializer.data}, status=200)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
@authentication_classes([BasicAuthentication])
def blogApiPost(request, id=0):
    blog_data = JSONParser().parse(request)
    blog_serializer = BlogSerializer(data=blog_data)
    if blog_serializer.is_valid():
        try:
            blog_serializer.save()
            logger.info("POST request received for adding a blog")
            return JsonResponse(
                {"status": "success", "message": "Added Successfully!"}, status=201
            )
        except ValidationError as e:
            logger.error("Blog already exists")
            return Response({"status": "error", "message": e.args[0]}, status=400)
    logger.error("Invalid data received in POST request for adding a blog")
    return Response({"status": "error", "message": "Invalid data."}, status=400)


@csrf_exempt
def blogDetailApi(request, slug):
    try:
        blog = Blog.objects.get(slug=slug)
    except Blog.DoesNotExist:
        return JsonResponse(
            {"status": "error", "message": "Blog does not exist."}, safe=False
        )

    if request.method == "GET":
        blog_serializer = BlogSerializer(blog)
        return JsonResponse(
            {"status": "success", "data": blog_serializer.data}, status=200
        )
    elif request.method == "PUT":
        blog_data = JSONParser().parse(request)
        blog_serializer = BlogSerializer(blog, data=blog_data)
        if blog_serializer.is_valid():
            blog_serializer.save()
            return JsonResponse(
                {"status": "success", "message": "Updated Successfully!"}, status=200
            )
        return JsonResponse(
            {"status": "error", "message": "Failed to Update."}, status=400
        )
    elif request.method == "DELETE":
        blog.delete()
        return JsonResponse(
            {"status": "success", "message": "Deleted Successfully!"}, status=200
        )


@csrf_exempt
def blogLikeApi(request, id):
    try:
        blog = Blog.objects.get(id=id)
    except Blog.DoesNotExist:
        return JsonResponse(
            {"status": "error", "message": "Blog does not exist."}, safe=False
        )

    if request.method == "PUT":
        blog_data = JSONParser().parse(request)
        if "bloglikes" in blog_data:
            blog.bloglikes += blog_data["bloglikes"]
        if "blogdislikes" in blog_data:
            blog.blogdislikes += blog_data["blogdislikes"]
        blog.save()
        return JsonResponse(
            {"status": "success", "message": "Updated Successfully!"}, status=200
        )
    else:
        return JsonResponse(
            {"status": "error", "message": "Failed to Update."}, status=400
        )


@csrf_exempt
def add_comment(request, slug):
    try:
        blog = Blog.objects.get(slug=slug)
    except Blog.DoesNotExist:
        return JsonResponse(
            {"status": "error", "message": "Blog does not exist."}, safe=False
        )

    if request.method == "POST":
        comment_data = JSONParser().parse(request)
        comment_data["blog"] = blog.id
        comment_serializer = CommentSerializer(data=comment_data)
        if comment_serializer.is_valid():
            comment_serializer.save()
            return JsonResponse(
                {"status": "success", "message": "Comment added successfully!"}
            )
        return JsonResponse(comment_serializer.errors, status=400)


@csrf_exempt
def get_comments(request, slug):
    try:
        blog = Blog.objects.get(slug=slug)
    except Blog.DoesNotExist:
        return JsonResponse(
            {"status": "error", "message": "Blog does not exist."}, safe=False
        )

    if request.method == "GET":
        comments = Comments.objects.filter(blog=blog)
        comment_serializer = CommentSerializer(comments, many=True)
        return JsonResponse({"status": "success", "data": comment_serializer.data})
    else:
        return JsonResponse(
            {"status": "error", "message": "Invalid request method."}, status=400
        )


@csrf_exempt
def SaveFile(request):
    file = request.FILES["file"]
    file_name = default_storage.save(file.name, file)
    return JsonResponse(
        {
            "status": "success",
            "message": "File added successfully!",
            "file_name": file_name,
        },
        safe=False,
    )


Configuring URLs

Create a file BlogPost/urls.py and add the URL to match the view we created. Also add the following code.

from django.urls import path
from BlogPost import views

from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
    path('blog/', views.blogApiGet),
     path('addblog/', views.blogApiPost),
    path('blog/<slug:slug>/', views.blogDetailApi),
    path('blog/<int:id>/like/', views.blogLikeApi),
    path('blogs/<slug:slug>/comments/', views.add_comment),
    path('blogs/<slug:slug>/comment/', views.get_comments),
    path('savefile/', views.SaveFile),
     path('getuser/', views.UsersAPIView),
]+static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)



We also need to import URLs from the users application to the main 
django_rest/urls.py file. So go ahead and do that. We are using the include function here, so don't forget to import it.

from django.contrib import admin
from django.urls import path,include
from BlogPost import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('BlogPost.urls')),
]


API Testing

To see your Django project in action

python manage.py runserver

The app is now running in the port 8080

Now that we are done creating the endpoint, let's do a test and see if we are on track. We will use Postman to do the tests. If you are not familiar with Postman, it's a tool which presents a friendly GUI for constructing requests and reading responses.

  1. GET /api/blog/

    • Description: Retrieve all blog posts.
    • Method: GET
    • Request: No request body required.
    • Response:
      • Status Code: 200 (OK)
      • Body (JSON):
        { "status": "success", "data": [ { "id": 1, "slug": "first-blog-post", "blogtext": "This is the first blog post.", "blogtitle": "First Blog", "blogtopic": "Technology", "blogauthor": "John Doe", "blogdate": "2023-06-10T10:30:00Z", "bloglikes": 10, "blogdislikes": 2, "blogimage": "" }, { "id": 2, "slug": "second-blog-post", "blogtext": "This is the second blog post.", "blogtitle": "Second Blog", "blogtopic": "Travel", "blogauthor": "Jane Smith", "blogdate": "2023-06-09T15:45:00Z", "bloglikes": 5, "blogdislikes": 0, "blogimage": "image.jpg" } ] }
  2. POST /api/addblog/

    • Description: Add a new blog post.
    • Method: POST
    • Request (JSON):
      { "blogtext": "This is a new blog post.", "blogtitle": "New Blog", "blogtopic": "Fashion", "blogauthor": "Emily Johnson", "blogdate": "2023-06-11T09:15:00Z", "bloglikes": 0, "blogdislikes": 0, "blogimage": "" }
    • Response:
      • Status Code: 201 (Created)
      • Body (JSON):
        { "status": "success", "message": "Added Successfully!" }
  3. GET /api/blog/{slug}/

    • Description: Retrieve a specific blog post by its slug.
    • Method: GET
    • Response:
      • Status Code: 200 (OK)
      • Body (JSON):
        { "status": "success", "data": { "id": 1, "slug": "first-blog-post", "blogtext": "This is the first blog post.", "blogtitle": "First Blog", "blogtopic": "Technology", "blogauthor": "John Doe", "blogdate": "2023-06-10T10:30:00Z", "bloglikes": 10, "blogdislikes": 2, "blogimage": "" } }
  4. PUT /api/blog/{id}/like/

    • Description: Update the like count of a blog post.
    • Method: PUT
    • Request (JSON):
      { "bloglikes": 1 }
    • Response:
      • Status Code: 200 (OK)
      • Body (JSON):
        { "status": "success", "message": "Updated Successfully!" }
  5. POST /api/blogs/{slug}/comments/

    • Description: Add a comment to a specific blog post.
    • Method: POST
    • Request (JSON):
      { "comment_text": "This is a comment.", "comment_author": "Alice Smith" }
    • Response:
      • Status Code: 200 (OK)
      • Body (JSON):
        { "status": "success", "message": "Comment added successfully!" }
  6. GET /api/blogs/{slug}/comment/

    • Description: Retrieve all comments for a specific blog post.
    • Method: GET
    • Response:
      • Status Code: 200 (OK)
      • Body (JSON):
        { "status": "success", "data": [ { "comment_id": 1, "blog": 1, "comment_text": "This is a comment.", "comment_author": "Alice Smith", "comment_date": "2023-06-11T11:30:00Z" }, { "comment_id": 2, "blog": 1, "comment_text": "Another comment.", "comment_author": "Bob Johnson", "comment_date": "2023-06-11T12:15:00Z" } ] }
  7. POST /api/savefile/

    • Description: Upload a file.
    • Method: POST
    • Request (Form Data):
      { "file": <file_data> }
    • Response:
      • Status Code: 200 (OK)
      • Body (JSON):
        { "status": "success", "message": "File added successfully!", "file_name": "image.jpg" }
  8. GET /api/getuser/

    • Description: Retrieve the authenticated user information.
    • Method: GET
    • Response:
      • Status Code: 200 (OK)
      • Body (JSON):
        { "status": "success", "message": "John Doe" }

Conclusion

In this blog, we have explored the exciting world of the Django BlogPost app and its API endpoints. We've seen how to interact with the app to perform various actions, such as retrieving blog posts, adding new blogs, updating likes, adding comments, and even uploading files.


Github Link: Python Django CRUD API with MySQL and Django Rest Framework

Python Django CRUD API with MySQL and Django Rest Framework

“A hero is one who knows how to hang on for one minute longer.” Norwegian Proverb

June 19, 2023

0
0

Comments

Ferris Noel 1 year ago

Good Work!

+

© 2024 Inc. All rights reserved. mulikevs