Source
In this tutorial, I would like to explain how to create a very simple Live Chat application with the help of Django Rest Framework & AJAX.
Django Rest Framework is a very popular framework in django. It's a good idea to learn DRF if you are onto backend development with Django. DRF makes creating RESTful APIs easy as pie!
Note: The reader is expected to have some idea on basic concepts of django like Models, Views etc. I am using the new Django 2.0 in this tutorial.
In addition to DRF I will use the following Libraries for the specified purposes:
- MaterializeCSS - For designing the web UI
- Jquery - For AJAX
- Django Widget Tweaks - A useful module for handling forms easier in Django
Lets Begin...
1. Directory and environment setup
It is a good practice to use a virtual environemnt while you are creating a new project. Helps you track the requirements for the project and also the global version of packages are kept unaffected.
To create a virtual environemnt we use virtualenv. If it is not already installed in your PC then just installl itusing pip.
pip install virtualenv
Before creating the virtualenv lets create a directory for our project. For that first of all, lets put a name for our Application. Let it be ChatApp (I know it could have been better. But nothing creative comes into my mind now! Lol)
Create a directory and cd into it.
mkdir ChatApp
cd ChatApp
To create a virtualenv, just specify the name of the environement after the virtualenv command in terminal like this:
virtualenv chat-env
Activate the environment and install required python packages
source chat-env/bin/activate
pip intsall django djangorestframework django-widget-tweaks
After everything is successfully installed, to create a project just open a terminal in a directory and type the following command:
django-admin startproject ChatApp .
Mind the '.' after ..ChatApp, it is included because we dont want it to create a new directory. So the above command creates a directory ChatApp with some files and a file manage.py, required for a basic django application. If everything went well, your directory structure will look like this:
ChatApp
├── ChatApp
├── chat-env
├── manage.py
To check everything is working, just type to start the development server:
./manage.py runserver
...And check the address 127.0.0.1:8000 in a browser. It will show something like this:
2. App Setup
You are expected to know that in every independent module in django is considered as an app. So we need to create an app for Chat.
To create 'chat' app
./manage.py startapp chat
Now lets create a model for our messages:
ChatApp/chat/models.py
from django.contrib.auth.models import User
from django.db import models
class Message(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sender')
receiver = models.ForeignKey(User, on_delete=models.CASCADE, related_name='receiver')
message = models.CharField(max_length=1200)
timestamp = models.DateTimeField(auto_now_add=True)
is_read = models.BooleanFeild(default=False)
def __str__(self):
return self.message
class Meta:
ordering = ('timestamp',)
sender
is used to identify the sender of the messagereceiver
is used to identify the receiver of the messagemessage
is a CharField to store the text.timestamp
stores the date and time of creation of messageis_read
keeps track if the message is read or not.
Ordering is set such that it follows the timestamp. ie. The latest message will be the last one on the list.
We make use of the User model which is builtin model in Django, for managing users. We need just the username field and password from it, for our basic application.
So that's all the tables we need. Now lets migrate our changes to the database. But before that we need to add the apps to INSTALLED_APPS
list in ChatApp/settings.py
INSTALLED_APPS = [
.
.
'widget_tweaks',
'rest_framework',
'chat'
]
Apply migrations:
./manage.py makemigrations
./manage.py migrate
Now lets make the serializers for the models.
Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
ChatApp/chat/serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
from chat.models import Message
#
# User Serializer
class UserSerializer(serializers.ModelSerializer):
"""For Serializing User"""
password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ['username', 'password']
#
# Message Serializer
class MessageSerializer(serializers.ModelSerializer):
"""For Serializing Message"""
sender = serializers.SlugRelatedField(many=False, slug_field='username', queryset=User.objects.all())
receiver = serializers.SlugRelatedField(many=False, slug_field='username', queryset=User.objects.all())
class Meta:
model = Message
fields = ['sender', 'receiver', 'message', 'timestamp']
Let's see one by one....
We import the serializers
from rest_framework to make use of the serializer classes.
The ModelSerializer
class provides a shortcut that lets you automatically create a Serializer class with fields that correspond to the Model fields.
In UserSerializer, password
is specified write_only to avoid gettting the password displayed on GET request. We only need it on POST request in case of creation of a new user.
The sender and receiver of Message is serialized as SlugRelatedField
to represent the target of the relationship using a field on the target. The field is specified as slug_field. It also takes a 'many' argument which identifies the relation is many-to-many or not. We gave it false, since a message can only contain a single sender and receiver.
The 'queryset' argument takes the list of objects from which the related object is to be chosen.
Associating Views to serializers
Now lets create some views to return our serialized data.
User View
ChatApp/chat/views.py
from django.contrib.auth.models import User # Django Build in User Model
from django.http.response import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from chat.models import Message # Our Message model
from chat.serializers import MessageSerializer, UserSerializer # Our Serializer Classes
# Users View
@csrf_exempt # Decorator to make the view csrf excempt.
def user_list(request, pk=None):
"""
List all required messages, or create a new message.
"""
if request.method == 'GET':
if pk: # If PrimaryKey (id) of the user is specified in the url
users = User.objects.filter(id=pk) # Select only that particular user
else:
users = User.objects.all() # Else get all user list
serializer = UserSerializer(users, many=True, context={'request': request})
return JsonResponse(serializer.data, safe=False) # Return serialized data
elif request.method == 'POST':
data = JSONParser().parse(request) # On POST, parse the request object to obtain the data in json
serializer = UserSerializer(data=data) # Seraialize the data
if serializer.is_valid():
serializer.save() # Save it if valid
return JsonResponse(serializer.data, status=201) # Return back the data on success
return JsonResponse(serializer.errors, status=400) # Return back the errors if not valid
Message View
ChatApp/chat/views.py
@csrf_exempt
def message_list(request, sender=None, receiver=None):
"""
List all required messages, or create a new message.
"""
if request.method == 'GET':
messages = Message.objects.filter(sender_id=sender, receiver_id=receiver)
serializer = MessageSerializer(messages, many=True, context={'request': request})
return JsonResponse(serializer.data, safe=False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = MessageSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
The above view is pretty same as the User view, except it requires sender and receiver as URL parameters. The sender and receiver expects the id of the users, associated with the message.
We can now, associate urls with these views. So lets create urls.py file inside our chat
directory and include it in the main urls.py in the ChatApp/ChatApp
directory. To incude the new chat/urls.py
just add a new line to the urlpatterns list in ChatApp/urls.py
urlpatterns = [
...
path('', include('chat.urls')),
]
One of the new features in Django 2.0 is the introduction of path() function, which is a substitute for the old url() function. The main advantage is that we can specify URL arguments without any complex regular expressions. You can read more about it here : Django 2.0 Release Notes
Edit the chat/urls.py
to point our views.
from django.urls import path
from . import views
urlpatterns = [
# URL form : "/api/messages/1/2"
path('api/messages/<int:sender>/<int:receiver>', views.message_list, name='message-detail'), # For GET request.
# URL form : "/api/messages/"
path('api/messages/', views.message_list, name='message-list'), # For POST
# URL form "/api/users/1"
path('api/users/<int:pk>', views.user_list, name='user-detail'), # GET request for user with id
path('api/users/', views.user_list, name='user-list'), # POST for new user and GET for all users list
]
Now create a super user for our site.
./manage.py createsuperuser
Give required details and create the superuser. Now you can login to admin panel through : http://127.0.0.1:8000/admin after running the development server.
To make the Messages to show up in the admin panel. Add register the model in
chat/admin.py
from django.contrib import admin
from chat.models import Message
admin.site.register(Message)
Now the Messages will show up in the admin panel. Try creating some message with the same user as sender and receiver.
You can check the working of API using curl after creating some messages, like this.
curl http://127.0.0.1:8000/api/messages/1/1
Will return something like this (Here I have created 3 messages with same sender and receiver):
[{"sender": "ajmal", "receiver": "ajmal", "message": "Hai", "timestamp": "2017-12-16T14:38:03.096207Z"}, {"sender": "ajmal", "receiver": "ajmal", "message": "Hai", "timestamp": "2017-12-16T16:01:46.433918Z"}, {"sender": "ajmal", "receiver": "ajmal", "message": "New message", "timestamp": "2017-12-18T15:49:30.333146Z"}]
To get the list of users
curl http:127.0.0.1:8000/users/
Will return the list of users like this :
[{"username": "ajmal"}]
So that's all for now. We have setup already the 90% of the backend required for our ChatApp. We shall learn to build a front end structure for our app in the next part. I have added the above code in Github. You can check it out here : Github DRF-Chat
Thank you.
Posted on Utopian.io - Rewarding Open Source Contributors
Very informative. Your explanation is clear and concise. What any programmer looking for a solution wants.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Thank you :-)
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Hey @ajmaln I am @utopian-io. I have just upvoted you!
Achievements
Suggestions
Get Noticed!
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Nice, in-depth tutorial! More web development tutorials!
I just wish Steemit had a nicer way to format code in blog posts.
Any chance of such feature being added?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Exactly! I have requested the devs to add it soon. It is a very necessary feature.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Oh, that's terrific news! Thanks for making that request. ;)
Perhaps later we'd also have syntax highlighting for different languages which would be amazing and would make tutorials like these stand out and be more easier to navigate/follow.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
This post has received gratitude of 1.00 % from @jout
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Thank You!! This is a perfect Django challenge for me(beginner ).
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Whoa! Great job bro.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
The online status doesn't work, I tried to fix it but failed. You should perhaps review it.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit