Hello guys,

I'm working on my Django project and I need to integrate Celery to do 
something.
I'm using Django 1.11.20.

*My context :*

I have a template page with a search form. Once you did a search, it 
returns a table with search results.
It's possible to export to .xls format with/without search.

There are 2 possibilities :


   - If user wants to export *less than 70.000 objects*, the export creates 
   an HttpResponse with download link.
   - If user wants to export *more than 70.000 objects*, a Celery task has 
   to be initiate, it creates the file, save it into the MEDIA folder and user 
   gets an email with expirable download link.

I'm trying to implement this second part.

The check is done on template page with number of results according to the 
search part. 
If < 70.000 objects, export button calls *export_xls()* function, else it 
calls the Celery task 

*get_xls_export()HTML Template file :*

{% 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 %}

This part calls the right function depending on search result.

*Case 1 : Less than 70.000 objects*

The view associated to the previous HTML template is :

class FinalProductListView(PermissionRequiredMixin, EdqmListSearchView):

    model = FinalProduct
    page_title = _('Final Products')
    default_sort_params = ('manufacturer_name', 'asc')
    sort_params = ['pk']

    create_link = 'ocabr:finalproduct-add'
    update_link = 'ocabr:finalproduct-edit'
    delete_link = 'ocabr:finalproduct-delete'
    view_link = 'ocabr:finalproduct-view'
    print_link = 'ocabr:print-object'

    permission_required = 'ocabr.view_finalproduct'

    template_name = 'ocabr/final_product/final_product_search.html'

    paginate_by = getattr(PaginationMixin, 'paginate_by', None)
    headers = {
        'releasing_body__short_name': 'Releasing Body/Institution',
        'is_withdrawn': 'Is nullified',
    }

    # Configuration of fields
    fields = ['manufacturer_name', 'releasing_body__short_name', 
'product_trade_name', 'pheur_name', 'name', 'final_bulk',
              'decision', 'date_decision', 'is_withdrawn', 'member_state']

    def get_queryset(self):
        queryset = super(FinalProductListView, self).get_queryset()

        date_format = settings.USER_DATE_FORMAT
        # Add filter from search form.
        if 'mah' in self.request.GET:
            if self.request.GET['mah'] != '':
                queryset = 
queryset.filter(mah__icontains=self.request.GET['mah'].strip())
        if 'releasing_body' in self.request.GET:
            if self.request.GET['releasing_body']:
                queryset = 
queryset.filter(releasing_body__id=int(self.request.GET['releasing_body']))
        if 'trade_name' in self.request.GET:
            if self.request.GET['trade_name'] != '':
                queryset = 
queryset.filter(product_trade_name__icontains=self.request.GET['trade_name'].strip())
        if 'member_state' in self.request.GET:
            if self.request.GET['member_state']:
                queryset = 
queryset.filter(member_state__id=int(self.request.GET['member_state']))
        if 'pheur_name' in self.request.GET:
            if self.request.GET['pheur_name'] != '':
                queryset = 
queryset.filter(pheur_name__id=self.request.GET['pheur_name'])
        if 'decision' in self.request.GET:
            if self.request.GET['decision']:
                queryset = 
queryset.filter(decision__id=int(self.request.GET['decision']))
        if 'final_bulk_num' in self.request.GET:
            if self.request.GET['final_bulk_num'] != '':
                queryset = 
queryset.filter(final_bulk__icontains=self.request.GET['final_bulk_num'].strip())
        if 'domain' in self.request.GET:
            if self.request.GET['domain']:
                queryset = 
queryset.filter(domain__id=int(self.request.GET['domain']))
        if 'manufacturer_name' in self.request.GET:
            if self.request.GET['manufacturer_name'] != '':
                queryset = 
queryset.filter(manufacturer_name__id=self.request.GET['manufacturer_name'])
        if 'date_from' in self.request.GET and 'date_to' in 
self.request.GET:
            if self.request.GET['date_from'] and not 
self.request.GET['date_to']:
                try:
                    queryset = 
queryset.filter(date_decision__gte=Utils.filter_date_format(self.request.GET['date_from'],
 
date_format))
                except:
                    pass
            if not self.request.GET['date_from'] and 
self.request.GET['date_to']:
                try:
                    queryset = 
queryset.filter(date_decision__lte=Utils.filter_date_format(self.request.GET['date_to'],
 
date_format))
                except:
                    pass
            if self.request.GET['date_from'] and 
self.request.GET['date_to']:
                try:
                    queryset = 
queryset.filter(date_decision__range=(Utils.filter_date_format(self.request.GET['date_from'],
 
date_format), Utils.filter_date_format(self.request.GET['date_to'], 
date_format)))
                except:
                    pass

        if 'name' in self.request.GET:
            if self.request.GET['name'] != '':
                queryset = 
queryset.filter(name__icontains=self.request.GET['name'].strip())
        if 'eu_cert_n' in self.request.GET:
            if self.request.GET['eu_cert_n'] != '':
                queryset = 
queryset.filter(eu_cert_n__icontains=self.request.GET['eu_cert_n'].strip())
        if 'status' in self.request.GET:
            if self.request.GET['status'] != '':
                queryset = 
queryset.filter(status_final_product__icontains=self.request.GET['status'].strip())
        if 'upstream_code_product' in self.request.GET:
            if self.request.GET['upstream_code_product'] != '':
                queryset = 
queryset.filter(upstream_products__name__icontains=self.request.GET['upstream_code_product'].strip())
        if 'certificate_nullified' in self.request.GET:
            if self.request.GET['certificate_nullified'] != '' and 
self.request.GET['certificate_nullified'] != 'both':
                queryset = queryset.filter(
                    is_withdrawn__icontains='t' if 
self.request.GET['certificate_nullified'] == 'True' else 'f')

        return queryset

    def get_context_data(self, **kwargs):
        """ Update the context data with query_string."""
        context = super(EdqmListSearchView, self).get_context_data(**kwargs)
        context['total_records'] = self.get_total_count()
        # Create or Update
        if 'certificate_nullified' in self.request.GET:
            form = FinalProductSearchForm(data=self.request.GET, 
user=self.request.user)
        else:
            form = FinalProductSearchForm(user=self.request.user, 
initial={'certificate_nullified': 'both'})

        if not self.page_title:
            self.page_title = _("List of ") + 
str(self.model._meta.verbose_name_plural)

        # Check perms
        if 'add' in self.model._meta.default_permissions and not 
self.request.user.has_perm(self.model._meta.app_label + '.add_' + 
self.model._meta.model_name):
            self.create_link = None
        if 'change' in self.model._meta.default_permissions and not 
self.request.user.has_perm(self.model._meta.app_label + '.change_' + 
self.model._meta.model_name):
            self.update_link = None
        if 'delete' in self.model._meta.default_permissions and not 
self.request.user.has_perm(self.model._meta.app_label + '.delete_' + 
self.model._meta.model_name):
            self.delete_link = None

        # Send id to print info or excel.
        search_text = ""
        already_sorted = False
        if 'csrfmiddlewaretoken' in self.request.GET:
            # Log search form
            Utils.log_search_form(self, 'Final product')

            search_form_info = [str(info) + '=' + self.request.GET[info] 
for info in self.request.GET if self.request.GET[info] != '' and info != 
'csrfmiddlewaretoken' and self.request.GET[info] != 'both' and info != 
'page']
            is_date_to = False
            is_date_from = False
            for date_info in search_form_info:
                if 'date_from' in date_info:
                    is_date_from = True
                if 'date_to' in date_info:
                    is_date_to = True
            is_range_date = True if is_date_to and is_date_from else False
            d1 = ''
            d2 = ''
            for compute_txt in search_form_info:

                # Change filter name.
                if 'mah' in compute_txt:
                    compute_txt = compute_txt.replace('mah', 
'mah__contains')
                elif 'sorting' in compute_txt:
                    already_sorted = True
                elif 'releasing_body' in compute_txt:
                    compute_txt = compute_txt.replace('releasing_body', 
'releasing_body__id')
                elif 'trade_name' in compute_txt:
                    compute_txt = compute_txt.replace('trade_name', 
'product_trade_name__icontains')
                elif 'member_state' in compute_txt:
                    compute_txt = compute_txt.replace('member_state', 
'member_state__id')
                elif 'pheur_name' in compute_txt:
                    compute_txt = compute_txt.replace('pheur_name', 
'pheur_name__id')
                elif 'decision' in compute_txt:
                    compute_txt = compute_txt.replace('decision', 
'decision__id')
                elif 'final_bulk_num' in compute_txt:
                    compute_txt = compute_txt.replace('final_bulk_num', 
'final_bulk__icontains')
                elif 'domain' in compute_txt:
                    compute_txt = compute_txt.replace('domain', 
'domain__id')
                elif 'manufacturer_name' in compute_txt:
                    compute_txt = compute_txt.replace('manufacturer_name', 
'manufacturer_name__id')
                elif 'name' in compute_txt:
                    compute_txt = compute_txt.replace('name', 
'name__icontains')
                elif 'eu_cert_n' in compute_txt:
                    compute_txt = compute_txt.replace('eu_cert_n', 
'eu_cert_n__icontains')
                elif 'status' in compute_txt:
                    compute_txt = compute_txt.replace('status', 
'status_final_product__icontains')
                elif 'upstream_code_product' in compute_txt:
                    compute_txt = 
compute_txt.replace('upstream_code_product', 
'upstream_products__name__icontains')
                elif 'certificate_nullified' in compute_txt:
                    compute_txt = 
compute_txt.replace('certificate_nullified', 'is_withdrawn__icontains')
                    if 'True' in compute_txt:
                        compute_txt = compute_txt.replace('True', 't')
                    else:
                        compute_txt = compute_txt.replace('False', 'f')

                date_format = settings.USER_DATE_FORMAT
                if not is_range_date:
                    if 'date_from' in compute_txt and is_date_from and not 
is_date_to:
                        text_to_replace = compute_txt.split('=')[1]
                        try:
                            text_to_replace = Utils.change_date_format(
                                datetime.datetime.strptime(text_to_replace, 
date_format),
                                date_format).replace('/', '-')
                        except ValueError:
                            pass
                        compute_txt = 
compute_txt.replace(compute_txt.split('=')[1], text_to_replace)
                        compute_txt = compute_txt.replace('date_from', 
'date_decision__gte')
                    if 'date_to' in compute_txt and not is_date_from and 
is_date_to:
                        text_to_replace = compute_txt.split('=')[1]
                        try:
                            text_to_replace = 
Utils.change_date_format(datetime.datetime.strptime(text_to_replace, 
date_format), date_format).replace('/','-')
                        except ValueError:
                            pass
                        compute_txt = 
compute_txt.replace(compute_txt.split('=')[1], text_to_replace)
                        compute_txt = compute_txt.replace('date_to', 
'date_decision__lte')
                else:
                    if 'date_from' in compute_txt:
                        text_to_replace = compute_txt.split('=')[1]
                        try:
                            text_to_replace = Utils.change_date_format(
                                datetime.datetime.strptime(text_to_replace, 
date_format),
                                date_format).replace('/', '-')
                        except ValueError:
                            pass
                        d1 = 
text_to_replace.replace(compute_txt.split('=')[1], text_to_replace)
                        continue
                    if 'date_to' in compute_txt:
                        text_to_replace = compute_txt.split('=')[1]
                        try:
                            text_to_replace = 
Utils.change_date_format(datetime.datetime.strptime(text_to_replace, 
date_format), date_format).replace('/','-')
                        except ValueError:
                            pass
                        d2 = 
text_to_replace.replace(compute_txt.split('=')[1], text_to_replace)
                        continue
                if d1 and d2:
                    search_text += 'date_decision__gte=' + d1 + 
';date_decision__lte=' + d2 + ';'
                    d1 = ''
                    d2 = ''

                search_text += compute_txt + ';'
            if search_text:
                search_text = search_text[:-1]

        # Sorting patch
        if not already_sorted:
            if 'sorting' in self.request.GET and search_text == '':
                search_text = 'sorting=' + self.request.GET['sorting']

        context.update({
            'title': self.page_title,
            'fields': self.fields,
            'labels': self.get_labels(),
            'view_link': self.view_link,
            'create_link': self.create_link,
            'update_link': self.update_link,
            'delete_link': self.delete_link,
            'search_link': self.search_link,
            'export_links': self.export_links,
            'model': str(self.model._meta.model_name),
            'search_form': form,
            'item_count': self.object_list.count(),
            'print_link': self.print_link,
            'search_info_str': search_text,
            'limit_print_search': settings.LIMIT_PRINT_SEARCH

        })

        if not context.get('is_paginated', False):
            return context

        # Update paginator range
        paginator = context.get('paginator')
        num_pages = paginator.num_pages

        current_page = context.get('page_obj')
        page_no = current_page.number

        if num_pages <= 10 or page_no <= 5:  # case 1 and 2
            pages = [x for x in range(1, min(num_pages + 1, 11))]
        elif page_no > num_pages - 4:  # case 4
            pages = [x for x in range(num_pages - 9, num_pages + 1)]
        else:  # case 3
            pages = [x for x in range(page_no - 5, page_no + 5)]

        context.update({'pages': pages})
        return context


In my template, I have a function export_xls() called when user press 
"Export" button :

class ExportOCABR(View):

    def export_xls(self, model="", search_info=""):
        app_label = 'ocabr'
        its_fp_or_up_product = False
        obsolete = False
        if self.GET.get('obsolete', '') == 'True':
            obsolete = True

        default_sorting_key = ''
        show_date_format = settings.USER_DATE_FORMAT
        if model == "finalproduct" or model == "upstreamproduct":
            its_fp_or_up_product = True
            default_sorting_key = 'manufacturer_name' if model == 
"finalproduct" else 'releasing_body__short_name'
        if model == "releasebodyinstitution":
            default_sorting_key = 'name'

        model = apps.get_model(app_label=app_label, model_name=model)

        # create a workbook in memory
        output = io.BytesIO()

        book = Workbook(output, {'constant_memory': True})
        sheet = book.add_worksheet('Page 1')
        # Sheet header, first row
        row_num = 0

        columns = model.export_field_excel()
        rows_width_max = {}
        bold_format = book.add_format({'bold': True})
        max_col_width = []

        for col_num in range(len(columns)):
            rows_width_max[col_num] = columns[col_num].__len__()
            sheet.write(row_num, col_num, columns[col_num], bold_format)
            max_col_width.append(len(columns[col_num]) if 
len(columns[col_num]) > 10 else 10)

        default_sorting = True
        sorting_key = ''
        # Define search get all object or sorted value.
        if search_info != '':
            create_filters = search_info.split(';')
            if create_filters.__len__() == 1:
                if 'sorting' in create_filters[0]:
                    default_sorting = False
                    sorting_key = 
create_filters[0].split('=')[1].replace('~', '-')
                    search_info = ''
            else:
                for criter in create_filters:
                    if 'sorting' in criter:
                        default_sorting = False
                        sorting_key = criter.split('=')[1].replace('~', '-')
                        search_info = search_info.replace(criter, "")
                        search_info = search_info[:-1]

        objects = model.objects.all()

        if not its_fp_or_up_product:
            if obsolete:
                objects = objects.obsolete()
            else:
                objects = objects.active()

        if sorting_key:
            objects = objects.order_by(sorting_key, 'pk')

        if default_sorting:
            objects = objects.order_by(default_sorting_key, 'pk')

        if search_info != '':
            create_filters = search_info.split(';')
            for search_filter in create_filters:
                search_filter = search_filter.split('=')
                try:
                    if search_filter[1]:
                        objects = objects.filter(**{search_filter[0]: 
search_filter[1]})
                except:
                    # Crud patch search
                    if search_filter[0] == 'q':
                        search_info = search_info.replace('q=', '')
                    objects = objects.filter(get_query(search_info, 
model.get_xls_values_list()))

        rows = objects.values_list(*model.get_xls_values_list())
        # Part which export file directly if less than 70.000 entries else 
create file with download link

        for row in rows:
            row_num += 1
            for col_num in range(len(row)):
                # Patch True False for boolean field
                is_bool = False
                if type(row[col_num]) is bool:
                    is_bool = True

                if col_num in model.get_date_field_number():
                    if row[col_num]:
                        sheet.write(row_num, col_num, 
row[col_num].strftime(show_date_format))
                else:
                    if is_bool:
                        sheet.write(row_num, col_num, 'True' if 
row[col_num] else 'False')
                    else:
                        sheet.write(row_num, col_num, row[col_num])

                if len(str(row[col_num])) > max_col_width[col_num]:
                    max_col_width[col_num] = len(str(row[col_num]))

        # AutoFit col
        for col_num in range(len(columns)):
            sheet.set_column(col_num, col_num, max_col_width[col_num] + 1)

        book.close()

        # construct response
        output.seek(0)
        name = 'Obsolete' if obsolete else ''
        name += str(model._meta.verbose_name_plural)
        response = HttpResponse(output.read(),
                                
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
        response['Content-Disposition'] = 'attachment; filename="' + name + 
'.xlsx"'

        return response

The url file looks like :

app_name = 'ocabr'


urlpatterns = [

# Final Product
    url(r'^finalproduct/list/$', FinalProductListView.as_view(), name=
'finalproduct-list'),

# Excel export list
    url(r'export/xls/(?P<model>[-\w]+)/$', ExportOCABR.export_xls, name=
'export-xls'),
    url(r'export/xls/(?P<model>[-\w]+)/(?P<search_info>.*)/$', ExportOCABR.
export_xls, name='export-xls'),
]


*Case 2 : More than 70.000 objects*

I have a tasks.py file :

@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

    default_sorting_key = ''
    show_date_format = settings.USER_DATE_FORMAT
    if model == "finalproduct" or model == "upstreamproduct":
        its_fp_or_up_product = True
        default_sorting_key = 'manufacturer_name' if model == 
"finalproduct" else 'releasing_body__short_name'
    if model == "releasebodyinstitution":
        default_sorting_key = 'name'

    model = apps.get_model(app_label=app_label, model_name=model)

    # create a workbook in memory
    output = io.BytesIO()

    book = Workbook(output, {'constant_memory': True})
    sheet = book.add_worksheet('Page 1')
    # Sheet header, first row
    row_num = 0

    columns = model.export_field_excel()
    rows_width_max = {}
    bold_format = book.add_format({'bold': True})
    max_col_width = []

    for col_num in range(len(columns)):
        rows_width_max[col_num] = columns[col_num].__len__()
        sheet.write(row_num, col_num, columns[col_num], bold_format)
        max_col_width.append(len(columns[col_num]) if len(columns[col_num]) 
> 10 else 10)

    default_sorting = True
    sorting_key = ''
    # Define search get all object or sorted value.
    if search_info != '':
        create_filters = search_info.split(';')
        if create_filters.__len__() == 1:
            if 'sorting' in create_filters[0]:
                default_sorting = False
                sorting_key = create_filters[0].split('=')[1].replace('~', 
'-')
                search_info = ''
        else:
            for criter in create_filters:
                if 'sorting' in criter:
                    default_sorting = False
                    sorting_key = criter.split('=')[1].replace('~', '-')
                    search_info = search_info.replace(criter, "")
                    search_info = search_info[:-1]

    objects = model.objects.all()

    if not its_fp_or_up_product:
        if obsolete:
            objects = objects.obsolete()
        else:
            objects = objects.active()

    if sorting_key:
        objects = objects.order_by(sorting_key, 'pk')

    if default_sorting:
        objects = objects.order_by(default_sorting_key, 'pk')

    if search_info != '':
        create_filters = search_info.split(';')
        for search_filter in create_filters:
            search_filter = search_filter.split('=')
            try:
                if search_filter[1]:
                    objects = objects.filter(**{search_filter[0]: 
search_filter[1]})
            except:
                # Crud patch search
                if search_filter[0] == 'q':
                    search_info = search_info.replace('q=', '')
                objects = objects.filter(get_query(search_info, 
model.get_xls_values_list()))

    rows = objects.values_list(*model.get_xls_values_list())
    # Part which export file directly if less than 70.000 entries else 
create file with download link

    for row in rows:
        row_num += 1
        for col_num in range(len(row)):
            # Patch True False for boolean field
            is_bool = False
            if type(row[col_num]) is bool:
                is_bool = True

            if col_num in model.get_date_field_number():
                if row[col_num]:
                    sheet.write(row_num, col_num, 
row[col_num].strftime(show_date_format))
            else:
                if is_bool:
                    sheet.write(row_num, col_num, 'True' if row[col_num] 
else 'False')
                else:
                    sheet.write(row_num, col_num, row[col_num])

            if len(str(row[col_num])) > max_col_width[col_num]:
                max_col_width[col_num] = len(str(row[col_num]))

    # AutoFit col
    for col_num in range(len(columns)):
        sheet.set_column(col_num, col_num, max_col_width[col_num] + 1)

    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, 'nore...@test.fr',
            receiver)
    except ConnectionRefusedError as e:
        return ['error_message', _('Error for sending email')]
    return ['success_message', _('Generation of export file is done')]

It's similar than *export_xls()* function, but the end is a bit different. 
Because I don't create an HttpResponse, but I save the file in MEDIA folder 
and send an email to user.

*My question :*

How and where I can create a *get_xls_export.delay()* when user click on 
the export button for more than 70.000 objects ? How I can call it from 
urls.py file ?

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/da79f356-6164-4c27-bfa2-a918d474a22e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to