首页 > 解决方案 > Django - Export CSV in ManytoMany Field

问题描述

How can I export the name of a category on M2M a relationship? I've tried to use .all() but it returns everything without format, so it is not useful

#----------
class Category(models.Model):
    name = models.CharField(verbose_name='Name', max_length=254)

class SubCategory(models.Model):
    name = models.CharField(verbose_name='Name', max_length=254)
    category = models.ManyToManyField(Category, blank=True)

#----------

from django.http import HttpResponse
import csv

def csv_export(request):
    # Get database information
    qs = SubCategory.objects.all()

    # Create the HttpResponse object with the appropriate CSV header.
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="export_file.csv"'

    writer = csv.writer(response)
    writer.writerow(['name','category'])

    for rule in qs:
        writer.writerow(['name','category'])

    return response

标签: pythondjangocsv

解决方案


A .csv files is basically a table. So it is not straightforward to serialize a many-to-many relation. If a SubCategory has two categories, then how will you serialize this?

You can serialize all combinations of subcategories/categories. So then the .csv file looks like:

subcat1,cat1|cat2|cat3
subcat2,cat2|cat4
subcat3,cat1

a potential problem with this is that if the category name contains a pipe character (|), then you can not make a distinction between foo|bar as two Categorys foo and bar, or a category with name foo|bar.

from django.http import HttpResponse
import csv

def csv_export(request):
    # Get database information
    qs = SubCategory.objects.prefetch_related(
        'category'
    )

    # Create the HttpResponse object with the appropriate CSV header.
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="export_file.csv"'

    writer = csv.writer(response)
    writer.writerow(['name','category'])

    for rule in qs:
        writer.writerow(
            [rule.name,'|'.join(c.name for c in rule.category.all())]
        )

    return response

推荐阅读