9

I want to upload a file to my database and after it is uploaded import it and eventually export the data into my database. I have the uploading working just fine but I'm not sure how to get the absolute path of the file after it is uploaded. I'm able to print out the name of the document, but if the same document name is uploaded it is appended but still shows the original file name if I call form.cleaned_data['document'].name. What can I do to get the absolute file path and then call a function to start processing this file?

So this is what I'm looking to do:

  • User uploads a .csv file
  • File gets saved in db (with a description and file path. File path is getting stored properly in db)
  • Get file location of file that was just uploaded
  • Start to process this file to convert the .csv data and store in database

models.py

from django.db import models

# Create your models here.
class Document(models.Model):
    description = models.CharField(max_length=255, blank=True)
    document = models.FileField(upload_to='documents/')
    uploaded_at = models.DateTimeField(auto_now_add=True)

views.py

from django.shortcuts import render, redirect
from django.views import View
# Create your views here.

from .forms import DocumentForm
from .models import Document   

class  FileUpload(View):
    def post(self, request):
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            print()
            print(form.cleaned_data['document'].name)
            form.save()
            return redirect('main_db_model:home')
        else:
            return render(request, 'file_upload_form.html', {
                'form': form
            })

    def get(self, request):
        form = DocumentForm()
        return render(request, 'file_upload_form.html', {
            'form': form
        })

forms.py

from django import forms
from .models import Document

class DocumentForm(forms.ModelForm):
    class Meta:
        model = Document
        fields = ('description', 'document', )

file_upload_form.html (template):

{% extends "base.html" %}

{% block content %}
  <form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Upload</button>
  </form>
      {% if saved %}
         <strong>Your profile was saved.</strong>
      {% endif %}
  This is the form page
  <p><a href="{% url 'main_db_model:home' %}">Return to home</a></p>

    <p>Uploaded files:</p>
      <ul>
        {% for obj in documents %}
          <li>
            <a href="{{ obj.document.url }}">{{ obj.document.name }}</a>
            <small>(Uploaded at: {{ obj.uploaded_at }})</small>
            {{ obj.document.url }}
          </li>
        {% endfor %}
      </ul>
{% endblock %}
Ducksauce88
  • 640
  • 3
  • 12
  • 26

3 Answers3

9

Previously, I suggest you to change this upload_to='documents/' to upload_to='documents/%Y/%m/%d', why? this to handle huge document files inside your path of documents/.


form.cleaned_data['document'] (or request.FILES['document']) return a UploadedFile object. of course form.cleaned_data['document'].name should return a name only.

class  FileUpload(View):
    def post(self, request):
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            # this `initial_obj` if you need to update before it uploaded.
            # such as `initial_obj.user = request.user` if you has fk to `User`, 
            # if not you can only using `obj = form.save()`
            initial_obj = form.save(commit=False)
            initial_obj.save()

            # return path name from `upload_to='documents/'` in your `models.py` + absolute path of file.
            # eg; `documents/filename.csv`
            print(initial_obj.document)

            # return `MEDIA_URL` + `upload_to` + absolute path of file.
            # eg; `/media/documents/filename.csv`
            print(initial_obj.document.url)

            form.save()

But you will get a different if you using upload_to='documents/%Y/%m/%d',

print(initial_obj.document)      # `documents/2017/02/29/filename.csv`

print(initial_obj.document.url)  # `/media/documents/2017/02/29/filename.csv`
binpy
  • 3,994
  • 3
  • 17
  • 54
1

The answers work nice if you are trying to save the file from an actual Django Form. But, maybe someone (like me) stumbled upon this question from a REST API context. So, here's what works for me on django==3.2 in the context of handling a rest api file upload post request:

    from django.core.files.storage import FileSystemStorage
    img_file = request.FILES['file']
    
    fs = FileSystemStorage()
    filename = fs.save(img_file.name, img_file)
    uploaded_file_path = fs.path(filename)
    print('absolute file path', uploaded_file_path)    

so, fs.path(filename) actually returns the absolute path to the file i just uploaded. e.g: in my case my app runs in a debian docker container and my media directory is /vol/web/media and i get output like /vol/web/media/myfile.jpg which is exactly what i want.

because_im_batman
  • 975
  • 10
  • 26
1

I think it is better to get the path after uploading the file, below is an example using the create method (model.objects.create())

models.py

class AuctionPurchase(models.Model):
    file = models.FileField( upload_to='auction_purchase_files/')
    file_name = models.CharField(max_length=255)
    updated = models.DateTimeField(auto_now=True)
    timestamp = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.file_name
    
    class Meta:
        ordering = ["-timestamp", ]

views.py

def auction_purchases_upload(request):
    template_name = 'auction_purchases/auction_purchases_upload.html'
    if request.method == 'POST':
        file = request.FILES.get('file')
        file_obj = AuctionPurchase.objects.create(
            file=file,
            file_name = file.name
        )
        file_path = '/media/'+ str(file_obj.file)
        Print(file_path)
        messages.success(request, 'File successfully upload.')
        return redirect('auction_purchases:auction_purchases_upload')
    form = AuctionPurchaseForm()
    return render(request, template_name,{'form':form})

in the view, we can use the create method which returns an instance of the created object which can be accessed using model field names for example file_obj.file gives you the saved relative path which I just had to append the media folder to make the path complete, to get the absolute path just do file_obj.file.path