Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # forms.py
- from shared.admin.fields import DynamicArrayField
- class ExampleAdminForm(forms.ModelForm):
- class Meta:
- model = Realty
- fields = '__all__'
- field_classes = {
- 'prices': DynamicArrayField,
- }
- # fields.py
- from itertools import chain
- from django import forms
- from django.contrib.postgres.utils import prefix_validation_error
- from .widgets import DynamicArrayFieldWidget
- __all__ = ('DynamicArrayField')
- class DynamicArrayField(forms.Field):
- default_error_messages = {
- 'item_invalid': 'Item %(nth)s in the array did not validate: ',
- }
- def __init__(self, base_field, **kwargs):
- self.base_field = base_field
- self.max_length = kwargs.pop('max_length', None)
- kwargs.setdefault('widget', DynamicArrayFieldWidget)
- super().__init__(**kwargs)
- def clean(self, value):
- cleaned_data = []
- errors = []
- value = filter(None, value)
- for index, item in enumerate(value):
- try:
- cleaned_data.append(self.base_field.clean(item))
- except forms.ValidationError as error:
- errors.append(prefix_validation_error(
- error, self.error_messages['item_invalid'],
- code='item_invalid', params={'nth': index},
- ))
- if errors:
- raise forms.ValidationError(list(chain.from_iterable(errors)))
- return cleaned_data
- # widgets.py
- from django import forms
- __all__ = (
- 'DynamicArrayFieldWidget',
- )
- class DynamicArrayFieldWidget(forms.TextInput):
- template_name = 'admin/forms/widgets/dynamic_array.html'
- def get_context(self, name, value, attrs):
- value = value or ['']
- context = super().get_context(name, value, attrs)
- final_attrs = context['widget']['attrs']
- id_ = context['widget']['attrs'].get('id')
- subwidgets = []
- for index, item in enumerate(context['widget']['value']):
- widget_attrs = final_attrs.copy()
- if id_:
- widget_attrs['id'] = f'{id_}_{index}'
- widget = forms.TextInput()
- widget.is_required = self.is_required
- subwidgets.append(widget.get_context(name, item, widget_attrs)['widget'])
- context['widget']['subwidgets'] = subwidgets
- return context
- def value_from_datadict(self, data, files, name):
- try:
- getter = data.getlist
- except AttributeError:
- getter = data.get
- return getter(name)
- def format_value(self, value):
- return value or []
- # dynamic_array.html
- {% load i18n %}
- {% spaceless %}
- <div class="dynamic-array-widget" style="width: 100%">
- <ul id="array-items">
- {% for widget in widget.subwidgets %}
- <li class="array-item">
- {% include widget.template_name %}
- <img class="del-array-item" style="margin: 0 5px;" src="/static/admin/img/icon-deletelink.svg" alt="Delete">
- </li>
- {% endfor %}
- </ul>
- <div class="add-row">
- <button type="button" class="add-array-item">{% blocktrans %}Add another{% endblocktrans %}</button>
- </div>
- </div>
- {% endspaceless %}
- <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
- <script>
- var $clone = $('.array-item').last()
- $('.dynamic-array-widget').on("click", '.add-array-item', function() {
- var $new = $clone.clone()
- var id_parts = $new.find('input').attr('id').split('_')
- var id = id_parts.slice(0, -1).join('_') + '_' + String(parseInt(id_parts.slice(-1)[0]) + 1)
- $new.find('input').attr('id', id)
- $new.find('input').prop('value', '')
- $new.appendTo('#array-items')
- });
- $('.dynamic-array-widget').on("click", '.del-array-item', function() {
- $(this).parent('li').remove();
- });
- </script>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement