0

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.

1
  • Should I replace HttpReponse by FileResponse? from django.http import FileResponse, and then save to a path? Commented May 16, 2022 at 16:44

1 Answer 1

0

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 :)

Sign up to request clarification or add additional context in comments.

5 Comments

I don't want to download the file right away, because I'll be using this export function as a celery task that will be run in the background, once it's done, it will create a new object in the Export table and put a file in a directory, where the user can go download them on a given page.
Then you can just put the file you created in your 'file' FileField and it should work. And when downloading you just download the file through its filepath
Yes but the question is how do I save the Fileresponse as a file? Do I just assign it as exportobj.file = response then exportobj.save() ?
Yes, by doing this you'll be creating your instance of Export. I just don't understand why you want to return a response, can't you just create and save your file ? Since you're running this code with Celery and not in your views, you don't need to return a response nor take a request as a parameter.
I know! This code is the normal one (without celery), now I don't want to download it, so I want to replace return response with a file saving. I just wanted to make sure that Ithe response is a file instance that I can assign to a FileField and save an object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.