Django Uygulamalarında Excel, CSV, JSON gibi Farklı Formattaki Veriyi İçe ve Dışa Aktarma
Django ile web uygulamaları geliştirdiğimizde veri tabanına farklı formatlarda verilerimizi aktarmak veya veritabanındaki verileri tablo halinde elde etmek isteriz. django-import-export paketi bizim için bu işlemi kolaylaştıran bir pakettir. Paket Excel, CSV, JSON gibi farklı formattaki verileri desteklemektedir.
Kurulum
Paket kurulumu için pip paket yöneticisini kullanarak paketi bilgisayarımıza yüklüyoruz.
pip install django-import-export
Daha sonra settings.py dosyamıza aşağıdaki kodları ekleyerek uygulamamıza tanımlıyoruz. Ayrıca veriyi içe(import) veya dışa(export) aktarma yaparken bir problem çıkarsa veri bütünlüğünü sağlamak için IMPORT_EXPORT_USE_TRANSACTIONS = True olarak tanımlıyoruz.
INSTALLED_APPS = (
...
'import_export',
)
# The default value is False
IMPORT_EXPORT_USE_TRANSACTIONS = Truedjango-import-export paketini kullanırken paketin nasıl çalışacağını tanımladığımız model sınıflarımıza benzeyen Resource konsepti bulunmaktadır. Bunun için uygulamamızın içinde resource.py isminde dosya oluşturuyoruz.
models.py dosyamızda Category ve Comment adında iki model tanımladık. Comment’ leri django-import-export paketi ile içe ve dışa aktaracağız.
models.py
from django.db import models
class Category(models.Model):
title = models.CharField(max_length = 500, unique=True)
def __str__(self):
return "{0}".format(self.title)
class Comment(models.Model):
description = models.TextField()
category = models.ForeignKey(Category, on_delete= models.CASCADE )resources.py dosyasında hangi model kullanılacağını tanımlıyoruz.
resources.py
from import_export import resources
from label.models import Comment
class CommentResource(resources.ModelResource):
class Meta:
model = CommentDosya yapısı aşağıdaki resimdeki gibidir:

Veriyi Dışa Aktarma
Veritabanımızdaki Comment tablosunu frontendde bir form oluşturarak kullanıcının istediği formatta bilgisayarına indirebilmesini sağlayacağız. Bunu için ilk önce urls.py path’ imizi tanımlayacağız.
path('export-data/', export_data, name="export_data"),Daha sonra export_data.html isminde template i oluşturuyoruz. Bu template ‘ te formumuzu oluşturalım.
{% block content %}
<div class="card card-secondary">
<div class="card-header">
<h3 class="card-title">Export Comments</h3>
</div>
<div class="card-body">
<form role="form" method="POST" action="{% url 'label:export_data' %}" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label>Choose Format Type</label>
<select class="custom-select" name="file-format">
<option selected>Choose format...</option>
<option>CSV</option>
<option>JSON</option>
<option>XLS (Excel)</option>
</select>
</div> <br><br><br>
<button type="submit" class="btn btn-info btn-block">Export</button>
</form>
</div>
</div>
{% endblock %} 
views.py
views.py dosyasında form post edildiğinde çalışacak fonksiyonu tanımladık.
from django.http import HttpResponse
from .resources import CommentResource
def export_data(request):
if request.method == 'POST':
# Get selected option from form
file_format = request.POST['file-format']
comment_resource = CommentResource()
dataset = comment_resource.export()
if file_format == 'CSV':
response = HttpResponse(dataset.csv, content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="exported_data.csv"'
return response
elif file_format == 'JSON':
response = HttpResponse(dataset.json, content_type='application/json')
response['Content-Disposition'] = 'attachment; filename="exported_data.json"'
return response
elif file_format == 'XLS (Excel)':
response = HttpResponse(dataset.xls, content_type='application/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename="exported_data.xls"'
return response
return render(request, 'label/export_import_data_page.html') Veriyi İçe Aktarma
Frontend’ de form oluşturarak kullanıcının CSV veya JSON formatındaki verisini veritabanına yükleyeceğiz. İlk önce form post edildiğinde çalışacak path’ i tanımlıyoruz.
path('import-data/', import_data, name="import_data"),Aşağıda templatimizde kullanılacak formun kodu bulunmaktadır.
{% block content %}
<div class="card card-secondary">
<div class="card-header">
<h3 class="card-title">Import Comments</h3>
</div>
<div class="card-body">
<form role="form" method="POST" action="{% url 'label:import_data' %}" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label>Choose Format Type</label><br>
<input class="mb-2" type="file" name="importData">
<select class="custom-select" name="file-format1">
<option selected>Choose format...</option>
<option>CSV</option>
<option>JSON</option>
</select>
</div> <br><br><br>
<button type="submit" class="btn btn-info btn-block">Import</button>
</form>
</div>
</div>
{% endblock %} 
views.py
from tablib import Dataset
def import_data(request):
if request.method == 'POST':
file_format = request.POST['file-format1']
comment_resource = CommentResource()
dataset = Dataset()
new_comments = request.FILES['importData']
if file_format == 'CSV':
imported_data = dataset.load(new_comments.read().decode('utf-8'),format='csv')
result = comment_resource.import_data(dataset, dry_run=True)
elif file_format == 'JSON':
imported_data = dataset.load(new_comments.read().decode('utf-8'),format='json')
# Testing data import
result = comment_resource.import_data(dataset, dry_run=True)
if not result.has_errors():
# Import now
comment_resource.import_data(dataset, dry_run=False)
return render(request, 'label/export_import_data_page.html')')Category modelini Comment modelimize bire çok ilişkisi bulunmaktadır. Bu ilişkiyide yönetebilmemiz için resource.py dosyamızı değiştimemiz gerekiyor.
resources.py
from import_export import resources, widgets, fields
from label.models import Comment, Category
class CharRequiredWidget(widgets.CharWidget):
def clean(self, value, row=None, *args, **kwargs):
val = super().clean(value)
if val:
return val
else:
raise ValueError('this field is required')
class ForeignkeyRequiredWidget(widgets.ForeignKeyWidget):
def clean(self, value, row=None, *args, **kwargs):
if value:
print(self.field, value)
return self.get_queryset(value, row, *args, **kwargs).get(**{self.field: value})
else:
raise ValueError(self.field+ " required")
class CommentResource(resources.ModelResource):
category = fields.Field(column_name='category', attribute='category', widget=ForeignkeyRequiredWidget(Category, 'title'),
saves_null_values=False) # title Category modelindeki kolon ismi
description = fields.Field(saves_null_values=False, column_name='description', attribute='description',
widget=CharRequiredWidget())
class Meta:
model = Comment
fields = ('id', 'description', 'category')
clean_model_instances = True
# class CommentResource(resources.ModelResource):
# class Meta:
# model = CommentAşağıdaki CSV dosya yapısı ile uygulamamızı deneyebiliriz.
id,category,description 1,Computer,Lorem ipsum dolor sit amet, consectetur 2,Computer,adipiscing elit, sed do eiusmod tempor incididunt ut 3,TV,labore et dolore magna aliqua. Ut enim ad minim veniam, quis nost 4,TV,Sed ut perspiciatis unde omnis iste natus error sit voluptatem 5,TV,accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
Django Admin Paneli
admin.py dosyasına aşağıdaki kodları ekleyerek Django Admin panelinde comment modelimiz için içe ve dışa aktarma işlemlerini kolayca yapabiliriz.
from django.contrib import admin
from label.models import Comment
from import_export.admin import ImportExportModelAdmin
@admin.register(Comment)
class CommentAdmin(ImportExportModelAdmin):
pass
Yukarıdaki kodları eklediğimizde Admin panelinde Comment modeline girdiğimizde sağ üstte IMPORT ve EXPORT adında iki düğmenin belirdiğini göreceksiniz. EXPORT düğmesine basarak aşağıdaki resimde de görüldüğü gibi bir çok formatta Comment tablomuzu dışa aktarabiliriz.

Export dediğimizde çıkacak ekranda hangi formatta olmasını tanımlayacağımız form karşımıza çıkacaktır.

Eğer Admin panelinde Export ve Import formlarını customize etmek istiyorsak admin.py deki dosyamızı aşağıdaki şekilde düzenleyebiliriz.
from django.contrib import admin
from label.models import Comment, Category
from import_export.admin import ImportExportModelAdmin
# 1 METHOD
# @admin.register(Comment)
# class CommentAdmin(ImportExportModelAdmin):
# pass
# 2 METHOD
from .resources import CommentResource
class CommentAdmin(ImportExportModelAdmin):
resource_class = CommentResource
list_display = ('description', 'category')
admin.site.register(Comment, CommentAdmin)Django projelerinde kullanabileceğiniz çok kullanışlı bir pakettir. Bu yazıda kullanılabilecek tüm durumları basit ve açık bir şekilde anlatmaya çalıştım. Başarılar.
Kaynaklar
- https://django-import-export.readthedocs.io/en/latest/index.html