'Django Export Excel Save File In Server (Celery + RabbitMQ)

I have a Django view that exports an Excel File and prompts a Download Dialog when the file is ready. I am installing Celery and RabbitMQ to make this task a background task. This means that the excel file will not be prompted to be downloaded, but I would like to save it somewhere in the machine, so the user can later go to a page and download it.

Example:

Go to List page -> Click on Export -> Get a message: "Your file will be ready soon" -> User goes to "Downloads" Page -> Finds the file in a list -> Clicks to download.

So the process for me now is to create a new model:

class Export(models.Model):
    dossier = models.ForeignKey(
        Dossier,
        related_name="Export",
        on_delete=models.CASCADE,
        default=None,
        editable=False,
    )
    timestamp = models.DateTimeField(auto_now_add=True, editable=False)
    file = models.FileField(
        upload_to=get_rapport_filename, verbose_name="Fichiers Excel"
    )

    def __str__(self):
        return "%s - %s" % (self.dossier, self.timestamp)

    class Meta:
        verbose_name = "Excel Report"
        verbose_name_plural = "Excel Reports"

And this is the code I have that generates the Excel file:

@shared_task
def export_dossiers_factures(request):
    dossier_queryset = Dossier.objects.filter(status_admin="Facturé")
    today = str(date.today())
    response = HttpResponse(
        content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    )
    response["Content-Disposition"] = (
        "attachment; filename=" + today + "-dossiers_factures.xlsx"
    )
    workbook = Workbook()

    # Get active worksheet/tab
    worksheet = workbook.active
    worksheet.title = "Dossiers facturés"

    # Define the titles for columns
    columns = [
        "Numéro",
        "Créé le",
        "Assurance",
        "Prestataire",
        "Matricule",
        "N° de BL",
        "N° de Facture",
        "Date de mise à jour",
    ]

    row_num = 1

    # Assign the titles for each cell of the header
    for col_num, column_title in enumerate(columns, 1):
        cell = worksheet.cell(row=row_num, column=col_num)
        cell.value = column_title

    # Iterate through all movies
    for dossier in dossier_queryset:
        row_num += 1

        # Define the data for each cell in the row
        row = [
            dossier.numero,
            str(dossier.date_posted)[:10],
            dossier.assurance.name,
            dossier.created_by.user.username,
            dossier.matricule,
            dossier.bl,
            dossier.invoice,
            str(dossier.date_updated)[:10],
        ]

        # Assign the data for each cell of the row
        for col_num, cell_value in enumerate(row, 1):
            cell = worksheet.cell(row=row_num, column=col_num)
            cell.value = cell_value

    workbook.save(response)

    return response

I want to replace return response, by soomething that saves the file somewhere and creates an object pointing to that file.



Solution 1:[1]

I did approximately the same thing as you, and I did it that way :

First, I create my file and write whatever I want in it, so in your case, something like that :

filepath = f'/filepath_to/{today}-dossiers_factures.xlsx'
workbook = Workbook(filepath)
... # Whatever you want to do with your file

And once you have your file created, you juste have to get your file with a FileReponse :

response = FileResponse(open(filepath, 'rb'))
response['Content-Disposition'] = f"attachment; filename={today}-dossiers_factures.xlsx"
return response

So in this case, you download the file which has been stored in the place you put it in, instead of just creating a file and returning it.

Hope I understood what you wanted and that answers your question :)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1