7 Aug 2023

Upload Files APP

This is a simple app to upload files to a server. It is built with Django using the Model View Controller (MVC) design pattern. The app is containerized using Docker and can be deployed locally or to a production server.

This app doesn't have user management. It is assumed that the user is already authenticated and authorized to upload files to the server. And it not using the Admin interface.

The repository for this app can be found here.

Running App

The home page has a form where the images are supply and a table to display the files that have been uploaded. The only allow file types are (txt, pdf and docx) and the maximum file size is 5MB.

img1

Requirements

To run this app you will need the following:

  • Docker

Installation

Local

docker-compose -f local.yml build
docker-compose -f local.yml django python manage.py makemigrations
docker-compose -f local.yml django python manage.py migrate
docker-compose -f local.yml up -d

Production

docker-compose -f production.yml build
docker-compose -f production.yml django python manage.py makemigrations
docker-compose -f production.yml django python manage.py migrate
docker-compose -f production.yml up -d

Struncture

The structure of the app a simple Django app:

.
UploadFilesApp/
├── __init__.py
├── Migration/
├   └── __init__.py
├── admin.py
├── apps.py
├── models.py
├── views.py

The models.py file has the model for the app. The views.py file has the logic for the app. The form.py file has the logic of filtering and saving the files. The apps.py file has the configuration for the app.

Model

The Model has four fields: - name: name of the file - file: the file - size: size of the file - uploaded: date of the upload

# django
from django.db import models
# utils
import re


def extension_directory_path(instance, filename):
    extension_search = re.search('^.*\.([a-z]{1,})$', filename)
    if extension_search:
        extension = extension_search.group(1)
        return 'files/{0}/{1}'.format(extension, filename)


class File(models.Model):
    """
    File

    This is the model for the data base
    """
    name = models.CharField(max_length=200)
    file = models.FileField(upload_to=extension_directory_path)
    size = models.PositiveBigIntegerField()
    uploaded = models.DateField(auto_now=True)

The upload of the files is done using the FileField field. The upload_to parameter is a function that returns the path where the file will be saved. In this case the path is files/{extension}/{filename}. The extension_directory_path function is used to get the extension of the file and return the path.

Views

The view render the template and handle the form. The form_valid method is used to save the file to the database. The get_context_data method is used to add the list of files to the context.

# Django
from django.urls import reverse_lazy
from django.views.generic import FormView
# Forms
from UploadApp.forms import FileForm
# Model
from UploadApp.models import File


class FileView(FormView):
    template_name = 'files.html'
    form_class = FileForm
    success_url = reverse_lazy('upload_home')

    def form_valid(self, form):
        """If the form is valid, save the associated model."""
        self.object = form.save()
        return super(FileView, self).form_valid(form)

    def get_context_data(self, **kwargs):
        """adding List"""
        new_kwargs = super(FileView, self).get_context_data(**kwargs)
        new_kwargs['files'] = File.objects.all()
        return new_kwargs

Form

The form is used to validate the file and save it to the database. The clean method is used to validate the file size and format. The maximum size is define by the MAX_DOCUMENT_SIZE variable and the formats by the DOCUMENT_FORMATS variable. The save method is used to save the file to the database.

# Django
from django import forms
from django.forms import ModelForm, ValidationError
# Model
from UploadApp.models import File
# Utils
import re

MAX_DOCUMENT_SIZE = 5*(1024**2)

DOCUMENT_FORMATS = ['pdf', 'docx', 'txt']


class FileForm(ModelForm):
    """
    File Form

    This form to parser the data to model and make the validation of the files.

    Validations :
    - size : no more than 5MB
    - formats accepted : .pdf .docx .txt

    """
    class Meta:
        model = File
        fields = ['file', 'name', 'size']
        exclude = ['name', 'size']

    def clean(self):

        file = self.cleaned_data['file']
        if file.size > MAX_DOCUMENT_SIZE:
            raise ValidationError('The File has to be less than 5MB')

        search_ext = re.search('^.*\.([a-z]{1,})$', file.name)
        if search_ext:
            if not (search_ext.group(1) in DOCUMENT_FORMATS):
                raise ValidationError(
                    'File has to be {}'.format(DOCUMENT_FORMATS))
        else:
            raise ValidationError('File has no extension')

        return self.cleaned_data

    def save(self, commit=True):

        instance = super(FileForm, self).save(commit=False)
        file = self.cleaned_data['file']
        instance.name = file.name
        instance.size = file.size
        if commit:
            instance.save()
        return instance

Template

The template is a simple bootstrap 5 template. The files.html template is used to render the list of files and the form. The base.html template is used to render the files.html template.

The template files.html has the form of upload files and the list of files uploaded.

File.html:

{% extends "base.html" %}

{% block head_content %}
  <title> File Form </title>
{% endblock%}

{% block container %}
  <div class="row justify-content-center mt-3">
    <div class="col-auto">
      <h1>File Form</h1>
    </div>
  </div>

  <div class="m-5">

    {% if form.errors %}
    {% for field in form %}
        {% for error in field.errors %}
            <div class="alert alert-danger">
                {{ error|escape }}
            </div>
        {% endfor %}
    {% endfor %}
    {% for error in form.non_field_errors %}
        <div class="alert alert-danger">
            {{ error|escape }}
        </div>
    {% endfor %}
    {% endif %}

    <form action="{% url 'upload_home' %}" method="POST" enctype="multipart/form-data">
      {% csrf_token %}
      <div class="mb-3 row">
        <div class="col-3"> 
          <label for="formFile" class="form-label">Only [.pdf,.txt,.doc]</label>
        </div>
        <div class="col-9">
          <input class="form-control" type="file" id="formFile" name="file">
        </div>
      </div>

      <div class="mb-3 row">
        <div class="offset-10 col-2">
          <button type="submit" class="btn btn-primary">Send</button>
        </div>
      </div>

    </form>
  </div>   

  <div class="row justify-content-center">
    <div class="col-auto">
      <h3>Files</h3>
    </div>
  </div>

  <table class="table">
    {% if files %}
      <thead>
        <tr>
          <th scope="col">Name</th>
          <th scope="col">Size</th>
          <th scope="col">Link</th>
        </tr>
      </thead>
      <tbody>
        {% for file in files %}
          <tr>
            <td>{{ file.name }}</td>
            <td>{{ file.size|filesizeformat }}</td>
            <td><a href="{{ file.file.url }}" >Download</a></td>
          </tr>   
        {% endfor %}
      </tbody>
    {% else %}
      <thead>
        <tr>
          <th scope="col">Name</th>
          <th scope="col">Size</th>
          <th scope="col">Link</th>
        </tr>
      </thead>
      <tbody>
      </tbody>
    {% endif %}
  </table>

{% endblock %}
© 2019 Jsuarez.Dev