Handle uploaded file in Django

Here is the case: we want to import data to our database from a data file (csv, excel...). For example, we want to import user accounts from a csv file:

data.csv:


remember to save its format as following:


- We have the Account model which data will be imported from the above csv file:  my_app.models.py:



from django.db import models

class Account(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
unique_id = models.CharField(max_length=255)

last_modified = models.DateTimeField(auto_now = True)
first_created = models.DateTimeField(auto_now_add = True)

def __unicode__(self):

return "%s %s" % (self.first_name, self.last_name)


- Define a form for uploading csv file in my_app.forms.py:

from django import forms

class UploadFileForm(forms.Form):
file  = forms.FileField(label='')

- Utils functions in my_app.utils.py:
...

from my_app import models
import csv

def handle_uploaded_file(file, valid_fields_method, record_creation_function):

file.seek(0)

# !importtant
# csv file must be encoded in UTF-8
sniffdialect = csv.Sniffer().sniff(file.read(10000), delimiters='\t,;')
file.seek(0)

#print sniffdialect.fieldnames
data = csv.DictReader(file, dialect=sniffdialect)

if not valid_fields_method(data.fieldnames):
return False, -1

result, rows_error = record_creation_function(data)

return result, rows_error


def account_valid_fields(field_names):
required_fields = ('first_name', 'last_name', 'unique_id')

for field in required_fields:
if field not in field_names:
return False
return True


def create_account_in_db(dict_data):
list_data = []
result = False
rows_error = 0
for record in dict_data:
first_name = record['first_name']
last_name = record['last_name']
unique_id = record['unique_id']

account = models.Account(first_name=first_name,\
last_name=last_name,\
unique_id=unique_id,\
grad_year=grad_year,\
account_type=account_type)
list_data.append(account)

if list_data:
                # bulk_create will create multiple object in a single query
created_accounts = models.Account.objects.bulk_create(list_data)
if len(list_data) == len(created_accounts):
result = True
else:
rows_error = len(list_data) - len(created_accounts)

return result, rows_error

...

- And in our my_app.views.py:
...
from django.contrib import messages
from my_app import forms
from my_app import utils

@login_required
def add_multiple_accounts(request):
if request.method == 'POST':
csv_upload_form = forms.UploadFileForm(request.POST, request.FILES)
if csv_upload_form.is_valid():
file = csv_upload_form.cleaned_data['file']
csv_result, rows_error = utils.handle_uploaded_file(file, utils.account_valid_fields, utils.create_account_in_db)

if csv_result:
message = 'Successfully imported accounts from the csv file to the database.\n'
message += 'The system is creating Active Directory Accounts using those information in the background.\n'
message += 'Please wait...'
messages.add_message(request, messages.INFO, message)

else:
message = 'There are some errors occured. Please try again.'
messages.add_message(request, messages.INFO, message)
else:
csv_upload_form = forms.UploadFileForm()

return render_to_response('add_multiple.html', locals(), context_instance=RequestContext(request))
...

- Finally, in our template add_multiple.html:

<div id="csv-upload-form-content">
{% if csv_upload_form %}
<form enctype="multipart/form-data" method="POST" action="">{% csrf_token %}

<div>{{ csv_upload_form.as_p }}</div>

<div class="btn-seperator">
<button type="submit" class="action green btn-center" name="action" value="add_csv"><span class="label">Upload</span></button>
</div>
</form>
{% endif %}
</div>

Coolness!!!

Comments