Функция экспорта Django и задача «Сельдерей» - PullRequest
0 голосов
/ 13 февраля 2019

Я хотел бы использовать Celery для асинхронного выполнения моей задачи, и у меня возникают некоторые проблемы с этим.Я хотел бы создать файл данных с помощью метода экспорта.

Контекст:

Пользователь может экспортировать результаты поиска в файл .xlsx.Однако есть 2 случая:

  • Поиск содержит менее 70 000 строк.Таким образом, пользователь может напрямую загрузить сгенерированный выходной файл с помощью HttpResponse.
  • Поиск содержит более 70 000 строк.В этом случае файл записывается в папку media благодаря задаче Celery.Таким образом, даже если файл доступен через несколько минут после запроса, пользователь может перемещаться по приложению.Он получит электронное письмо со ссылкой для загрузки сгенерированного файла.

Я работаю над второй частью, и у меня возникают проблемы с заданием задачи Celery.

Мой код:

Эта кнопка используется для выполнения задачи Celery в моем шаблоне HTML :

{% if item_count > 70000 %}
    {% if search_info_str %}
      <a title="Print" href="{% url print_link model='finalproduct' search_info=search_info_str %}" class="button btn btn-default print_items"><span class="glyphicon glyphicon-print"></span></a>
      <a title="Export to Excel" name="export" class="button btn btn-default" href="{% url 'ocabr:cron_export' model=model search_info=search_info_str %}"><span class="glyphicon glyphicon-export"></span></a>
    {% else %}
      <a title="Print" href="{% url print_link model='finalproduct' %}" class="button btn btn-default print_items"><span class="glyphicon glyphicon-print"></span></a>
      <a title="Export to Excel" name="export" class="button btn btn-default" href="{% url 'ocabr:cron_export' model=model %}"><span class="glyphicon glyphicon-export"></span></a>
    {% endif %}
{% endif %}

Эта кнопка вызывает мою функцию благодаряк этому urls.py файлу:

app_name = 'ocabr'

urlpatterns = [

    # URL for the previous HTML template
    url(r'^finalproduct/list/$', FinalProductListView.as_view(), name='finalproduct-list'),

    # URL which calls Celery function
    url(r'^cron_export/(?P<model>[-\w]+)/(?P<search_info>.*)/$', CronExport.as_view(), name='cron_export'),
    url(r'^cron_export/(?P<model>[-\w]+)/$', CronExport.as_view(), name='cron_export'),

]

Затем я определяю в файле cron.py этот класс:

class CronExport(View):

    def export_xls_celery(self, request, *args, **kwargs):
        get_xls_export.delay(**kwargs)
        result = ['success_message', _('You will receive email in few minutes with result of data correction')]
        return render(request, 'ocabr/final_product/final_product_search.html', {result[0]: result[1]})

    def get(self, request, **kwargs):
        self.export_xls_celery(self.request)
        return render(request, 'ocabr/final_product/final_product_search.html')

Функция get_xls_export() вызывает файл tasks.py , который содержит функцию экспорта:

@shared_task(bind=True, time_limit=3600, soft_time_limit=3600)
def get_xls_export(self, model="", search_info=""):
    app_label = 'ocabr'
    its_fp_or_up_product = False
    obsolete = False
    if self.GET.get('obsolete', '') == 'True':
        obsolete = True

    # some code with columns adjust, ..

    book.close()
    output.seek(0)
    name = 'Obsolete' if obsolete else ''
    name += str(model._meta.verbose_name_plural)

    name = name + "_" + str(datetime.now().strftime("%Y_%m_%d_%H_%M_%s")) + '.xlsx'
    default_storage.save(name, output)

    # Send mail to django admin
    receiver = settings.CRON_RECEIVER_MESSAGE
    message = "Hello, \n \n" \
              "This is an automatically generated e-mail. \n \n " \
              "On " + str(
        datetime.now()) + " with export file " + name + " available there : " + settings.MEDIA_ROOT + "/" + name

    try:
        send_mail(
            '[TEST database] Export file generated by OCABR',
            message, 'noreply@test.fr',
            receiver)
    except ConnectionRefusedError as e:
        return ['error_message', _('Error for sending email')]
    return ['success_message', _('Generation of export file is done')]

Выпуск (ы):

Этоэто то, что я получаю, когда нажимаю на кнопку экспорта:

Exception Type: NoReverseMatch at /cron_export/finalproduct/
Exception Value: Reverse for '' not found. '' is not a valid view function or pattern name.

Я немного растерялся, и я не побеждаюсь, чтобы запустить задачу сельдерея

РЕДАКТИРОВАТЬ:

Это мой файл final_product_search.html:

{% extends 'list_crud/base.html' %}
{% load staticfiles %}
{% load i18n %}
{% load crispy_forms_tags %}
{% load is_date %}

{% block content %}
  <h2>{% trans title %}</h2>

  {# Add record function #}
  {% if create_link %}
    <a class="button btn btn-default" href="{% url create_link %}" title="{% trans 'Add a Final Product to database' %}">
      <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
    </a>
  {% endif %}
  {% if item_count > 0 and item_count < 70000 %}
    {% if search_info_str %}
      <a title="Print" href="{% url print_link model='finalproduct' search_info=search_info_str %}" class="button btn btn-default print_items"><span class="glyphicon glyphicon-print"></span></a>
      <a title="Export to Excel" class="button btn btn-default" href="{% url 'ocabr:export-xls' model=model search_info=search_info_str %}"><span class="glyphicon glyphicon-export"></span></a>
    {% else %}
      <a title="Print" href="{% url print_link model='finalproduct' %}" class="button btn btn-default print_items"><span class="glyphicon glyphicon-print"></span></a>
      <a title="Export to Excel" class="button btn btn-default" href="{% url 'ocabr:export-xls' model=model %}"><span class="glyphicon glyphicon-export"></span></a>
    {% endif %}
  {% else %}
    {% if search_info_str %}
      <a title="Print" href="{% url print_link model='finalproduct' search_info=search_info_str %}" class="button btn btn-default print_items"><span class="glyphicon glyphicon-print"></span></a>
      <a title="Export to Excel" name="export" value="export" class="button btn btn-default" href="{% url 'ocabr:cron_export' model=model search_info=search_info_str %}"><span class="glyphicon glyphicon-export"></span></a>
    {% else %}
      <a title="Print" href="{% url print_link model='finalproduct' %}" class="button btn btn-default print_items"><span class="glyphicon glyphicon-print"></span></a>
      <a title="Export to Excel" name="export" value="export" class="button btn btn-default" href="{% url 'ocabr:cron_export' model=model %}"><span class="glyphicon glyphicon-export"></span></a>
    {% endif %}
  {% endif %}

  {% if create_link or export_links %}<br><br>{% endif %}

  {# Search form #}
  <form id="form_search" class="navbar-search" method="GET" action="{{ url }}">

    {% csrf_token %}
    <div class="row">
      <div class="col-md-5">
        {{ search_form.mah|as_crispy_field}}
      </div>
      <div class="col-md-5">
        {{ search_form.releasing_body|as_crispy_field }}
      </div>
    </div>
    <div class="row">
      <div class="col-md-5">
        {{ search_form.trade_name|as_crispy_field}}
      </div>
      <div class="col-md-5">
        {{ search_form.member_state|as_crispy_field }}
      </div>
    </div>
    <div class="row">
      <div class="col-md-5">
        {{ search_form.pheur_name|as_crispy_field}}
      </div>
      <div class="col-md-5">
        {{ search_form.decision|as_crispy_field }}
      </div>
    </div>
    <div class="row">
      <div class="col-md-5">
        {{ search_form.final_bulk_num|as_crispy_field}}
      </div>
      <div class="col-md-5">
        {{ search_form.domain|as_crispy_field }}
      </div>
    </div>
    <div class="row">
      <div class="col-md-5">
        {{ search_form.manufacturer_name|as_crispy_field}}
      </div>
      <div class="col-md-5">
        {{ search_form.date_from|as_crispy_field }}
      </div>
    </div>
    <div class="row">
      <div class="col-md-5">
        {{ search_form.name|as_crispy_field}}
      </div>
      <div class="col-md-5">
        {{ search_form.date_to|as_crispy_field }}
      </div>
    </div>
    <div class="row">
      <div class="col-md-5">
        {{ search_form.eu_cert_n|as_crispy_field}}
      </div>
      <div class="col-md-5">
        {{ search_form.status|as_crispy_field }}
      </div>
    </div>
    <div class="row">
      <div class="col-md-5">
        {{ search_form.upstream_code_product|as_crispy_field}}
      </div>
       <div class="col-md-5">
        <label for="id_certificate_nullified" class="control-label ">
                Certificate nullified
        </label>
        {{ search_form.certificate_nullified }}
      </div>
    </div>
    <br>

    <input type="submit" class="btn btn-default" value="{% trans 'Search' %}" />
    <input type="button" class="btn btn-default" name="clear" value="Reset" onclick="clearForm(this.form);">

  </form>

  <br><br>

  {% if query_string %}
    <p class="text-info">Search results for <strong>{{ query_string }}</strong></p>
  {% endif %}

  {% block filter_bar %}
  {% endblock %}
  {# List of results #}
  {% if object_list %}
    {% with total=item_count %}
      {{ total }} record{{ total|pluralize }} / {{ total_records }}
    {% endwith %}
    {% block list_table %}
      <table id="table_{{ model }}" class="table table-bordered table-striped table-condensed table_model" style="table-layout:fixed; word-wrap: break-word;">
        {#    Headers #}
        <thead>
        <tr>
          {% for item in fields %}
            <th id="head_{{ item }}">
              {% if sort_by == item %}
                {% if order == 'asc' %}
                  <a href="?{% url_replace request 'sorting' '~'|add:item %}">
                    {{ labels|get_key:item }} </a>
                  <span class="glyphicon glyphicon-chevron-up align-right pull-right" aria-hidden="true"></span>
                {% else %}
                  <a href="?{% url_replace request 'sorting' item %}">
                    {{ labels|get_key:item }} </a>
                  <span class="glyphicon glyphicon-chevron-down align-right pull-right" aria-hidden="true"></span>
                {% endif %}
              {% else %}
                <a href="?{% url_replace request 'sorting' item %}">
                  {{ labels|get_key:item }}
                </a>
              {% endif %}
            </th>
          {% endfor %}
          {% block extra_field_header %}{% endblock %}
          {% if update_link or delete_link or view_link %}
              <th class="action_list"></th>
          {% endif %}
        </tr>
        </thead>
        {#    Rows #}
        <tbody>
        {% for item in object_list %}
          <tr id="tr_{{ item.pk }}">
            {% block default_values %}
              {% with values=item|get_ordered_values:fields %}
                {% for value in values %}
                  <td>
                    {% if value is not none %}
                      {{ value }}
                    {% endif %}
                  </td>
                {% endfor %}
              {% endwith %}
            {% endblock %}
            {% block extra_field_body %}{% endblock %}

            {% block actions %}
              {% if update_link or delete_link or view_link %}
                <td>
                  {% if view_link %}
                    <a class="btn btn-default btn-xs" href="{% url view_link pk=item.pk %}" title="View">
                      <span class="glyphicon glyphicon-eye-open"></span>
                    </a>
                  {% endif %}
                  {% if update_link %}
                    <a class="btn btn-default btn-xs" href="{% url update_link pk=item.pk %}" title="Edit">
                      <span class="glyphicon glyphicon-pencil"></span>
                    </a>
                  {% endif %}
                  {% if delete_link %}
                    <a class="btn btn-default btn-xs" href="{% url delete_link pk=item.pk %}" title="Delete">
                      <span class="glyphicon glyphicon-trash"></span>
                    </a>
                  {% endif %}
                </td>
              {% endif %}
            {% endblock %}
          </tr>
        {% endfor %}
        </tbody>
      </table>
    {% endblock %}
  {% else %}
    <div class="alert alert-warning" role="alert">{% trans "Nothing found" %}</div>
  {% endif %}

  {# Pagination #}
  {% include 'list_crud/pagination.html' %}

  {% block extra_html %}
  {% endblock %}

{% endblock %}

У меня также возникает проблема с сельдереем:

[2019-02-13 16:15:48,085: ERROR/ForkPoolWorker-4] Task ocabr.tasks.get_xls_export[85cb15ea-1443-4ce6-8832-ce5e8a14a4ca] raised unexpected: AttributeError("'Context' object has no attribute 'GET'",)
Traceback (most recent call last):
  File "/home/.pyenv/versions/3.6.2/envs/ocabr/lib/python3.6/site-packages/celery/app/trace.py", line 382, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/home/.pyenv/versions/3.6.2/envs/ocabr/lib/python3.6/site-packages/celery/app/trace.py", line 641, in __protected_call__
    return self.run(*args, **kwargs)
  File "/home/Bureau/Projets/ocabr/src/ocabr/tasks.py", line 26, in get_xls_export
    if self.GET.get('obsolete', '') == 'True':
AttributeError: 'Context' object has no attribute 'GET'

1 Ответ

0 голосов
/ 13 февраля 2019

Вы не передаете kwargs из обработчика get в свой метод экспорта xlsx:

def get(self, request, **kwargs):
    self.export_xls_celery(request, **kwargs)

Кроме того, кажется, что вы хотите получить доступ к атрибутам экземпляра запроса в своей задаче.К сожалению, это невозможно - лучший способ сделать это - просто передать необходимые параметры в качестве дополнительных параметров.Итак, в задании:

@shared_task(bind=True, time_limit=3600, soft_time_limit=3600)
def get_xls_export(self, model="", search_info="", query_params=None):
    app_label = 'app'
    its_fp_or_up_product = False
    query_params = query_params or {}
    obsolete = False
    if query_params.get('obsolete', '') == 'True':
        obsolete = True

И в вызове задания:

def export_xls_celery(self, request, *args, **kwargs):
    get_xls_export.delay(query_params=request.GET, **kwargs)
    result = ['success_message', _('You will receive email in few minutes with result of data correction')]
    return render(request, 'app/final_product/final_product_search.html', {result[0]: result[1]})
...