CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-django-import-export

Django application and library for importing and exporting data with included admin integration.

Pending
Overview
Eval results
Files

widgets-transformation.mddocs/

Widgets and Data Transformation

Comprehensive widget system for transforming data between Python objects and serialized formats, including support for various data types and relationships.

Capabilities

Base Widget Class

The foundation widget class that all other widgets extend.

class Widget:
    def clean(self, value, row=None, **kwargs):
        """
        Transform imported value into appropriate Python object.
        
        Parameters:
        - value: Raw value from import data
        - row: dict, complete row data (optional)
        - **kwargs: Additional cleaning options
        
        Returns:
        Cleaned Python value
        """

    def render(self, value, obj=None, **kwargs):
        """
        Transform Python value into export representation.
        
        Parameters:
        - value: Python value to export
        - obj: Model instance being exported (optional)
        - **kwargs: Additional rendering options
        
        Returns:
        Serialized value for export
        """

Numeric Widgets

Widgets for handling numeric data types with validation and formatting.

class NumberWidget(Widget):
    """Base widget for numeric data with locale support."""
    
    def __init__(self, **kwargs):
        """
        Initialize numeric widget.
        
        Parameters:
        - **kwargs: Widget configuration options
        """

class FloatWidget(NumberWidget):
    """Widget for floating-point numbers."""

class IntegerWidget(NumberWidget):
    """Widget for integer numbers."""

class DecimalWidget(NumberWidget):
    """Widget for decimal numbers with precision control."""

Text and Character Widgets

Widgets for handling text and character data.

class CharWidget(Widget):
    def __init__(self, allow_blank=False, **kwargs):
        """
        Widget for character/string data.
        
        Parameters:
        - allow_blank: bool, allow blank values (default: False)
        - **kwargs: Additional widget options
        """

Boolean Widget

Widget for handling boolean data with customizable true/false values.

class BooleanWidget(Widget):
    TRUE_VALUES = ['1', 1, True, 'true', 'TRUE', 'True', 'yes', 'YES', 'Yes', 'y', 'Y']
    FALSE_VALUES = ['0', 0, False, 'false', 'FALSE', 'False', 'no', 'NO', 'No', 'n', 'N']
    NULL_VALUES = ['', None]

    def __init__(self, **kwargs):
        """
        Widget for boolean data with customizable value mapping.
        
        Parameters:
        - **kwargs: Widget configuration options
        """

Date and Time Widgets

Widgets for handling temporal data with format support.

class DateWidget(Widget):
    def __init__(self, format='%Y-%m-%d', **kwargs):
        """
        Widget for date data.
        
        Parameters:
        - format: str, date format string (default: '%Y-%m-%d')
        - **kwargs: Additional widget options
        """

class DateTimeWidget(Widget):
    def __init__(self, format='%Y-%m-%d %H:%M:%S', **kwargs):
        """
        Widget for datetime data.
        
        Parameters:
        - format: str, datetime format string (default: '%Y-%m-%d %H:%M:%S')
        - **kwargs: Additional widget options
        """

class TimeWidget(Widget):
    def __init__(self, format='%H:%M:%S', **kwargs):
        """
        Widget for time data.
        
        Parameters:
        - format: str, time format string (default: '%H:%M:%S')
        - **kwargs: Additional widget options
        """

class DurationWidget(Widget):
    """Widget for duration/timedelta data."""

Data Structure Widgets

Widgets for complex data structures like JSON and arrays.

class JSONWidget(Widget):
    def __init__(self, **kwargs):
        """
        Widget for JSON data serialization/deserialization.
        
        Parameters:
        - **kwargs: JSON serialization options
        """

class SimpleArrayWidget(Widget):
    def __init__(self, separator=',', **kwargs):
        """
        Widget for simple array data.
        
        Parameters:
        - separator: str, separator character (default: ',')
        - **kwargs: Additional widget options
        """

Relationship Widgets

Widgets for handling Django model relationships.

class ForeignKeyWidget(Widget):
    def __init__(self, model, field='pk', use_natural_foreign_keys=False, key_is_id=False, **kwargs):
        """
        Widget for foreign key relationships.
        
        Parameters:
        - model: Related model class
        - field: str, field to use for lookup (default: 'pk')
        - use_natural_foreign_keys: bool, use natural keys for lookup
        - key_is_id: bool, whether key is the model ID
        - **kwargs: Additional widget options
        """

    def get_queryset(self, value, row, *args, **kwargs):
        """
        Get queryset for foreign key lookup.
        
        Parameters:
        - value: Lookup value
        - row: dict, complete row data
        - *args: Additional arguments
        - **kwargs: Additional keyword arguments
        
        Returns:
        QuerySet for lookup operations
        """

    def get_lookup_kwargs(self, value, row, **kwargs):
        """
        Get lookup kwargs for foreign key resolution.
        
        Parameters:
        - value: Lookup value
        - row: dict, complete row data
        - **kwargs: Additional options
        
        Returns:
        Dict of lookup kwargs
        """

class ManyToManyWidget(Widget):
    def __init__(self, model, separator=None, field=None, **kwargs):
        """
        Widget for many-to-many relationships.
        
        Parameters:
        - model: Related model class
        - separator: str, separator for multiple values
        - field: str, field to use for lookup
        - **kwargs: Additional widget options
        """

Utility Functions

def format_datetime(value, datetime_format):
    """
    Format datetime value using specified format string.
    
    Parameters:
    - value: datetime object or string
    - datetime_format: str, format string
    
    Returns:
    Formatted datetime string
    """

Usage Examples

Basic Widget Usage

from import_export import fields, widgets

class BookResource(resources.ModelResource):
    # Use date widget with custom format
    published_date = fields.Field(
        attribute='published_date',
        widget=widgets.DateWidget(format='%d/%m/%Y')
    )
    
    # Use boolean widget for active status
    is_active = fields.Field(
        attribute='is_active',
        widget=widgets.BooleanWidget()
    )
    
    # Use JSON widget for metadata
    metadata = fields.Field(
        attribute='metadata',
        widget=widgets.JSONWidget()
    )

Custom Widget Creation

class UpperCaseWidget(widgets.CharWidget):
    """Custom widget that converts text to uppercase."""
    
    def clean(self, value, row=None, **kwargs):
        value = super().clean(value, row, **kwargs)
        return value.upper() if value else value
    
    def render(self, value, obj=None, **kwargs):
        return value.upper() if value else value

class BookResource(resources.ModelResource):
    title = fields.Field(
        attribute='title',
        widget=UpperCaseWidget()
    )

Foreign Key Widget with Custom Lookup

class AuthorWidget(widgets.ForeignKeyWidget):
    """Custom foreign key widget for author lookup."""
    
    def __init__(self, **kwargs):
        super().__init__(Author, field='name', **kwargs)
    
    def get_queryset(self, value, row, *args, **kwargs):
        # Custom queryset filtering
        return Author.objects.filter(active=True)
    
    def get_lookup_kwargs(self, value, row, **kwargs):
        # Custom lookup logic
        return {'name__iexact': value.strip()}

class BookResource(resources.ModelResource):
    author = fields.Field(
        attribute='author',
        widget=AuthorWidget()
    )

Many-to-Many Widget with Custom Separator

class TagWidget(widgets.ManyToManyWidget):
    """Custom M2M widget for tags."""
    
    def __init__(self, **kwargs):
        super().__init__(Tag, separator='|', field='name', **kwargs)
    
    def clean(self, value, row=None, **kwargs):
        if not value:
            return []
        
        # Split by separator and clean each tag
        tag_names = [name.strip() for name in value.split(self.separator)]
        tags = []
        
        for name in tag_names:
            if name:
                tag, created = Tag.objects.get_or_create(name=name)
                tags.append(tag)
        
        return tags

class BookResource(resources.ModelResource):
    tags = fields.Field(
        attribute='tags',
        widget=TagWidget()
    )

Date Widget with Multiple Formats

class FlexibleDateWidget(widgets.DateWidget):
    """Date widget that accepts multiple input formats."""
    
    FORMATS = ['%Y-%m-%d', '%d/%m/%Y', '%m/%d/%Y', '%Y%m%d']
    
    def clean(self, value, row=None, **kwargs):
        if not value:
            return None
        
        if isinstance(value, date):
            return value
        
        # Try multiple formats
        for fmt in self.FORMATS:
            try:
                return datetime.strptime(str(value), fmt).date()
            except ValueError:
                continue
        
        raise ValueError(f"Unable to parse date: {value}")

class BookResource(resources.ModelResource):
    published_date = fields.Field(
        attribute='published_date',
        widget=FlexibleDateWidget()
    )

Widget with Row Context

class ConditionalWidget(widgets.CharWidget):
    """Widget that behaves differently based on row data."""
    
    def clean(self, value, row=None, **kwargs):
        value = super().clean(value, row, **kwargs)
        
        # Modify value based on other row data
        if row and row.get('category') == 'special':
            return f"SPECIAL: {value}"
        
        return value

class BookResource(resources.ModelResource):
    title = fields.Field(
        attribute='title',
        widget=ConditionalWidget()
    )

Complex Data Transformation Widget

class PriceWidget(widgets.Widget):
    """Widget for handling price data with currency conversion."""
    
    def __init__(self, currency='USD', **kwargs):
        self.currency = currency
        super().__init__(**kwargs)
    
    def clean(self, value, row=None, **kwargs):
        if not value:
            return None
        
        # Handle different price formats
        if isinstance(value, str):
            # Remove currency symbols and convert
            value = value.replace('$', '').replace(',', '').strip()
        
        try:
            price = Decimal(str(value))
            
            # Convert currency if needed
            if row and row.get('currency') != self.currency:
                price = self.convert_currency(
                    price, row.get('currency'), self.currency
                )
            
            return price
        except (ValueError, TypeError):
            raise ValueError(f"Invalid price format: {value}")
    
    def render(self, value, obj=None, **kwargs):
        if value is None:
            return ''
        return f"${value:.2f}"
    
    def convert_currency(self, amount, from_currency, to_currency):
        # Mock currency conversion
        rates = {'USD': 1.0, 'EUR': 0.85, 'GBP': 0.75}
        return amount * rates.get(to_currency, 1.0) / rates.get(from_currency, 1.0)

class BookResource(resources.ModelResource):
    price = fields.Field(
        attribute='price',
        widget=PriceWidget(currency='USD')
    )

Widget with Validation

class ISBNWidget(widgets.CharWidget):
    """Widget for ISBN validation and formatting."""
    
    def clean(self, value, row=None, **kwargs):
        value = super().clean(value, row, **kwargs)
        
        if not value:
            return value
        
        # Clean ISBN format
        isbn = ''.join(c for c in value if c.isdigit() or c.upper() == 'X')
        
        # Validate ISBN length
        if len(isbn) not in [10, 13]:
            raise ValueError(f"Invalid ISBN length: {isbn}")
        
        # Add hyphens for readability
        if len(isbn) == 13:
            return f"{isbn[:3]}-{isbn[3:4]}-{isbn[4:6]}-{isbn[6:12]}-{isbn[12]}"
        else:
            return f"{isbn[:1]}-{isbn[1:4]}-{isbn[4:9]}-{isbn[9]}"
    
    def render(self, value, obj=None, **kwargs):
        # Remove hyphens for export
        return ''.join(c for c in (value or '') if c.isdigit() or c.upper() == 'X')

class BookResource(resources.ModelResource):
    isbn = fields.Field(
        attribute='isbn',
        widget=ISBNWidget()
    )

Widget Configuration in Resource Meta

class BookResource(resources.ModelResource):
    class Meta:
        model = Book
        widgets = {
            'published_date': {'format': '%d/%m/%Y'},
            'price': {'currency': 'EUR'},
            'is_active': {'true_values': ['yes', 'y', '1']},
            'tags': {'separator': ';'},
        }

Install with Tessl CLI

npx tessl i tessl/pypi-django-import-export

docs

admin-integration.md

file-formats.md

forms-ui.md

index.md

management-commands.md

resources-fields.md

widgets-transformation.md

tile.json