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!!!
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
Post a Comment