Part 2 — How to build a File Manager Storage web app with Django Rest Framework and Vue.js with Vuex and Ag-grid integration
This article is Part 2 of “How to build a File Manager Storage web app series”. In Part 1, we completed the setup of Django rest framework and Vue.js and connected them by the help of Django webpack loader. In the second part, we will go deep in vue and create our application for uploading, listing and deleting any files with an eye-catching ui. 💎
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 second part of this tutorial we are going to:
- Configure DRF to upload and delete files
- Configure main.js and install vue libraries to be used
- Create Vue components and Configure Ag-Grid settings
- Configure vuex settings for CRUD with Axios
Let’s start coding! ☕️
Configure DRF to upload and delete files
in Part 1, we created an API endpoint with Django rest framework. We will update only viewsets.py that we created under filemanager. This will be the only update that we need to do in server side.
viewsets.py as given the final code below.
We provide a queryset for all objects in our database. This queryset is passed automatically when we call our DataViewSet class. We already registered ‘files’ url extension in routers.py that we created under djvuetut. This queryset will be used here to list all files that we have in our database.
Additionally, We have to handle the post and delete requests in server side and update our database accordingly. We are doing it with post and destroy methods respectively.
Configure main.js and install vue libraries to be used
we are going to use the following vue libraries:
Vuex → a state management pattern + library for Vue.js applications. We will deep dive in it when we create our API requests.
Axios → a node.js HTTP client in order to create API requests.
Bootstrap-vue → Bootstrap V4 components and grid system available for Vue.js
Fortawesome → a font and icon toolkit based on CSS and LESS.
Install all of these libraries with npm:
npm i --save vuex axios bootstrap bootstrap-vue moment npm i --save ag-grid-community ag-grid-vue vue-property-decorator npm i --save @fortawesome/fontawesome-svg-core npm i --save @fortawesome/free-solid-svg-icons npm i --save @fortawesome/vue-fontawesome
After installing our dependencies, we are ready to configure our main.js, the actual starting point of a vue app. We will need to tell vue which libraries we are going to use in our app.
Make sure that the final appearence of main.js is same as below:
Create Vue components and Configure Ag-Grid settings
We are going to create two components: one for header and one for main body. Create a folder called components under src folder. Inside it, create
Home.vue as shown.
App.vue as below. This is the starting point of our application’s front-end.
Next, let’s continue by updating Header.vue: This is just a basic navigation bar actually. The good thing is that we have now bootstrap for vue installed. Bootstrap is a front-end component library and a toolkit for developing with HTML, CSS, and JS. It is much more easier to design a beautiful and professional looking front-end by using bootstrap.
Add the following code inside
Our header component is now ready. Let’s continue with our main component Home.vue but I have to stop here and think about how to continue because Home.vue contains a lot of code. I am just going to write here the full code and after that I will explain step by step the way I constructed it. I have added comments to html section and splitted it virtually as:
- Part-1 → List files
- Part-2 → Upload a file
- Part-3 → Delete file(s)
Add the following code inside
Let’s deep dive to understand what we did here.
Part-1 → List files: we added an ag-grid-vue component. This is where we defined our grid configuration and data source we need.
OnGridReady() is the method which is called when DOM is loaded. We defined our column headers and a few other settings for ag-grid in
beforeMount(). To understand better the lifecycle hooks of vue, have a look at the article below:
Understanding Vue.js Lifecycle Hooks
Lifecycle hooks are an important part of any serious component. You often need to know when your component is created…
we also created
onRowSelected() methods in which we implement our actions.
onRowSelected() method, we calculate the size of selected files and add the total value to
selectedDataTotal to be displayed when single or multiple rows selected.
onRowClicked() method, we send the selected filename to vuex store to be prepared for downloading. More on this later in vuex section.
As you may notice we import a few helper methods from js files that we have to create under /src folder:
We would like to render the type of files which are uploaded. So we can show the correspondent icon for each filetype in
filetype column instead of showing the same icon for all. We use it as a Ag-grid framework component in
cellRenderer parameter of
filetype column definition.
filetypeCellRenderer.js and add the code below:
Now, we are good to go! we would like to display in our list the file sizes in a beautiful format like 85.5KB, 3MB or 2GB etc. So, we will need to format it. In
size column definition, we call
sizeFormatter method as a valueFormatter parameter provided by Ag-grid. This will expect a formatted value that we will provide. Similarly, I would also like format the date and time for uploads. We are going to use a beautiful date and time parser library
moment.js that we already installed. It is so straightforward to parse a date or datetime object with it as you can find more details on their website.
we are going to display the date and time for uploaded files since their addition.
utils.js under /src and add the code below:
Configure vuex settings for CRUD with Axios
I will continue Part-1 → List files under this header bacause we are going to use Vuex and Axios in order to fetch files from our backend as we delivered their information in JSON format from our API.
Vuex is a state management pattern + library for Vue.js applications. We will need the following methods as vuex actions:
- loadFiles → fetch data from Database
- postFile → upload a file into Database
- deleteFile → delete a file from Database by a given ID
- downloadFile → enable to download a file from our media folder by given file name
It is possible to create our application without vuex since we may think that we are going to use it standalone. But what if we would like to perform these actions from multiple locations in our appication. For scalability, it is a good practice to use a state management system, so we won’t need to write the same code for each component. Vuex gives us the opportunity to define a central store that we dispatch a request and get back a response from any Vue components conveniently.
store.js file under /src and add the following code:
now back to
Home.vue and notice that we have a computed property that we call
mapState inside which we imported from vuex.
Computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when some of its reactive dependencies have changed.
mapState method enables us to access store members easily. We have defined an empty
rowData array under
state in our store.js and we fetch this
rowData to be used in our ag-grid list.
Computed property will recall this array whenever it is changed. So we will always have up-to-date file list from our Database without any page refresh. That’s great!
So, the question is now, how is that
rowData array populated? let’s check back our
Home.vue and notice that we dispatch loadFiles method in our mounted method as below:
we then perform a commit to
SET_FILES method under mutations. We fetch the file list with
Axios get method and pass that data.
Part-2 → Upload a file: we used a b-form-file bootstrap-vue component that also supports drag and drop functionality when opening a file browser window. Our @click action is set to
submitFile() method which primarily makes a check for max. file size and then make a vuex call as below:
by also passing the selected file as a form data.
postFile() vuex action method performs a axios post request with passed data to be received by DRF. It is important in a post request to define the Content-Type of the data that we are sending.
post() method that we have added in viewsets.py provided by DRF is able to handle a post request for a file type object with a definition. We defined it as ‘multipart/form-data’ because we designed our application to receive any time of file. We then perform a commit to
POST_FILE method under mutations as we append the new added file to
rowData array and lastly dispatch
loadFiles() for a refresh in our list.
Part-3 → Delete file(s): similarly our @click action is set to
deleteFile() method. Ag-grid lets us to add a checkbox for each row and get their properties for selected rows by simply
this.gridApi.getSelectedNodes() method. We can get an array of unique id’s of all selected files and pass these array to vuex after confirming the delete request with a modal window. This is triggered with
handleOk() method and make a vuex call as below:
deleteFile() vuex action method performs a axios delete request with provided
destroy() method in DRF viewsets.py handle this request and delete the file(s) with given ids.
Last but not least
Ag-grid also lets us to take an action when a specific row is clicked. We have pre-defined
onRowClicked() method available for that. Within this method we make a vuex call to download the file when clicked as below:
downloadFile() vuex action method performs a get request to direct url of the file with the filename saved under ‘media’ folder and uses the response type ‘blob’ to prepare the file to be downloaded. All the files that we uploaded can also be viewed/accessed through the absolute URL path with filename as follows:
I encourage you to implement the tutorial and learn while you are building. You can also clone the full project from Github repository and add to your project by making a few tweaks and adjustments. You can find the source code here on Github.