Django Import Export From Basic To Advance
Tags: django, webdev, djangoimportexport, djangodevelopers
Published: 2022-10-13 13:14:54

### Table of Content

- Installation and configuration
- Models
- Resources
- Exporting Data
- Exporting Data as JSON
- Exporting Data as CSV
- Data Filtering + Exporting Data
- Dealing with the model relationship
- Foreign Key Relationship
- Method 1: Using ForeignKeyWidget.
- Method 2: Using fields
- Many To Many Key Relationships
- Method 1: By data manipulation on export
- Method 2: By adding  ManyToManyWidget
- Advanced data manipulation on export.
- Adding new columns
- Changing headers before exporting
- Export order
- Admin Integration

django-import-export is a library used to handle importing and exporting of data. This package supports multiple formats.

1. csv
2. xls
3. xlsx
4. tsv
5. ods
6. json
7. yml
8. html
This Library also has a Django admin integration.

For this article, I am using django==4.0 and django-import-export==2.9.0

## Installation and configuration

```
pip install django-import-export
```
Update your settings.py file.

```
INSTALLED_APPS = [
    .....
    'import_export',
]
```

### Models.
Now add our models. Here we have 3 simple models. I am using Foreign Key and Many to Many relations to cover these in this article.

```
# models.py
from django.db import models


class Author(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Category(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Book(models.Model):
    name = models.CharField('Book name', max_length=100)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    published = models.DateField('Published')
    categories = models.ManyToManyField(Category)

    def __str__(self):
        return self.name
```
Now run migrations.

### Resources
Create a new file resources.py in your app. In this file, we will add the Model Resource classes which are similar to how Django handles forms.py.

```
# resources.py
from import_export import resources
from .models import Author, Category, Book


class BookResource(resources.ModelResource):
    class Meta:
        model = Book
```
This is the simplest configuration. We can add more, like fields, and exclude in Meta just like forms.py configuration. We will discuss it below.

### Exporting data
Run python manage.py shell. It will open the shell.

#### Exporting Data as JSON.

```
from app.resources import BookResource

book_resource = BookResource()
data = book_resource.export()
data.json
```
Output:

```
[
    {"id": 1, "name": "Django for APIs", "author": 1, "published": "2022-09-16", "categories": "2"},
    {"id": 2, "name": "Beginning Django E-Commerce", "author": 2, "published": "2022-09-16", "categories": "1,3"},
    {"id": 3, "name": "Django Unleashed", "author": 3, "published": "2022-09-16", "categories": "1,2"},
    {"id": 4, "name": "Django Design Patterns and Best Practices", "author": 4, "published": "2022-09-16", "categories": "1,4"}
]
```

#### Exporting Data as CSV.

```
from app.resources import BookResource

book_resource = BookResource()
data = book_resource.export()
data.csv
```
Output:

```
id,name,author,published,categories
1,Django for APIs,1,2022-09-16,2
2,Beginning Django E-Commerce,2,2022-09-16,"1,3"
3,Django Unleashed,3,2022-09-16,"1,2"
4,Django Design Patterns and Best Practices,4,2022-09-16,"1,4"
```

### Data Filtering + Exporting Data.
We can also export the filtered data.

```
from app.models import Book
from app.resources import BookResource


query = Book.objects.filter(name="Django for APIs")
book_resource = BookResource()
data = book_resource.export(query)
data.json
```
Output:

```
[
    {"id": 1, "name": "Django for APIs", "author": 1, "published": "2022-09-16", "categories": "2"}
]
```

### Views and URLs.

```
# views.py
from django.http import HttpResponse
from .resources import BookResource


def export_csv(request):
    book_resource = BookResource()
    data = book_resource.export()
    response = HttpResponse(data.csv, content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="books.csv"'
    return response
```

```
# urls.py

from django.urls import path
from .views import export_csv

urlpatterns = [
    path('export-csv', export_csv, name='export_csv')
]
```
Go to this URL: 

http://127.0.0.1:8000/export-csv  (It will download the books.csv file)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qsvlkol06n9425w7x888.PNG)
**Note:**
For json export use content_type='application/json' and filename=books.json

For excel (xslx) export use content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' and filename=books.xlsx

You can check more content_type from [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types):

## Dealing with the model relationship.
When defining ModelResource fields it is possible to follow model relationships:

### 1) Foreign Key Relationship.
In the above CSV file picture, you can see that in the author column we have author ids like 1,2,3,4, not the author name.

#### 1. Method 1: Using ForeignKeyWidget.

> class import_export.widgets.ForeignKeyWidget(model, field='pk', *args, **kwargs)

fields = 'pk' is the default lookup criterion for the lookup field. But we can customize it. As in our example, we are using field = "name" as a lookup field.

I prefer this method because it's a widget so you can use this widget in any resource class.

```
# resources.py

from import_export import resources
from .models import Author, Category, Book
from import_export.fields import Field
from import_export.widgets import ForeignKeyWidget

class BookResource(resources.ModelResource):
    
    author = Field(
        column_name='author',
        attribute='author',
        widget=ForeignKeyWidget(model=Author, field='name'))

    class Meta:
        model = Book
```
Output

```
name,published,categories,author
Django for APIs,2022-09-16,2,William S. Vincent
Beginning Django E-Commerce,2022-09-16,"1,3",James McGaw
Django Unleashed,2022-09-16,"1,2",Andrew Pinkham
Django Design Patterns and Best Practices,2022-09-16,"1,4",Arun Ravindran
```
#### 2. Method 2: Using fields
Adding author__name in fields under Meta will give the author name instead of the author id.

```
# resources.py

class BookResource(resources.ModelResource):
    class Meta:
        model = Book
        fields = ('name', 'author__name', 'published', 'categories')
```
Output:

```
name,published,categories,author__name
Django for APIs,2022-09-16,2,William S. Vincent
Beginning Django E-Commerce,2022-09-16,"1,3",James McGaw
Django Unleashed,2022-09-16,"1,2",Andrew Pinkham
Django Design Patterns and Best Practices,2022-09-16,"1,4",Arun Ravindran
```
Read more from here. [How to export Foreign key relation in django](https://www.letscodemore.com/blog/export-foreign-key-relation-in-django/)

### 2) Many To Many Key Relationships.
If we check the output then we observed that the categories column contains ids of categories like 2, "1,3" etc

We have 2 methods to do this.

#### 1) Method 1:  By data manipulation on export:
We can manipulate data (any field) before exporting. We will perform some more manipulation in this article.

> Not all data can be easily extracted from an object/model attribute. In order to turn complicated data model into a (generally simpler) processed data structure on export, dehydrate_<fieldname> method should be defined: source: [Documentation](https://django-import-export.readthedocs.io/en/latest/getting_started.html#advanced-data-manipulation-on-export)

```
# resources.py

class BookResource(resources.ModelResource):
    
    def dehydrate_categories(self, book):
        data = []
        for i in book.categories.all():
            data.append(i.name)
        joined_string = ",".join(data)
        return str(joined_string)

    class Meta:
        model = Book
```
Output:

```
id,name,author,published,categories
1,Django for APIs,1,2022-09-16,Coding
2,Beginning Django E-Commerce,2,2022-09-16,"Programming,ecommerce"
3,Django Unleashed,3,2022-09-16,"Programming,Coding"
4,Django Design Patterns and Best Practices,4,2022-09-16,"Programming,Best practices"
```
**2) Method 2: By adding ManyToManyWidget:**

> class import_export.widgets.ManyToManyWidget(model, separator=None, field=None, *args, **kwargs)

I prefer this method.

```
# resources.py

from import_export import resources
from .models import Author, Category, Book
from import_export.widgets import ManyToManyWidget
from import_export.fields import Field


class BookResource(resources.ModelResource):
    categories = Field(
        column_name='categories', attribute='categories',
        widget=ManyToManyWidget(model=Category, separator=',', field='name'))

    class Meta:
        model = Book
```
Output
```
categories,id,name,author,published
Coding,1,Django for APIs,1,2022-09-16
"Programming,ecommerce",2,Beginning Django E-Commerce,2,2022-09-16
"Programming,Coding",3,Django Unleashed,3,2022-09-16
"Programming,Best practices",4,Django Design Patterns and Best Practices,4,2022-09-16
```
Read in detail from [here](https://www.letscodemore.com/blog/how-to-export-manytomany-field-relation-in-django-import-export-using-manytomanywidget/).

## Advanced data manipulation on export.

### Adding new columns.
Let's take an example. You have 2 column names or fields i.e first name and last name. And you want to add a new column "Full Name" by combining the first and last name in the exported file.
In our case, we are adding 2 new columns **full_title** and **description**.

```
# resources.py

class BookResource(resources.ModelResource):

    full_title = Field(column_name='Full Title')
    description = Field()

    def dehydrate_full_title(self, book):
        book_name = getattr(book, "name", "unknown")
        author_name = getattr(book.author, "name", "unknown")
        return '%s by %s' % (book_name, author_name)

    def dehydrate_description(self, book):
        book_name = getattr(book, "name", "unknown")
        author_name = getattr(book.author, "name", "unknown")
        published = getattr(book, "published", "unknown")
        return "%s is published by %s in %s" % (book_name, author_name, published)


    def dehydrate_categories(self, book):
        data = []
        for i in book.categories.all():
            data.append(i.name)
        joined_string = ",".join(data)
        return str(joined_string)

    class Meta:
        model = Book
```
Output:

```
Full Title,description,id,name,author,published,categories
Django for APIs by William S. Vincent,Django for APIs is published by William S. Vincent in 2022-09-16,1,Django for APIs,1,2022-09-16,Coding
Beginning Django E-Commerce by James McGaw,Beginning Django E-Commerce is published by James McGaw in 2022-09-16,2,Beginning Django E-Commerce,2,2022-09-16,"Programming,ecommerce"
Django Unleashed by Andrew Pinkham,Django Unleashed is published by Andrew Pinkham in 2022-09-16,3,Django Unleashed,3,2022-09-16,"Programming,Coding"
Django Design Patterns and Best Practices by Arun Ravindran,Django Design Patterns and Best Practices is published by Arun Ravindran in 2022-09-16,4,Django Design Patterns and Best Practices,4,2022-09-16,"Programming,Best practices"
```

### Changing headers before exporting.
We can also change the headers before exporting our data.

```
# resources.py

from import_export import resources
from .models import Author, Category, Book
from .widgets import CustomManyToManyWidget
from import_export.fields import Field


class BookResource(resources.ModelResource):
    
    def get_export_headers(self):
        headers = super().get_export_headers()
        for i, h in enumerate(headers):
            if h == 'author':
                headers[i] = "Author"
            if h == 'name':
                headers[i] = "Book Name"
        return headers

    class Meta:
        model = Book
```
Output:

```
id,Book Name,Author,published,categories
1,Django for APIs,1,2022-09-16,2
2,Beginning Django E-Commerce,2,2022-09-16,"1,3"
3,Django Unleashed,3,2022-09-16,"1,2"
4,Django Design Patterns and Best Practices,4,2022-09-16,"1,4"
```

### Export Order.
You can set export_order in the Meta class.

```
# resources.py

class Meta:
        model = Book
        export_order = (
            'name', 'author', 'full_title', 'published', 'categories', 'description'
        )
```
Output:

```
name,author,Full Title,published,categories,description
Django for APIs,William S. Vincent,Django for APIs by William S. Vincent,2022-09-16,Coding,Django for APIs is published by William S. Vincent in 2022-09-16
Beginning Django E-Commerce,James McGaw,Beginning Django E-Commerce by James McGaw,2022-09-16,"Programming,ecommerce",Beginning Django E-Commerce is published by James McGaw in 2022-09-16
Django Unleashed,Andrew Pinkham,Django Unleashed by Andrew Pinkham,2022-09-16,"Programming,Coding",Django Unleashed is published by Andrew Pinkham in 2022-09-16
Django Design Patterns and Best Practices,Arun Ravindran,Django Design Patterns and Best Practices by Arun Ravindran,2022-09-16,"Programming,Best practices",Django Design Patterns and Best Practices is published by Arun Ravindran in 2022-09-16
```
You can also use exclude in the Meta class. I will recommend you go through the [documentation](https://django-import-export.readthedocs.io/en/latest/). Because django-import-export documentation is really good and easy to understand.

## Admin Integration.

```
# admin.py

from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from .models import (Author, Category, Book)
from .resources import BookResource


admin.site.register(Author)
admin.site.register(Category)


class BookAdmin(ImportExportModelAdmin):
    resource_class = BookResource

admin.site.register(Book, BookAdmin)
```
Then go to the admin panel. You will see the following options in the admin panel under Book Model.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9uonqr060stg660axsfp.PNG)
You can select a different format and press submit. Your data will be exported in your required format.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swkfysoe2wiytcyv21h0.PNG)
That's it for now. If you like this article then please show some love in the comment section or by supporting the content. You can also suggest more articles in the comment section. I will definitely write your suggested articles.
Thank you.

## If you like my post then you can support me by buying me a coffee ☕
<a href="https://www.buymeacoffee.com/letscodemore" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;" ></a>

Happy Coding.

This artical is originally published here. [https://www.letscodemore.com/](https://www.letscodemore.com/)