Run external python script with input file as argument

Hello, i’m trying to developp my first Django project and i’m actually stuck with an error.

So i have on my HTML template a button to load a xls file, and a submitting button that i want it to start an external python script using this file.

Here is my project:

home.html

<h1>Home</h1>
<form action="/external/" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    Input xls file :<br><br>
    <input type="file" name="InputFile" accept=".xls" required><br>
    <input type="submit" value="execute python script"><br>
</form>

views.py

from subprocess import run, PIPE
import sys

def home(request):
    return render(request, 'home.html')

def external(request):
    inp=request.FILES.get('InputFile')
    run([sys.executable,r'pathToMyDjangoProject\script.py', inp], shell=False,stdout=PIPE)
    return render(request,'home.html')

urls.py

urlpatterns = [
    path(r'', views.home),
    path('external/', views.external),
]

script.py

def process(excelFile):
    rdr = pd.read_excel(excelFile, header=None).values.tolist()
    
    for row in rdr:
    ...

Finally the html page seems to work, le type of inp is a django InMemoryUploadedFile so i guess it’s correctly loaded but the error i have is :

TypeError at /external/
expected str, bytes or os.PathLike object, not InMemoryUploadedFile
.
|Request Method:|POST|
|Request URL:|http://127.0.0.1:8000/external/|
|Django Version:|3.2.7|
|Exception Type:|TypeError|
|Exception Value:|expected str, bytes or os.PathLike object, not InMemoryUploadedFile|

I think to understand there is a problem with variable’s type in the run() function but do you have an idea to correct it ?

Thank you by advance,

The way you have this constructed, you’re going to need to save the file to disk somewhere, and then pass the fully-qualified file name as the parameter.

Ok thank you, that is also what i thought.

And otherwise, do you have any idea on how to do it without the need to save the file ? Not the full script but maybe some terms or links to help me search in the right way ?

What about some model? Like FileToProcess?

You can then create manage.py command that has access to Django ORM, you can query the “to be processed” files and do what you need to do.

Unless there’s more to this than what you’ve posted, I don’t see the benefit of running this as an external process.

Instead of:

You could try:

def external(request):
    inp=request.FILES.get('InputFile')
    process(inp)
    return render(request,'home.html')

def process(excelFile):
    rdr = pd.read_excel(excelFile, header=None).values.tolist()
    
    for row in rdr:
    ...

You may also need to open and read the file to get the data from the file field - see Managing files | Django documentation | Django.

Well, my script.py is about 600 lines long. I did not imagine myself integrating it fully into the external function of views.py. Even if in practice it would work.

And what if i turn it into a python function and i just import it to views.py ? any contraindications?

Notice that I didn’t suggest that. I show external as calling a function named process.

A function is a function. There is no semantic difference whether a function resides in the same file or is in a different file and imported.

None at all.

The key here is that there’s no need to start another instance of Python to run your function. (Ok, so there are a couple of real edge cases that may make it desirable to do so, I just have no reason to believe that this would be one of them.)
By running your function within the same process, you can pass the data directly - it does not need to be passed via some external mechanism like a file or pipe.

I understand, yes sorry I did not really understand your first answer but it is ultimately the simplest to put in place.
Thanks for your help

1 Like