from django import forms from django_countries import countries class KeepOwnAttrsWidget(forms.Widget): def get_context(self, name, value, attrs): attrs.update(self.attrs) return super().get_context(name, value, attrs) class CountrySelectWithChoices(KeepOwnAttrsWidget, forms.Select): is_required = True def __init__(self, *args, **kwargs): kwargs['choices'] = countries super().__init__(*args, **kwargs) class KeepAttrsTextInput(KeepOwnAttrsWidget, forms.TextInput): pass def classify(field): return field.replace('_', '') def display(field): return field.replace('_', ' ').title() class NestedMultiWidget(KeepOwnAttrsWidget, forms.MultiWidget): template_name = 'addressfield/widgets/nested_with_label.html' def __init__(self, *args, **kwargs): widgets = [ widget(attrs={'class': classify(field), 'required': False, 'display': display(field)}) for field, widget in self.components.items() ] super().__init__(widgets, *args, **kwargs) @property def field_names(self): return [classify(field) for field in self.components.keys()] def decompress(self, value): if value: decompressed = [] for i, widget in enumerate(self.widgets): if hasattr(widget, 'components'): decompressed.append(widget.decompress(value)) else: decompressed.append(value.get(self.field_names[i])) return decompressed return [None] * len(self.components) def value_from_datadict(self, data, files, name): value = {} for i, widget in enumerate(self.widgets): widget_value = widget.value_from_datadict(data, files, name + '_%s' % i) # flatten the data structure to a single dict if hasattr(widget, 'widgets'): value.update(widget_value) else: value[self.field_names[i]] = widget_value return value class LocalityWidget(NestedMultiWidget): components = { 'locality_name': KeepAttrsTextInput, 'administrative_area': KeepAttrsTextInput, 'postal_code': KeepAttrsTextInput, } class AddressWidget(NestedMultiWidget): components = { 'country': CountrySelectWithChoices, 'thoroughfare': KeepAttrsTextInput, 'premise': KeepAttrsTextInput, 'locality': LocalityWidget, } class Media: js = ( 'jquery.addressfield.min.js', 'address_form.js', ) def __init__(self, *args, **kwargs): attrs = kwargs.get('attrs', {}) attrs['class'] = 'address' kwargs['attrs'] = attrs super().__init__(*args, **kwargs)