Pop custom file browser from a ModelForm

Hi, I am trying to create a form where the User fills in a few data in the form and then clicks a button(say ‘browse for files’) and that should pop up another form where the user can choose the server-side files. In the end, I need the file path the User chooses in that form, which I will then use to do further processing.

So far, I have defined my models with a FileField. But using that, it only shows the client side files which doesn’t meet my requirement. So I plan to create a button and using javascript onclick, open a pop-up form.

The pop-up form needs to navigate through the directories and track the User’s chosen file with the path. And this path is then passed to the main form and then it is submitted so that I can perform the next steps.

I have used this as a template, but finding it hard to translate to Django runnable one. (Simple Folder tree with PHP and jQuery)

I am a newbie to Django, javascript, and Ajax. Any help on this is appreciated.

Views.py:

def file_browserJSON(request):
    context = ''
    root_directory = os.path.join('path')
    files = []
    folders = []

    if os.path.exists(root_directory):
        if( root_directory[ len(root_directory) - 1 ] ==  '/' ):
            folders = root_directory
        else:
            folders = root_directory+'/'
        dir1 = dir(root_directory)
        for f in os.walk(root_directory):
            files.append(f)

    if( len(files) > 2):  #First 2 entries are . and ..  -skip them
        print(files)
        files.sort()
        print(files)
        context = '<ul class="filetree" style="display: none;">'

        for dirpath, dirs, f in os.walk(root_directory):
            print(dirpath)
            print (dirs)
            print(f)
            context += '<li class="folder collapsed"><a href="#" rel="'+ str(dirpath)+'/">' + str(dirs) + '</a></li>'
            for file in f:
                ext = re.sub('/^.*\./', '', file)
                context += '<li class="file ext_'+ext+'"><a href="#" rel="' +dirpath+'">'+file+'</a></li>'
        context += '</ul>'
    return JsonResponse({'context':context})

forms.py:

from django import forms
from django.db import models

from .models import Whisper

class WhisperForm(forms.ModelForm):
    class Meta:
        model = Whisper
        fields = '__all__'
        labels = {
                    'name': 'Name for this command run ',
                    'model': 'Model to use ',
                    'output_format': 'Format of the output file ',
                    'task': 'Whether to perform speech recognition or translation ',
                    'language': 'Language spoken in the audio ',
                    'upload_file': 'Choose the file here ' //Need to add a button here 
        }

Filebrowder.html:
Javascript call (which I have created a separate form as I am not sure how to modify forms.py):

<script type="text/javascript" src="whisper/static/js/filebrowser.js" >
</script>
</head>
<body>
    <div id="container"> </div>
    <div id="selected_file"></div>
</body>

filebroswer.js:

const containerBox = document.getElementById('container')
containerBox.innerHTML = '<ul class="filetree start"><li class="wait">' + 'Generating Tree...' + '<li></ul>'

const selectedFile = document.getElementById('selected_file')
selectedFile.innerHTML = '<ul class="filetree start"><li class="wait">' + 'Generating Tree...' + '<li></ul>'

$.ajax {
    type: 'GET'
    url: '/filebrowserJSON',
    success: function (response) {
        console.log('success', response.context)
        const data = response.context
        console.log(data)
        data.forEach(el=> {
            const entry = $(".class:contains('folder')" );
            containerBox.innerHTML += `
            ${} // #Incomplete The data needs to be modified here such that the user navigation through folders is tracked.
        });
    },
    error: function(error) {
        console.log('error', error)
    }
}

If the files being selected are already on the server, you would not use a FileField to store the reference to that file, unless those files are in the directory tree starting with MEDIA_ROOT. You would only use a CharField, and store the absolute location of the file in that field.

(Note: For a variety of very good reasons, browsers will not allow you to programmatically modify a file upload input field.)

Thank you Ken for your response.
You are right, I do not wish to use Filefield anymore and I need to change it. This is because the FileField uploads the file into the static folder. In my case, I only need the path of the file.
So the form needs to have a ‘Browse’ like button to pop open another form where the user can traverse through the folder structure to point at the location of the file. This information is captured in that popup form and then passed to the calling form.
Do you have further pointers on this?

Not in general, no. I looked at the sample you’re working from, and it looks like a good starting point. Do you have a specific question or problem that you’re having with it?

Yes, I am looking for how to customize the forms.py such that it uses the ModelForm to load the page and then in the same file, I can also customize the code to create my pop up form. How can I achieve this? Do I need to use Formsets? something I read in stackoverflow but not sure if I need it in my case.

Clarifying a couple of points.

A Form (model or otherwise) does not “load a page”.

A view generates a page, typically from a template.

When rendering a template to create a page, the view passes some data (the “context”) to the rendering engine, that the rendering engine uses to populate the data fields in the template.

That context may contain a Form, which was created by the view.

A view may create multiple forms, and add them all to the template to be rendered.

The template may contain references to other files to load things like JavaScript and CSS. Those external references are loaded by the browser when the page has been sent to the browser.

That JavaScript may create “pop up” (usually modal) dialog boxes to be displayed in the browser.

So, the view can create the form to be displayed as a modal dialog box. That div would usually be part of the page being rendered by the template engine. It’s the responsibility of the JavaScript to show/hide that div when appropriate, and possibly to do any processing involved with any data entered into that dialog.

How you handle modal dialog boxes depends a lot upon what JavaScript library / framework you may be using.

See Formsets | Django documentation | Django for the definition of what a formset is and what it does. Whether you want to use one is something only you can determine. (What facet of this do you think it’s going to help you with?)

I now understand that Formset is not the answer to my question as it involves generating multiple forms from Modal.
I only need a custom pop-up to browse the server-side file system and track the traverse at the same time to get the full path of the file rather than uploading the file into the Django project.

I have the template:

 <div class="custom-file">
              <label class="custom-file-label" for="{{ field.upload_file.id_for_label }}">Browse the path for the input file</label>
              <input type="file" class="custom-file-input" id="customFile">
  </div>

and on click of the browse button (js and ajax), the pop-up would show the server-side file system retrieved from the view.
Now I am the concept that is not clear to me is how Django dynamically retrieves the path structure when the user clicks on each folder. The folder structure collapses and opens as the user clicks on it.

Can you please elaborate?

If you go back to the example that you referenced, you’ll see there’s a JavaScript function named getfilelist, which issues an AJAX call to retrieve file names from the server.

Actually, depending upon the number of files and the rate of activity of those files, you have at least three different ways to approach this:

  • Render the complete directory tree on the server, with the divs marked as display: none;. Have the JavaScript toggle that for each directory when clicked.

  • Send the entire directory tree structure as JSON to the browser, and have the JavaScript in the browser render just the html as it is needed.

  • Retrieve the file list incrementally as in the example.

If you have a relatively small number of directories / files (< 1000 or so), and files aren’t added or removed regularly (more than once/day), you’re probably ok with rendering the complete tree on the server at start.