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 = True
django-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 = Comment
Dosya 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 = Comment
Aş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