broinjc
broinjc

Reputation: 2699

Write reusable view

I have this function that takes a QuerySet and renders a CSV. I would like to write a view that renders a template with options to download different CSV files (Basically for anything defined in models.py)

# Exports CSV file using a QuerySet
def export(qs, fields=None, file_name='data'):
    model = qs.model
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename={0}-{1}.csv'.format(file_name, str(datetime.date.today()))
    writer = csv.writer(response)
    # Write headers to CSV file
    if fields:
        headers = fields
    else:
        headers = []
        for field in model._meta.fields:
            headers.append(field.name)
    writer.writerow(headers)
    # Write data to CSV file
    for obj in qs:
        row = []
        for field in headers:
            if field in headers:
                val = getattr(obj, field)
                if callable(val):
                    val = val()
                row.append(val)
        writer.writerow(row)
    # Return CSV file to browser as download
    return response

Currently I am writing a NON-reusable view:

def csv_of_surveys(request):
    r = export(Survey.objects.all(), file_name='surveys')
    return r

What can I do? My only idea was to send a code over and write a switch statement, so

{% url "csv_of" 0 %}
{% url "csv_of" 1 %}
{% url "csv_of" 2 %}
{% url "csv_of" 3 %}

Where 0, 1, 2 and 3 would correspond to downloading different things.

The new view would look something like:

def csv_of(request, code):
    if code == 0:
        r = export(Survey.objects.all(), file_name='surveys')
        return r
    elif code == 1:
        r = export(User.objects.all(), file_name='teachers')
        return r
    elif code == 2:
        r = export(Student.objects.all(), file_name='students')
        return r
    # ...
    else:
        return HttpResponseRedirect('/')

Is there a better way?

Upvotes: 1

Views: 54

Answers (1)

ubadub
ubadub

Reputation: 3880

Create a dictionary that maps the given code to the associated object, then reduce all the if statements you have into one if. For the file name, it looks like you're doing the same thing each time, which is pluralizing and lowercasing it, in which case you should set it in model._meta.verbose_name_plural, then access that when you need it:

file_codes = {0:Survey,1:User...}
def csv_of(request, code):
  if int(code) in file_codes.keys():
    obj = file_codes[int(code)]
    return export(obj.objects.all(), file_name = obj._meta.verbose_name_plural.title())
  else:
    return HttpResponseRedirect('/')

Upvotes: 1

Related Questions