Django-import-export: How can i get the name of the models choices field while exporting?

Created on 24 Sep 2016  路  4Comments  路  Source: django-import-export/django-import-export

I have a model with choices list (models.py):

class Product(models.Model):
    ...
    UNITS_L = 1
    UNITS_SL = 2
    UNITS_XL = 3
    PRODUCT_SIZE_CHOICES = (
    (UNITS_L, _('L')),
    (UNITS_SL, _('SL')),
    (UNITS_XL), _('XL'),
    )
    product_size = models.IntegerField(choices=PRODUCT_SIZE_CHOICES)
    ...

Also I added a new class for exporting needed fields(admin.py):

from import_export import resources, fields
...
Class ProductReport(resources.ModelResource):
    product_size = fields.Field()

    class Meta:
        model = Product

    #I want to do a proper function to render a PRODUCT_SIZE_CHOICES(product_size)

    def dehydrate_size_units(self, product):
        return '%s' % (product.PRODUCT_SIZE_CHOICES[product_size]) 

    fields = ('product_name', 'product_size')

Class ProductAdmin(ExportMixin, admin.ModelAdmin):
    resource_class = ProductReport

But this is not working. How can I get a named value of PRODUCT_SIZE_CHOICES in export by Django import-export ?

Most helpful comment

For anyone else who stumbled across this looking for an easy way to import/export your choice display values, I made a couple tweaks to @int-ua's code above and it's working well for me:

from import_export.widgets import Widget


class ChoicesWidget(Widget):
    """
    Widget that uses choice display values in place of database values
    """
    def __init__(self, choices, *args, **kwargs):
        """
        Creates a self.choices dict with a key, display value, and value,
        db value, e.g. {'Chocolate': 'CHOC'}
        """
        self.choices = {str(value): str(key) for (key, value) in choices}

    def clean(self, value, row=None):
        """Returns the db value given the display value"""
        return self.choices.get(value, value) if value else None

    def render(self, value):
        """Returns the display value given the db value"""
        for display_val, db_val in self.choices.items():
            if db_val == value:
                return display_val
        return ''

To use the widget, be sure to pass in the choices from your model:

from import_export import resources, fields


class MyModelResource(resources.ModelResource):
    my_choice_field = fields.Field(
        widget=ChoicesWidget(MyModel.MY_CHOICE_FIELD_CHOICES),
        column_name='my_choice_field',
        attribute='my_choice_field',
    )
    class Meta:
        ...

All 4 comments

You'll need a Widget for this, here is mine, for example:

class ChoicesWidget(Widget):
    """
    Widget to substitute localized choices.
    N.B.: before_import is executed before Widget.clean
    """
    def __init__(self, choices, *args, **kwargs):
        activate(settings.LANGUAGE_CODE)
        self.choices = {str(value): str(key) for (key, value) in choices}

    def clean(self, value, row=None):
        # 胁懈泻芯薪褍褦褌褜褋褟 锌褨褋谢褟 before_import
        return self.choices.get(value, value) if value else None

    def render(self, value):
        if value is None:
            return ""
        return value

For anyone else who stumbled across this looking for an easy way to import/export your choice display values, I made a couple tweaks to @int-ua's code above and it's working well for me:

from import_export.widgets import Widget


class ChoicesWidget(Widget):
    """
    Widget that uses choice display values in place of database values
    """
    def __init__(self, choices, *args, **kwargs):
        """
        Creates a self.choices dict with a key, display value, and value,
        db value, e.g. {'Chocolate': 'CHOC'}
        """
        self.choices = {str(value): str(key) for (key, value) in choices}

    def clean(self, value, row=None):
        """Returns the db value given the display value"""
        return self.choices.get(value, value) if value else None

    def render(self, value):
        """Returns the display value given the db value"""
        for display_val, db_val in self.choices.items():
            if db_val == value:
                return display_val
        return ''

To use the widget, be sure to pass in the choices from your model:

from import_export import resources, fields


class MyModelResource(resources.ModelResource):
    my_choice_field = fields.Field(
        widget=ChoicesWidget(MyModel.MY_CHOICE_FIELD_CHOICES),
        column_name='my_choice_field',
        attribute='my_choice_field',
    )
    class Meta:
        ...

Updated snippet to support latest version and made it O(1) in render method instead of O(n).

from import_export.widgets import Widget


class ChoicesWidget(Widget):
    """
    Widget that uses choice display values in place of database values
    """
    def __init__(self, choices, *args, **kwargs):
        """
        Creates a self.choices dict with a key, display value, and value,
        db value, e.g. {'Chocolate': 'CHOC'}
        """
        self.choices = dict(choices)
        self.revert_choices = dict((v, k) for k, v in self.choices.items())

    def clean(self, value, row=None, *args, **kwargs):
        """Returns the db value given the display value"""
        return self.revert_choices.get(value, value) if value else None

    def render(self, value, obj=None):
        """Returns the display value given the db value"""
        return self.choices.get(value, '')

https://stackoverflow.com/a/49186282/2714931
A hack method to realize this quickly, just configure _choice_fields to your choices fields, that's it.

import import_export
from import_export import resources


class MyModelResource(resources.ModelResource):
    _choice_fields = [
        'field_a', 'field_b',
    ]
    for _field_name in _choice_fields:
        locals()[_field_name] = import_export.fields.Field(
            attribute='get_%s_display' % _field_name,
            column_name=MyModel._meta.get_field(_field_name).verbose_name
        )
Was this page helpful?
0 / 5 - 0 ratings