Part 1 — How to build a File Manager Storage web app with Django Rest Framework and Vue.js with Vuex and Ag-grid integration

Since beginning of this year, I have been attending an online course. We have a platform that enables us to store and share files collaboratively. I wanted to clone it, so this web application idea was born ✍️

I will guide you step by step to create a File Manager storage web application. we will have a tech stack as Django with DRF, vue.js with webpack and vuex and libraries such as ag-grid, bootstrap, axios etc. 🎬

In the first part of this tutorial we are going to:

  • Install and setup Django with Django Rest Framework
  • Install Vue.js and webpack
  • Configure Django and vue to make them work together
  • Create a database model in Django
  • Create an API endpoint with Django Rest Framework

Let’s start coding! I suppose that you have Python 3.6+ and Node.js installed.

We start with creating a new environment as following. We are going to use “virtualenv” module which comes with Pythton 3.6

(OSX)
python3 -m venv .env
(Linux and Windows)
virtualenv .env
# Activate your environment
. .env/bin/activate

We are going to use:

Django — Vue.js 2.0 — Webpack

It is good practice to create and requirements.txt file to install our dependencies for Django.

cat > requirements.txt
Django
django-rest-framework
django-webpack-loader

Press Control + D to save the file or install each library with pip manually.

Enter the following and run requirements.txt to install Django and DRF, also with webpack loader package which we will need to make django to talk to vue.

pip3 install -r requirements.txt

Next, create a Django project

django-admin startproject djvuetut

Your current file tree should look like this for the moment. You may want to test it and see that it runs in your browser.

cd djvuetut
python manage.py runserver # start your server in localhost:8000

Ignore any unapplied migrations warning for the moment and let’s check http://localhost:8000 and make sure you have a running django app!

click control + C to stop the server.

Now, we will continue with Vue.js installation and setup.

~ make sure that you are in the same directory with manage.py

enter the commands below to install vue-cli and webpack with simle configuration.

npm install -g vue-cli
vue init webpack-simple
Generate project in current directory? Yes
Project name djvuetut
Project description A Vue.js project
Author talented <ozguryarikkas@gmail.com>
License MIT
Use sass? No
   vue-cli · Generated "djvuetut".
   To get started:

npm install
npm run dev

as stated:

enter npm install to install dependencies (npm modules) — This may take a while and npm run dev to start the server:

npm install
npm run dev

Default server port for Vue.js is 8080 so you can test that it runs at http://localhost:8080 if it doesn’t load automatically.

Just a quick introduction on how Vue is working in principal:

→ a vue.js instance is already created in main.js

→ the property el: binds the Vue instance to the HTML element with id=”app” and tell us that it is rendered in App.vue

→ in App.vue we have the Vue.js system to enable us to render data into DOM using double braces syntax.


Configure Django and Vue to work together

Now we need some Vue.js plugins to integrate vue with django. We already installed django-webpack-loader package for django. It requires just a few settings to work. First we need to install the following plugin.

Install the following by using npm

npm install --save-dev webpack-bundle-tracker
npm install --save-dev write-file-webpack-plugin

after installing these packages, it is time to make some configurations. Make the following changes in webpack.config.js


# ./webpack.config.js
var path = require('path');
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker'); // add this
module.exports = {  
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
// publicPath is deleted here
filename: 'build.js'
},
plugins: [ // add this new BundleTracker({ // add this filename: './webpack-stats.json' // add this
}) // add this
], // add this module: {
rules: [
....

in django settings, add the following:

# ./djvuetut/settings.py
# add 'webpack_loder' to INSTALLED APPS
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'webpack_loader', # add this
]
...
# Configure TEMPLATES DIRS and add BASE_DIR
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR], # update here
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages',
],
},
},
]
# Add this to the end of settings
# settings for django-webpack-loader
STATIC_ROOT = os.path.join(BASE_DIR, 'public')
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'dist'),
)
WEBPACK_LOADER = {
'DEFAULT': {
'CACHE': not DEBUG,
'BUNDLE_DIR_NAME': '',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
'POLL_INTERVAL': 0.1,
'TIMEOUT': None,
'IGNORE': ['.+\\.hot-update.js', '.+\\.map']
}
}

Yes, now we are ready with our configuration. We will need to add our django tags to the main index.html to load the render-bundle from webpack-bundle. final look of index.html will look like below with added tags:

# ./index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>djvuetut</title>
</head>
<body>
{% load render_bundle from webpack_loader %} // add this
<div id="app">
<django></django> // add this
</div>
{% render_bundle 'main' %}                           // add this
</body>
</html>

We also need to update the urls.py in djvuetut folder as follows:

# .djvuetut/urls.py
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('', TemplateView.as_view(template_name='index.html'), name='Home'),
]

and we need to add <django> tag as a component to our Vue instance in main.js under /src

# ./src/main.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
components: {
'django': App // add this
}
})

we should be ready now. Run the latest commands below.

# Compile VueJS App 
npm run build
#collect static files in public folder
python manage.py collectstatic
#start django server in localhost
python manage.py runserver

Voila! 👏👏👏 when you route to http://localhost:8000 , you should be seeing your view in App.vue. Now you have a fully working vue.js app with a django backend.


Create an API endpoint with Django rest framework

the goal is to create and API endpoint to:

  • upload and download files to the server
  • display a list of files in our database

let’s start an django app and call it filemanager, so we create our models for our API

django-admin startapp filemanager

we need to add it under INSTALLED_APPS in settings.py

we have already installed DRF when we run our requirements.txt but we also need to add it to installed apps to let django know that we are going to use it.

# ./djvuetut/settings.py
INSTALLED_APPS = [
...
'webpack_loader',
'filemanager' # add this
'rest_framework', # add this
]

actual file tree should look like this:

we will also create an admin user to manage our database from admin portal provided by django admin in case we need it. But first we have to migrate any changes before creating a super user:

$ python manage.py migrate
$ python manage.py createsuperuser
# I think creating a super user is self explanatory. We will use it later.

What we have to do now:

  • Create our model and fields to store file data in database
  • Create a folder in base project directory to store MEDIA data
  • Add media related settings (MEDIA_ROOT & MEDIA_URL) to settings.py
  • Modify url.py to handle static media content

let’s start with creating a model for our file manager in models.py:

# ./filemanager/models.py
from django.db import models
from django.utils import timezone
class Data(models.Model):
file_id = models.AutoField(primary_key=True)
file = models.FileField(null=True, max_length=255)
date_created = models.DateTimeField(default = timezone.now)
    def __str__(self):
return str(self.file.name)

our model enables us to upload any file with any extension and size. We haven’t setup any validators yet, we will make the fine tuning later. We need to complete a few more configuration to make it work.

# ./djvuetut/settings.py
MEDIA_URL = "/media/"                                 # add this 
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # add this

we also need to configure our urls.py and define the new added folder as a static folder.

# ./djvuetut/urls.py
... 
from django.conf import settings # add this from django.conf.urls.static import static # add this
urlpatterns = [
...
]
if settings.DEBUG:     
urlpatterns += static( # add this
settings.STATIC_URL,
document_root=settings.STATIC_ROOT
)
urlpatterns += static( # add this
settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Django Rest Framework (DRF) Setup

we will define a folder called “media” to keep our files. It will be automatically created in our first upload. now, it is time to create a setup to be able to use DRF.

There are more than one way and I wanted to create the most hassle free setup. It might not be so detailed or if we want to scale our app in the future, we may need to update our code accordingly. To maintain the correct business logic, we are going to create 3 files:

  • viewsets.py
  • serializers.py
  • routers.py

Let me add the required code for each. After that I will give an explanation what they do in general.

Create serializers.py under ./filemanager and add the following code:

# ./filemanager/serializers.py
from rest_framework import serializers
from .models import Data
class DataSerializer(serializers.ModelSerializer):
    size = serializers.SerializerMethodField()
name = serializers.SerializerMethodField()
filetype = serializers.SerializerMethodField()
since_added = serializers.SerializerMethodField()
    class Meta:
model = Data
fields = ('file_id', 'file', 'since_added', 'size', 'name', 'filetype')
    def get_size(self, obj):
file_size = ''
if obj.file and hasattr(obj.file, 'size'):
file_size = obj.file.size
return file_size
    def get_name(self, obj):
file_name = ''
if obj.file and hasattr(obj.file, 'name'):
file_name = obj.file.name
return file_name
    def get_filetype(self, obj):
filename = obj.file.name
return filename.split('.')[-1]
    def get_since_added(self, obj):
date_added = obj.date_created
return date_added

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.

The ModelSerializer class provides a shortcut that lets you automatically create a Serializer class with fields that correspond to the Model fields.

We have to use also SerializerMethodField to get the attributes of our file. we can just serialize file_name, file_size and filetype with this way. Get more info about serializers here in original documentation: http://www.django-rest-framework.org/api-guide/serializers/

create viewsets.py under ./filemanager and add the following code:

# ./filemanager/viewsets.py
from rest_framework import viewsets
from .models import Data
from .serializers import DataSerializer
class DataViewSet(viewsets.ModelViewSet):
queryset = Data.objects.all()
serializer_class = DataSerializer

DRF allows you to combine the logic for a set of related views in a single class, called a ViewSet. A ViewSet class is simply a type of class-based View.

We have to provide a queryset and a serializer class that we already created in our serializers.py

Get more info about viewsets here in original documentation: http://www.django-rest-framework.org/api-guide/viewsets/

create routers.py under ./djvuetut and add the following code:

# ./djvuetut/routers.py
from rest_framework import routers
from filemanager.viewsets import DataViewSet
router = routers.DefaultRouter()
router.register(r'files', DataViewSet, base_name='data')

With this piece of code, we created a router object and register ‘/files/’ url extension. We want to use it for our API endpoint with also providing our viewSet.

Get more info about routers here in original documentation: http://www.django-rest-framework.org/api-guide/routers/

The important point is here that we need to tell django’s urls.py that we will use a router to manage our urls with DRF

# ./djvuetut/urls.py
from django.contrib import admin
from django.urls import path, include # import "include" here
from .routers import router # add this
...
urlpatterns = [
path('admin/', admin.site.urls),
path('', TemplateView.as_view(template_name='index.html'),name='Home'),
path('api/', include(router.urls)), # add this
]
...

We are done for now with the DRF settings 🏹 let’s test it out! 😎

Enter the following in your browser and see what is available to us. By the help of DRF, we now have a fully usable rest framework for our model 💪🏻

http://localhost:8000/api/

click on ‘files’ viewset that we defined. Here you can upload a file to test it out. Just choose a file and click on POST. You will notice that a folder with a name “media” will be created in the root directory and the file is uploaded under it.

we will be able to reach the properties of that file through JSON. All we need to do is to parse this JSON query in our Vue views and that’s it. You can upload here more than one file and reach them individually by supplying their ids like that:

http://localhost:8000/api/files/<id>

In the second part of this series, we will create our front end in vue with bootstrap, integrate ag-grid to showcase our files, create CRUD operations with axios and much more. Stay tuned! ⚒⚒⚒