Uploading file using api in django rest framework

Uploading files to the server is one of the common tasks nowadays. In this tutorial, we will see how can we upload the file using the Django-rest framework.

First, let’s create a virtual environment for our Django project.

$ virtual env

Activate the virtual environment.

$ source env/bin/activate

install django and django-restframework

(env) $ pip install django==1.11.8
(env) $ pip install djangorestframework

Start a new django project with the name ‘fileupload’

(env) $ django-admin startproject fileupload

Move into the new project directory and create a new app called ‘file_app’

(env) $ cd fileupload
(env) $ python manage.py startapp file_app

So, we have created our project and also an app. Let’s set up our project for the main part.

Update ‘INSTALLED_APPS’ in settings.py file:

INSTALLED_APPS = [
- - - - - - - -
- - - - - - - -
- - - - - - - -
'rest_framework',
'file_app',
]

In settings.py add the path to the directory where you would want to store the uploaded files. Add the below line at the end of settings.py file:

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

Add the media url and the ‘file_app’ urls.py in the project’s urls.py file. The final urls.py will be:

from django.conf.urls import url, include
from django.contrib import admin
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
  url(r'^admin/', admin.site.urls),
  url(r'^file/', include('file_app.urls')),
]

if settings.DEBUG:
  urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Now, everything is set up. Let’s start coding.
We will start by creating models for our app.

in file_app/models.py

from django.db import models

class File(models.Model):

  file = models.FileField(blank=False, null=False)
  remark = models.CharField(max_length=20)
  timestamp = models.DateTimeField(auto_now_add=True)

Create a new file as ‘serializers.py’ in-app directory (i.e. in ‘file_app’) and add the following code there:

from rest_framework import serializers

from .models import File

class FileSerializer(serializers.ModelSerializer):

  class Meta():
    model = File
    fields = ('file', 'remark', 'timestamp')

Let’s edit the views.py and add the following code there:

from rest_framework.views import APIView
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework import status

from .serializers import FileSerializer

class FileView(APIView):

  parser_classes = (MultiPartParser, FormParser)

  def post(self, request, *args, **kwargs):

    file_serializer = FileSerializer(data=request.data)
    if file_serializer.is_valid():
      file_serializer.save()
      return Response(file_serializer.data, status=status.HTTP_201_CREATED)
    else:
      return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Now, let’s create the file_app/urls.py file and add the following code:

from django.conf.urls import url

from .views import FileView

urlpatterns = [
  url(r'^upload/$', FileView.as_view(), name='file-upload'),
]

 

That’s it. Now you can upload the file from frontend using this api.
Let’s try this out.

Prepare the database for the test:

(env) $ python manage.py makemigrations
(env) $ python manage.py migrate

And run the server

(env) $ python manage.py runserver

Open Postman app:

postman

enter the url in url field and set the method to POST which is at the left of the url field. Go to Body section and make sure that ‘form-data’ is checked.

In the key field write ‘file’, which should match the name of your model field name. In this case it is ‘file’. So, I am writing ‘file’. Then from drop-down select ‘File’ instead of ‘Text’. In value field browse the file and select it.

Repeat the process for remark section. Check it from image below:

postman-filled

Now click send, your file will be uploaded and you will get file data in response:

postman-success

 

That’s it.

How to host Django application and LEMP site on the same server using Nginx and Gunicorn on Ubuntu 16.04

You can host django application and PHP site on the same VPS. This is easy to set up and you don’t need to be an expert to do this.

Prerequisite:
* At least one domain name (i.e. example.com)

You must have a domain in order to host both django and php site. You cannot do this with just ip address.

Suppose you have one domain i.e., ‘example.com’, then for the django app we will use ‘example.com’ and ‘www.example.com’. And for php site we will use ‘php.example.com’.
To do this your domain’s ‘A’ record must be pointing towards the IP address of your server and in the host field you must have ‘*’. As shown below:

a_record_godaddy

Ok, let’s do it!

First we will start with Django site

Install required packages:

$ sudo apt-get update

for python2:

$ sudo apt-get install python-pip python-dev libpq-dev postgresql postgresql-contrib nginx gunicorn

for python3:

$ sudo apt-get install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx gunicorn

Install python virtual environment:

for python2:

$ sudo -H pip install --upgrade pip
$ sudo -H pip install virtualenv

for python3:

$ sudo -H pip3 install --upgrade pip
$ sudo -H pip3 install virtualenv

Set up Postgresql Database and User

You can skip this step if you are using sqlite or some other DBMS system.

Log into the postgresql interactive session in order to create new database and user.

$ sudo -u postgres psql

Now create database for your project:

postgres=# CREATE DATABASE project;

Now create new database user:

postgres=# CREATE USER projectuser WITH PASSWORD 'password';

Grant the user access to his database:

postgres=# GRANT ALL PRIVILEGES ON DATABASE project TO projectuser;

Now quit from PostgreSQL session

postgres=# \q

Initialize django project

Now its time we initialize our django projects and set it up.

First go to home directory and make directory for our django project.

$ cd ~
$ mkdir project
$ cd project

Now we will initialize virtual environment and install required python packages:

$ virtualenv venv
$ source venv/bin/activate
(venv) $ pip install django gunicorn psycopg2

Now, we will create our project and set it up.

(venv) $ django-admin startproject project .

Don’t forget to type the period at last, it will tell django to create project at the current location.

By now your directory structure would be like:

project/
|
| ---project/
    |---__init__.py
    |---settings.py
    |---urls.py
    |---wsgi.py
|---venv/
|---manage.py

Now we will edit our settings.py to add ALLOWED_HOSTS and DATABASE credentials:

(venv) $ nano project/settings.py

Add your domain or sub-domain or IP address to the allowed host:

ALLOWED_HOSTS = ['your_server_domain_or_IP', ]

and your database credentials (not required if you are using sqlite):

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'project',
'USER': 'projectuser',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
  }
}

and set your static root directory:

STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

Now migrate the database:

(venv) $ python manage.py makemigrations
(venv) $ python manage.py migrate

collect all the static files into root directory by typing:

(venv) $ python manage.py collectstatic

We will now test the server by running it:

(venv) $ python manage.py runserver 0.0.0.0:8000

Go to http://domain_or_ip:8000 you will see default django index page or your project’s index page.

Note:

If you are unable to see the page and if your firewall is enabled then you have to run following commands:
$ sudo ufw allow 8000
$ sudo ufw allow ‘Nginx Full’

Moving forward we will test our site with gunicorn. To do so run the below command:

(venv) $ gunicorn --bind 0.0.0.0:8000 project.wsgi

Replace the project.wsgi with your project’s name or with the name of the folder which contains wsgi.py.
You must see the same page as before, however without css and js as gunicorn does not have their location.

Deactivate the virtual environment:

(venv) $ deactivate

Now, we will create gunicorn systemd service file to start the application server.

$ sudo nano /etc/systemd/system/gunicorn.service

In gunicorn service file we will specify the dependencies of the django application, so that gunicorn would be able to execute them correctly.
Copy the code snippet below and paste it on the service file. Change the path relative to your django app.

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=user
Group=www-data
WorkingDirectory=/home/user/project # location of directory which contains your manage.py file (remove this comment)
ExecStart=/home/user/project/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/user/project/project.sock project.wsgi:application

[Install]
WantedBy=multi-user.target

Replace user with the name of the user you logged in as (Note: it should not be root). Be careful with the path you have provided. Make sure that the user you are using must be able to access that path with using ‘sudo’.

Now we will start gunicorn service:

$ sudo systemctl start gunicorn
$ sudo systemctl enable gunicorn

Print the list of content on the project directory.

$ ls /home/user/project

There must be a socket file with the name of your project i.e., project.sock, if it is not there then restart the gunicorn.

$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn

Check for socket file again, if the problem persist, check the status of gunicorn

$ sudo systemctl status gunicorn

If gunicorn is not running then check the error log with:

$ sudo journalctl -u gunicorn

These type of errors occur because of:
* the wrong path provided in the gunicorn service file,
* user does not have permission to access the project directory
* gunicorn is not installed correctly

After changing the gunicorn system file, always run the below command and check for socket file:

$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn

Configure Nginx to serve django app

First create a server block for our django app

$ sudo nano /etc/nginx/sites-available/project

In the server block paste the following code and change it according to your project:

server {
listen 80;
server_name example.com www.example.com; #replace with your domain

location =  /favicon.ico { access_log off; log_not_found off; }

location /static/ { #if you are using amazon s3, then skip this block
root /home/user/project;
}

location / {
include proxy_params;
proxy_pass http://unix:/home/user/project/project.sock; #path to your socket file
}
}

Now enable the file by linking it to sites-enabled directory:

$ sudo ln -s /etc/nginx/sites-available/project /etc/nginx/sites-enabled

Test the nginx configuration:

$ sudo nginx -t

If test failed then check the socket path and make sure socket file is there and check the nginx error log:

$ sudo tail -F /var/log/nginx/error.log

Make corrections and test again until test is passed.

If test is passed then restart the nginx:

$ sudo systemctl restart nginx

Go to your domain i.e. example.com and you will see your site up and running.

Set up LEMP

Install mysql server

$ sudo apt-get install mysql-server

and then complete the mysql set up with

$ sudo mysql_secure_installation

Follow the instruction and you are done.

Install php

$ sudo apt-get install php-fpm php-mysql php-curl php-gd php-mbstring php-mcrypt php-xml php-xmlrpc nginx

There is a flaw in php that will allow attackers to exploit php site. So, to fix this, open php configuration file:

$ sudo nano /etc/php/7.0/fpm/php.ini

and add

cgi.fix_pathinfo=0

at the end of the file.

Now, restart the php processor:

$ sudo systemctl restart php7.0-fpm

Configure Nginx to use php

Open the default server block configuration file:

$ sudo nano /etc/nginx/sites-available/default

and replace the code with the following one:

server {
listen 80 default_server;
listen [::]:80 default_server;

root /var/www/html; #directory path where your php files must be located
index index.php index.html index.htm index.nginx-debian.html;

server_name php.example.com; #your domain or sub-domain or server's ip address

location / {
try_files $uri $uri/ =404;
}

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}

location ~ /\.ht {
deny all;
}
}

Test the server block configuration file:

$ sudo nginx -t

If test failed then recheck your file and re-run the test.

If test is passed, then restart the nginx:

$ sudo systemctl reload nginx

Test the LEMP installation

Go to /var/www/html

$ cd /var/www/html

Create the file index.php

$ sudo nano index.php

Paste the following code in it:

<?php
phpinfo();

Go to php.example.com or whatever domain you have set. You must see information about your server and php installation. After checking the site, remove the file, it is not safe to show server information.