Python Django CRUD API with MySQL and Django Rest Framework
PythonStep-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:
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.
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.
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.
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
- 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.
- 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.
- 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.
- 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_restReplace
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.- 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
: Thesettings.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
: Theurls.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 =TrueMIDDLEWARE = ['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 fieldclass Meta:ordering = ['-blogdate']def __str__(self):return self.blogtitleclass 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:
Open your command prompt or terminal.
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.Once
pymysql
is installed, you need to add the database details in your Django project'ssettings.py
file. Open thesettings.py
file in your text editor.Look for the
DATABASES
setting in thesettings.py
file. It should be a dictionary containing the database configuration.Update the
DATABASES
dictionary with the following details for MySQL:import pymysqlpymysql.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.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 serializersfrom BlogPost.models import Blog,Commentsclass BlogSerializer(serializers.ModelSerializer):blogimage = serializers.CharField(allow_blank=True, required=False)class Meta:model = Blogfields = ['id', 'slug', 'blogtext', 'blogtitle', 'blogtopic', 'blogauthor', 'blogdate', 'bloglikes', 'blogdislikes', 'blogimage']class CommentSerializer(serializers.ModelSerializer):class Meta:model = Commentsfields = ['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:
UsersAPIView
: This view is a GET request that returns the authenticated user's information. It requires authentication and permission to access.blogApiGet
: This view is a GET request that returns all the blog posts available in the database. It uses theBlogSerializer
to serialize the blog posts' data.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 theBlogSerializer
. 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.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.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.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 theCommentSerializer
. If the data is valid, the comment is saved, and a success message is returned.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 theCommentSerializer
.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 loggingfrom django.shortcuts import render, get_object_or_404from rest_framework.authentication import SessionAuthentication, BasicAuthenticationfrom rest_framework.exceptions import AuthenticationFailedfrom rest_framework.permissions import IsAuthenticatedfrom django.views.decorators.csrf import csrf_exemptfrom rest_framework.parsers import JSONParserfrom rest_framework.response import Responsefrom rest_framework.exceptions import ValidationErrorfrom django.http.response import JsonResponsefrom rest_framework.decorators import (authentication_classes,permission_classes,api_view,)from BlogPost.models import Blog, Commentsfrom BlogPost.serializers import BlogSerializer, CommentSerializerfrom django.core.files.storage import default_storagelogger = 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_exemptdef 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_exemptdef 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_exemptdef 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.idcomment_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_exemptdef 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_exemptdef 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 pathfrom BlogPost import viewsfrom django.conf.urls.static import staticfrom django.conf import settingsurlpatterns = [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 adminfrom django.urls import path,includefrom BlogPost import viewsurlpatterns = [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.
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" } ] }
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!" }
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": "" } }
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!" }
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!" }
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" } ] }
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" }
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
June 19, 2023
Comments
Good Work!