Django生成Excel并用芹菜和RabbitMQ保存为对象

发布于 2025-01-31 04:23:38 字数 6657 浏览 1 评论 0原文

我正在使用Django 2.2.5,芹菜5.2.6和Rabbitmq,并且是最后2个新手。

我想生成Excel表并将其存储在新创建的对象中(不下载)中,这是我做了什么:

project/settings.py:

CELERY_RESULT_BACKEND = "django-db"
CELERY_CACHE_BACKEND = "django-cache"
CELERY_BROKER_URL = "amqp://localhost"

CELERY_ACCEPT_CONTENT = ["application/json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
CELERY_TIMEZONE = "Africa/Casablanca"

project/celery.py:

from __future__ import absolute_import, unicode_literals

import os
from celery import Celery

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "verautonoapi.settings")

app = Celery("verautonoapi")

app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()


@app.task(bind=True)
def debug_task(self):
    print("Request: {0!r}".format(self.request))

project/ init .py:

from __future__ import absolute_import, unicode_literals

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ("celery_app",)

app/tasks.py:

from __future__ import absolute_import, unicode_literals
from django.http import HttpResponse, FileResponse
from celery import shared_task
from celery.utils.log import get_task_logger
from time import sleep

from openpyxl import Workbook
from datetime import date
from .models import Dossier, Export

logger = get_task_logger(__name__)


@shared_task(bind=True, track_started=True)
def exp_en_cours(request):
    print("task activated")
    sleep(7)
    print("now starting generating the file")
    dossier_queryset = Dossier.objects.filter(status_admin="Validation OR Requise")
    today = str(date.today())
    response = FileResponse(
        content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    )
    response["Content-Disposition"] = (
        "attachment; filename=" + today + "-dossiers_en_cours.xlsx"
    )
    workbook = Workbook()

    # Get active worksheet/tab
    worksheet = workbook.active
    worksheet.title = "Dossiers en cours"

    # Define the titles for columns
    columns = [
        "Numéro",
        "Créé le",
        "Assurance",
        "Prestataire",
        "Matricule",
    ]

    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,
        ]

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

    Export.objects.create(status="Dossier en cours", file=response)

    return "OK"

app/models.py:

class Export(models.Model):
    STATUS = [
        ("Dossier en cours", "Dossier en cours"),
        ("Dossier validés", "Dossier validés"),
        ("Dossier rejetés", "Dossier rejetés"),
        ("Dossier à clôturer", "Dossier à clôturer"),
        ("Dossier clôturés", "Dossier clôturés"),
        ("Dossier facturés", "Dossier facturés"),
        ("Dossier archivés", "Dossier archivés"),
    ]
    status = models.CharField(max_length=20, choices=STATUS, null=True, blank=True)
    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 = "Rapport Excel"
        verbose_name_plural = "Rapports Excel"

我还安装了django_celery_results 并将其添加到已安装的应用程序中。

当我执行任务时,它会产生以下错误:

This FileResponse instance is not writable

​你能告诉我我在做什么错吗? Excel功能不正确吗?因为我确实进行了更改,以防止它下载(用fileerSponse替换了httpresponse)。还是兔子和芹菜?

更新:问题在于Excel功能,而不是芹菜/兔子。我复制了任务并将其作为常规视图,并获得了相同的错误。

更新2 :我修改了视图以使该文件作为字节流并使用ContentFile将其放入模型中,我能够下载该文件,但是它不能打开:

Views.py:

dossier_queryset = Dossier.objects.filter(status_admin="Validation OR Requise")
    # today = str(date.today())
    # response = FileResponse(
    #     content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    # )
    # response["Content-Disposition"] = (
    #     "attachment; filename=" + today + "-dossiers_en_cours.xlsx"
    # )
    workbook = Workbook()

    # Get active worksheet/tab
    worksheet = workbook.active
    worksheet.title = "Dossiers en cours"

    # Define the titles for columns
    columns = [
        "Numéro",
        "Créé le",
        "Assurance",
        "Prestataire",
        "Matricule",
    ]

    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,
        ]

        # 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

    vworkbook = BytesIO()
    workbook.save(vworkbook)

    content = vworkbook.getvalue()
    b64 = base64.b64encode(content)

    file = (
        "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,"
        + str(b64)
    )

    format, filestr = file.split(";base64,")
    # ext = format.split("/")[-1]
    ext = ".xlsx"

    Export.objects.create(
        status="Dossier en cours",
        file=ContentFile(base64.b64decode(filestr), name="temp." + ext),
    )

    return redirect("homepage")

I am using Django 2.2.5, Celery 5.2.6 and RabbitMQ and I am new to the last 2.

I want to generate an Excel sheet and store it in a FileField inside a newly created object (not download it), and this is what I did:

project/settings.py:

CELERY_RESULT_BACKEND = "django-db"
CELERY_CACHE_BACKEND = "django-cache"
CELERY_BROKER_URL = "amqp://localhost"

CELERY_ACCEPT_CONTENT = ["application/json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
CELERY_TIMEZONE = "Africa/Casablanca"

project/celery.py:

from __future__ import absolute_import, unicode_literals

import os
from celery import Celery

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "verautonoapi.settings")

app = Celery("verautonoapi")

app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()


@app.task(bind=True)
def debug_task(self):
    print("Request: {0!r}".format(self.request))

project/init.py :

from __future__ import absolute_import, unicode_literals

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ("celery_app",)

app/tasks.py:

from __future__ import absolute_import, unicode_literals
from django.http import HttpResponse, FileResponse
from celery import shared_task
from celery.utils.log import get_task_logger
from time import sleep

from openpyxl import Workbook
from datetime import date
from .models import Dossier, Export

logger = get_task_logger(__name__)


@shared_task(bind=True, track_started=True)
def exp_en_cours(request):
    print("task activated")
    sleep(7)
    print("now starting generating the file")
    dossier_queryset = Dossier.objects.filter(status_admin="Validation OR Requise")
    today = str(date.today())
    response = FileResponse(
        content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    )
    response["Content-Disposition"] = (
        "attachment; filename=" + today + "-dossiers_en_cours.xlsx"
    )
    workbook = Workbook()

    # Get active worksheet/tab
    worksheet = workbook.active
    worksheet.title = "Dossiers en cours"

    # Define the titles for columns
    columns = [
        "Numéro",
        "Créé le",
        "Assurance",
        "Prestataire",
        "Matricule",
    ]

    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,
        ]

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

    Export.objects.create(status="Dossier en cours", file=response)

    return "OK"

app/models.py:

class Export(models.Model):
    STATUS = [
        ("Dossier en cours", "Dossier en cours"),
        ("Dossier validés", "Dossier validés"),
        ("Dossier rejetés", "Dossier rejetés"),
        ("Dossier à clôturer", "Dossier à clôturer"),
        ("Dossier clôturés", "Dossier clôturés"),
        ("Dossier facturés", "Dossier facturés"),
        ("Dossier archivés", "Dossier archivés"),
    ]
    status = models.CharField(max_length=20, choices=STATUS, null=True, blank=True)
    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 = "Rapport Excel"
        verbose_name_plural = "Rapports Excel"

I also installed django_celery_results and added it to installed apps.

When I execute the task, it gives the following error:

This FileResponse instance is not writable

enter image description here

I tried on both Windows and Linux (on digitalocean) and I get the same error. Can you tell me what am I doing wrong? is the excel function not correct? Because I did make a change in it to prevent it from downloading (replaced HttpResponse with FileResponse). Or is it RabbitMQ and Celery?

UPDATE: The problem is in the Excel function, not Celery/RabbitMQ. I copied the task and put it as a regular view and I obtain the same error.

UPDATE 2: I modified the view to be able to get the file as a BytesIO stream and use ContentFile to put it inside a model, and I was able to download the file, but it cannot be open:

views.py:

dossier_queryset = Dossier.objects.filter(status_admin="Validation OR Requise")
    # today = str(date.today())
    # response = FileResponse(
    #     content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    # )
    # response["Content-Disposition"] = (
    #     "attachment; filename=" + today + "-dossiers_en_cours.xlsx"
    # )
    workbook = Workbook()

    # Get active worksheet/tab
    worksheet = workbook.active
    worksheet.title = "Dossiers en cours"

    # Define the titles for columns
    columns = [
        "Numéro",
        "Créé le",
        "Assurance",
        "Prestataire",
        "Matricule",
    ]

    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,
        ]

        # 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

    vworkbook = BytesIO()
    workbook.save(vworkbook)

    content = vworkbook.getvalue()
    b64 = base64.b64encode(content)

    file = (
        "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,"
        + str(b64)
    )

    format, filestr = file.split(";base64,")
    # ext = format.split("/")[-1]
    ext = ".xlsx"

    Export.objects.create(
        status="Dossier en cours",
        file=ContentFile(base64.b64decode(filestr), name="temp." + ext),
    )

    return redirect("homepage")

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

潇烟暮雨 2025-02-07 04:23:38

这是解决方案:

from io import BytesIO
from django.core.files.base import ContentFile

vworkbook = BytesIO()
workbook.save(vworkbook)

content = vworkbook.getvalue()

try:
    Export.objects.create(
        status="Dossiers archivés",
        file=ContentFile(
            content, name="dossiers_archivés-" + str(date.today()) + ".xlsx"
        ),
    )
    return "EXP_ARCH OK"
except Exception:
    raise Exception

Here's the solution:

from io import BytesIO
from django.core.files.base import ContentFile

vworkbook = BytesIO()
workbook.save(vworkbook)

content = vworkbook.getvalue()

try:
    Export.objects.create(
        status="Dossiers archivés",
        file=ContentFile(
            content, name="dossiers_archivés-" + str(date.today()) + ".xlsx"
        ),
    )
    return "EXP_ARCH OK"
except Exception:
    raise Exception
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文