Search code examples
pythondjangodjango-channelsdjango-file-upload

Django channels and file uploads


I'm learning Django on the fly so bear with me. My goal here is for a user to be able to upload a file, the server script process it and send the results on each iteration to the client live.

My current structure is: User uploads a file (csv), this is read via views.py then renders the blank html view, JS in the html creates a new websocket, and well this is where I'm not able to pass data outside of consumers.py or process the data.

I'm also not sure the structure of view > JS script > consumers > ? is the best route for processing files but I've not found much documentation on file uploads and channels.

views.py

from django.shortcuts import render
import pandas as pd

def someFunc(request):
    if request.method == "POST":
        global csvUpload
        csvUpload = pd.read_csv(request.FILES['filename'])
        return render(request, 'app/appPost.html')

I render the view here first so that the blank page displays prior to the script running.

appPost.html JS script

var socket = new WebSocket('ws://localhost:8000/ws/app_ws/')
socket.onmessage = function(event){
    var data = JSON.parse(event.data);
    console.log(data);
            
    var para = document.createElement("P");
    para.innerText = data.message;
    document.body.appendChild(para);
}

consumers.py

from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync

class WSConsumer(WebsocketConsumer):
def connect(self):
    self.accept()
    self.render()
    async_to_sync(self.add_group)('appDel_updates_group')

Ultimately I'd then like to pass what's in this alarm file to the websocket connection.

app_alarm.py

from requests.api import request
import pandas as pd
import requests as r

def app_process(csvUpload):

    csvUpload=pd.read_csv(request.FILES['filename'])
    csvFile = pd.DataFrame(csvUpload)
    colUsernames = csvFile.usernames
    for user in colUsernames:
        req = r.get('https://reqres.in/api/users/2')
        t = req.json()
        data = t['data']['email']
        
    message = user + " " + data
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        'appDel_updates_group',
        {'type': 'render', 'message': message}
    )

How do you do something after you render the view? (Django)

This seems somewhat relevant, I'm going to see about creating a thread to run the file processing only after the render is done in views.

Edit: Threading allowed me to render the page then pass the data over to the alarm.py file for processing. This has solved the issue I was having by adding the following code into the views.py:

x = threading.Thread(target=app_alarm.sub_process, args=[csvUpload])
x.setDaemon(False)
x.start()
return render(request, 'efax/appPost.html')

This allows views to grab the file using request.FILES and pass it to the script before the page was rendered without waiting on the completion of the processing. That would have rendered the entire point of channels in this situation pointless. Hopefully this helps someone.


Solution

  • Copying from the original post edit:

    Threading allowed me to render the page then pass the data over to the alarm.py file for processing. This has solved the issue I was having by adding the following code into the views.py:

    x = threading.Thread(target=app_alarm.sub_process, args=[csvUpload])
    x.setDaemon(False)
    x.start()
    return render(request, 'efax/appPost.html')
    

    This allows views to grab the file using request.FILES and pass it to the script before the page was rendered without waiting on the completion of the processing. That would have rendered the entire point of channels in this situation pointless. Hopefully this helps someone.