[Part-1] Creating a Simple Chat App with DjangoRestFramework
utopian-io·@ajmaln·
0.000 HBD[Part-1] Creating a Simple Chat App with DjangoRestFramework
https://i.pinimg.com/originals/0b/0e/c9/0b0ec9e50c489913a27faa01b0c5f919.png [Source](https://in.pinterest.com/pin/539728336574255931) 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](http://www.django-rest-framework.org) 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. <p> In addition to DRF I will use the following Libraries for the specified purposes: - [MaterializeCSS](http://materializecss.com) - For designing the web UI - [Jquery](https://jquery.com) - For AJAX - Django Widget Tweaks - A useful module for handling forms easier in Django # Lets Begin... <p> ## 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. ```bash 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. ```bash mkdir ChatApp cd ChatApp ``` To create a virtualenv, just specify the name of the environement after the __virtualenv__ command in terminal like this: ```bash 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: ```bash 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: ```bash ./manage.py runserver ``` ...And check the address [127.0.0.1:8000](http://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 ```bash ./manage.py startapp chat ``` Now lets create a model for our messages: ```ChatApp/chat/models.py``` ```python 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 message - ```receiver``` is used to identify the receiver of the message - ```message``` is a CharField to store the text. - ```timestamp``` stores the date and time of creation of message - ```is_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``` ```python INSTALLED_APPS = [ . . 'widget_tweaks', 'rest_framework', 'chat' ] ``` Apply migrations: ```python ./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``` ```python 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``` ```python 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](https://docs.djangoproject.com/en/2.0/releases/2.0/) Edit the ```chat/urls.py``` to point our views. ```python 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](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``` ```python 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](https://github.com/ajmaln/DRF-Chat) Thank you. <br /><hr/><em>Posted on <a href="https://utopian.io/utopian-io/@ajmaln/part-1-creating-a-simple-chat-app-with-djangorestframework">Utopian.io - Rewarding Open Source Contributors</a></em><hr/>
👍 bobdos, jout, blickyer, qurator, ethandsmith, gregory-f, isteemit, oleg326756, scrooger, derosnec, nelinoeva, theleapingkoala, feedyourminnows, abmakko, mutitum, coffeedrinker51, oganenova, oleg12, curie, liberosist, ratticus, meerkat, outwalking, hendrikdegrote, anwenbaumeister, kingyus, webresultat, plojslydia, blogtrovert, jasimg, xanderslee, shawnvanderveer, thedrewshow, kushed, pharesim, xeroc, toxichan, steemedia, oscarcc89, sieses, socialspace, raci, cebymaster, pacokam8, jhagi.bhai, bp423, diggerdugg, cotidiana, tanyaschutte, zellious, playitforward, mrblinddraw, steem-id, blakemiles84, gomeravibz, dan-bn, phenom, neurallearner, archduck, ted7, bonjovis, superoo7, iamai, sujoydey, sammie5, abhildev, egetex, fidel733, vhutshilojuan, utopian-io, bobsthinking, codeworkr, jrswab, paracelso, roh831, ajmaln,