Django: Как загрузить файл, сгенерированный скриптом python, запущенным в задаче celery, после ее завершения

Я создаю веб-приложение Django, и в настоящее время у меня есть возможность загрузить набор данных (файл .csv). При нажатии кнопки 'upload' запускается задача celery, которая манипулирует набором данных.

Я хочу отобразить страницу с кнопкой "результат загрузки", когда задача celery будет выполнена. В настоящее время существует прогресс-бар, который отображает прогресс выполнения скрипта с помощью инструментария celery-progress (это занимает довольно много времени).

У меня уже есть встроенные пользователи, если это поможет.

Вопрос 1: Где я должен хранить выходной файл, сгенерированный скриптом python, выполняемым в задаче celery, чтобы пользователь мог его скачать?

Вопрос 2: Как отобразить страницу с кнопкой "загрузить результат", когда задача celery будет выполнена?

Вопрос 3: Как мне заставить эту кнопку загрузить файл, относящийся к файлу пользователя?

Вот мой код на данный момент:

views.py

from django.conf import settings
from .models import Document
from .forms import DocumentForm
from django.views import View
from .tasks import RunBlackLight
import os
from django.http import Http404, HttpResponse


class RunView(View):
    form_class = DocumentForm
    initial = {'key': 'value'}
    template_name = 'runblacklight/progress.html'
    error_template_name = 'runblacklight/error.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {'form': form})

    def post(self, request, *args, **kwargs):
        if request.method == 'POST':
            # Get the uploaded dataset from the html page.
            form = self.form_class(request.POST, request.FILES)
            if form.is_valid():
                # Get the information out of the HTML form.
                num_gens = form.cleaned_data.get('number_of_generations')
                num_pop = form.cleaned_data.get('number_in_initial_populations')
                # Save the form, creating an object of the document model located in models.py
                obj = form.save()
                obj.user = request.user
                # Append /code to the file path, as the location of all of our media files is in
                # ./code in our DOCKER container.
                file_path = "/code" + obj.document.url
                print(os.path.abspath(file_path))
                # Error Handling, right now it only throws an error if .csv is not in the filename.
                if ".csv" not in file_path:
                    return render(request, self.error_template_name, {'form': form})
                # Save the instance of blacklight into the database.
                obj.save()
                # Call our celery task which will run the Blacklight instance
                run_blacklight_task = RunBlackLight.delay(num_pop, 2, num_gens, file_path)
                # Get the task id so that we can display the progress bar.
                task_id = run_blacklight_task.task_id
                return render(request, self.template_name, {'task_id': task_id})
        else:
            form = self.form_class()
        return render(request, self.template_name, {'form': form}) 

runblacklight/models.py

from django.conf import settings

User = settings.AUTH_USER_MODEL



def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'documents/user_{0}/{1}'.format(instance.user.id, filename)


class Document(models.Model):
    user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
    description = models.CharField(max_length=255, blank=True)
    document = models.FileField(upload_to=user_directory_path)
    number_of_generations = models.IntegerField()
    number_in_initial_populations = models.IntegerField()
    uploaded_at = models.DateTimeField(auto_now_add=True) 

runblacklight/forms.py

from django import forms
from .models import Document

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

runblacklight/templates/runblacklight/run.html

{% extends "home/base.html" %}
{% load static %}
{% block title %} Run Page {% endblock title%}
<!-- Extends the base template, defined in home/templates/home/base.html. This fills the content block on the website. -->
{% block content %}
    <div class="jumbotron" style="background-color: #000000; border-radius: 0 !important">
        <!-- The block below is where the form to upload datasets lives. It is defined in .runblacklight.html-->
        {% block demo %}
        {% endblock %}
        <!-- JQuery -->
        <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
        <!-- Bootstrap JS -->
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
        <!-- Celery Progress -->
        <script src="{% static 'celery_progress/celery_progress.js' %}"></script>
        <!-- The block below is where the js code for the progress bar lives. It is defined in .progress.html-->
        {% block progress_bar_js %}
        {% endblock progress_bar_js %}
    </div>
{% endblock content %}

runblacklight/templates/runblacklight/runblacklight.html

{% extends "runblacklight/run.html" %}
{% load static %}

{% block demo %}
<!-- Reset Form -->
    <a href="{% url 'run' %}" role="button" class="btn btn-primary btn-lg btn-block" style="border-radius: 0px; background-color:#000000; border-color:#000000"><b>RESET SIMULATION</b></a>

    <!-- Download Form -->
    <div class="container text-center" style="padding-top: 20px; background-color:#000000">
        <form id='fileuploadform' method="post" enctype="multipart/form-data" style="color:white">
                {% csrf_token %}
                {{ form.as_p }}
                <button id="fileuploadbutton" type="submit" style="background-color: #ffffff; border-color: #ffffff; color: #000000">Run</button>
        </form>
    </div>

    <!-- Download Status -->
    <div class="container" style="padding-top: 20px; background-color:#000000">
        <div class="card" style="height: 120px; background-color:#000000">
            {% block progress %}
            {% endblock progress %}
        </div>
    </div>
{% endblock demo %}

runblacklight/templates/runblacklight/progress.html

{% extends "runblacklight/runblacklight.html" %}
{% load static %}
{% block progress %}
    <div class="text-center" style="font-size: 14px; background-color: #000000">
        <div id="progress-bar-message" style="color: white">
            Click the "Run" button
        </div>
    </div>
    <div class='progress-wrapper' style="padding-top: 10px;">
        <div
                id='progress-bar' class='progress-bar progress-bar-striped' role='progressbar'
                style="height:30px; width: 0; border-radius: 5px; color: white">&nbsp;
        </div>
    </div>
    <div id="celery-result"></div>
{% endblock progress %}

{% block progress_bar_js %}
{% if task_id %}
<script type="text/javascript">
    // Progress Bar (JQuery)
    $(function () {
        var progressUrl = "{% url 'celery_progress:task_status' task_id %}";
        CeleryProgressBar.initProgressBar(progressUrl, {})
    });
</script>
{% endif %}
{% endblock progress_bar_js %}

runblacklight/tasks.py

# Celery
from celery import shared_task
# Celery-progress
from celery_progress.backend import ProgressRecorder
# Task imports
from locationofpythoncript.src.pythonscript import * #Population class
import time


# Celery Task
@shared_task(bind=True)
def RunBlackLight(self,
                  individuals_per_population,
                  number_of_parents_mating,
                  number_of_generations,
                  environment_goal_location):
    print('Task started')
    # Create the progress recorder instance
    # which we'll use to update the web page
    progress_recorder = ProgressRecorder(self)

    print('Start')
    population = Population(individuals_per_population, number_of_parents_mating, environment_goal_location)
    for generation in range(number_of_generations):
        print("Generation : ", generation)
        print("Number of individuals : ", len(population.individuallist))
        population.findparents()
        population.reproduce()
        progress_recorder.set_progress(generation + 1, number_of_generations, description="Downloading")
    best = max([population.individuallist[i].fitness for i in range(len(population.individuallist))])
    best.model.save()
    return 'Task Complete'

Большое спасибо за любую помощь, и, пожалуйста, дайте мне знать, если вам понадобится дополнительная информация. <3

Вернуться на верх