Django рдкреНрд░рдкрддреНрд░ рдлрд╝реАрд▓реНрдб - рдиреЗрд╕реНрдЯреЗрдб рддрд╛рд▓рд┐рдХрд╛

рд╢реБрдн рджреЛрдкрд╣рд░, рдкреНрд░рднрд╛рддред

рдореИрдВ XML рд╕реНрд╡рд░реВрдк рдореЗрдВ рдбреЗрдЯрд╛ рд╕рдВрдЧреНрд░рд╣рдг рдХреЗ рд╕рд╛рде "рдиреЗрд╕реНрдЯреЗрдб рдЯреЗрдмрд▓" рдкреНрд░рдХрд╛рд░ рдХреЗ django рдкреНрд░рдкрддреНрд░ рдлрд╝реАрд▓реНрдб рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд╕рд╛рде рдПрдХ рд▓реЗрдЦ рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рдХрд░рддрд╛ рд╣реВрдВред
рдпрд╣ рд░реБрдЪрд┐ рд░рдЦрдиреЗ рд╡рд╛рд▓реЛрдВ рдХреЛ рдХреНрд╖реЗрддреНрд░ рдФрд░ рдбреАрдЬреЗрдВрдЧреЛ рд╡рд┐рдЬреЗрдЯ рдХреЗ рдХрд╛рдо рдХреЛ рдмреЗрд╣рддрд░ рдврдВрдЧ рд╕реЗ рд╕рдордЭрдиреЗ рдФрд░ рдХрд┐рд╕реА рднреА рдордирдорд╛рдиреЗ рдХреНрд╖реЗрддреНрд░ рдмрдирд╛рдиреЗ рдХреА рджрд┐рд╢рд╛ рдореЗрдВ рдПрдХ рдХрджрдо рдЙрдард╛рдиреЗ рдореЗрдВ рдорджрдж рдХрд░реЗрдЧрд╛ред
рдпрджрд┐ рдЖрдк рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЬрд╛рдирддреЗ рд╣реИрдВ, рддреЛ рд▓реЗрдЦ рдЖрдкрдХреЗ рд▓рд┐рдП рджрд┐рд▓рдЪрд╕реНрдк рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред





Django рдкрд░ рдПрдХ рд╡рд░реНрдХрдлрд╝реНрд▓реЛ рдХреЗ рд▓рд┐рдП
рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдлрд╝реАрд▓реНрдб (рддрд╛рд▓рд┐рдХрд╛) рдореЗрдВ рд╕рдВрд░рдЪрд┐рдд рддрддреНрд╡реЛрдВ рдХреА рдПрдХ рд╕рд░рдгреА рдХреЗ рдЗрдирдкреБрдЯ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред

рд╡рд┐рдХрд▓реНрдкреЛрдВ рдХреЗ рдмреАрдЪ рд╡рд┐рдЪрд╛рд░-рд╡рд┐рдорд░реНрд╢ рдХреЗ рдПрдХ рд╕рдкреНрддрд╛рд╣ рдХреЗ рдмрд╛рдж
- рдЗрдирд▓рд╛рдЗрди рдлреЙрд░рдореЗрдЯ
- рд╕рдВрд▓рдЧреНрди рджрд╕реНрддрд╛рд╡реЗрдЬ (рдРрд╕реА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдкрд╣рд▓реЗ рд╕реЗ рдореМрдЬреВрдж рд╣реИ)
рдХрд╕реНрдЯрдо рдХреНрд╖реЗрддреНрд░ / рд╡рд┐рдЬреЗрдЯ XML / JSON рдореЗрдВ рдХреНрд░рдорд╛рдВрдХрди рдХреЗ рд╕рд╛рде
XML рдореЗрдВ рдлрд╝реЙрд░реНрдореЗрдЯ рдЪреБрдирд╛ рдЧрдпрд╛ рдерд╛

рдЗрдирд▓рд╛рдЗрди рдлреЙрд░рдореЗрдЯ рдХреЛ рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреА рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдЬрдЯрд┐рд▓рддрд╛ рдХреЗ рдХрд╛рд░рдг рдЕрд╕реНрд╡реАрдХрд╛рд░ рдХрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛:
- рдЗрд╕рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рдмрд╛рдж рд╣реА рдЗрдирд▓рд╛рдЗрди рдХреЛ рдмрдЪрд╛рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИ (рд╣рдо рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдХреЛ рдмрдЪрд╛рдиреЗ рдХреА рд╡рд┐рдзрд┐ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реЛ рдЬрд╛рддреЗ рд╣реИрдВ)
- рдПрдХ рдЕрд▓рдЧ рдореЙрдбрд▓ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ,
- рдореЙрдбрд▓ рд░реВрдкреЛрдВ

рд╕рдВрд▓рдЧреНрди рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рднреА рдлрд┐рдЯ рдирд╣реАрдВ рд╣реБрдП (рдкреНрд░рддреНрдпреЗрдХ рдРрд╕реЗ рдХреНрд╖реЗрддреНрд░ рдХреЗ рд▓рд┐рдП рдЕрдкрдирд╛ рд╕реНрд╡рдпрдВ рдХрд╛ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рд╕рдВрд░рдЪрдирд╛ рди рдмрдирд╛рдПрдВ)

рдХрд╕реНрдЯрдо рдХреНрд╖реЗрддреНрд░ рдХреЗ рд╡рд┐рдЪрд╛рд░ рдиреЗ рдЕрдзрд┐рдХ рдЖрдХрд░реНрд╖рд┐рдд рдХрд┐рдпрд╛ред
рдЖрдк рд╕рднреА рддрд░реНрдХ рдХреНрд╖реЗрддреНрд░ / рд╡рд┐рдЬреЗрдЯ рдореЗрдВ рд░рдЦ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рднреВрд▓ рд╕рдХрддреЗ рд╣реИрдВред
рдпрд╣ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕рд┐рд╕реНрдЯрдо рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдореЗрдВ рдиреНрдпреВрдирддрдо рдЬрдЯрд┐рд▓рддрд╛ рдЬреЛрдбрд╝рддрд╛ рд╣реИред

JSON (рднрд╛рд░, рдбрдВрдк) рдХреЗ рд╕рд╛рде рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдХрд╛рдо рдХреЗ рдмрд╛рд╡рдЬреВрдж,
SQL рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдбреЗрдЯрд╛рдмреЗрд╕ рд╕реЗ рд░рд┐рдкреЛрд░реНрдЯ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреЗ рдХрд╛рд░рдг XML рдЪреБрдирд╛ рдЧрдпрд╛ рдерд╛ред
рдпрджрд┐ PostgreSQL JSON рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ, рддреЛ Oracle рдореЗрдВ рдпрд╣ рдХреЗрд╡рд▓ рд╕рдВрд╕реНрдХрд░рдг 12 рдХреЗ рд╕рд╛рде рджрд┐рдЦрд╛рдИ рджреЗрддрд╛ рд╣реИред
XML рдореЗрдВ рд╣реЗрд░рдлреЗрд░ рдХрд░рддреЗ рд╕рдордп, рдЖрдк xpath рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдбреЗрдЯрд╛рдмреЗрд╕-рд╕реНрддрд░реАрдп рдЗрдВрдбреЗрдХреНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

рдПрд╕рдХреНрдпреВрдПрд▓ рд╕реНрддрд░ рдХрд╛ рдХрд╛рдо
--  XML   select t.id, (xpath('/item/@n_phone', nt))[1] as n_phone1, (xpath('/item/@is_primary', nt))[1] as is_primary1, (xpath('/item/@n_phone', nt))[2] as n_phone2, (xpath('/item/@is_primary', nt))[2] as is_primary2 from docflow_document17 t cross join unnest(xpath('/xml/item', t.nested_table::xml)) as nt; --   XML- select t.id from docflow_document17 t where t.id = 2 and ('1231234', 'False') in ( select (xpath('/item/@n_phone', nt_row))[1]::text, (xpath('/item/@is_primary', nt_row))[1]::text from unnest(xpath('/xml/item', t.nested_table::xml)) as nt_row ); 



рдкреНрд░рд╛рд░рдВрдн рдореЗрдВ, рдПрдХ рдХрд╛рдордХрд╛рдЬреА рд╡рд┐рдЬреЗрдЯ рддреБрд░рдВрдд рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рдерд╛, рдЬреЛ
- рдПрдХреНрд╕рдПрдордПрд▓ рдХреЛ рд░реЗрдВрдбрд░ рдореЗрдердб рдореЗрдВ рд╕реНрд╡реАрдХрд╛рд░ рдХрд┐рдпрд╛ рдЧрдпрд╛
- рдЙрддреНрдкрдиреНрди рдФрд░ рдкреНрд░рджрд░реНрд╢рд┐рдд рдлреЙрд░реНрдореЗрдЯ
- formet value_from_datadict рдореЗрдВ рдЬрдирд░реЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдбреЗрдЯрд╛ рдкреИрд░рд╛рдореАрдЯрд░ рд▓реЗ рд░рд╣рд╛ рд╣реИ, рдорд╛рдиреНрдп рдХрд░ рд░рд╣рд╛ рд╣реИ, XML рдПрдХрддреНрд░рд┐рдд рдХрд░ рд░рд╣рд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдереВрдХ рд░рд╣рд╛ рд╣реИ
рдпрд╣ рд╕рдм рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рдерд╛ рдФрд░ рдмрд╣реБрдд рд╕рд░рд▓ рдерд╛ред
 class XMLTableWidget(widgets_django.Textarea): class Media: js = (settings.STATIC_URL + 'forms_custom/xmltable.js',) def __init__(self, formset_class, attrs=None): super(XMLTableWidget, self).__init__(attrs=None) self._Formset = formset_class def render(self, name, value, attrs=None): initial = [] if value: xml = etree.fromstring(value) for row in xml: initial.append(row.attrib) formset = self._Formset(initial=initial, prefix=name) return render_to_string('forms_custom/xmltable.html', {'formset': formset}) def value_from_datadict(self, data, files, name): u"""    ,    XML  -  formset-  ,    initial- :    formset-  ,       """ formset_data = {k: v for k, v in data.items() if k.startswith(name)} formset = self._Formset(data=formset_data, prefix=name) if formset.is_valid(): from lxml.builder import E xml_items = [] for item in formset.cleaned_data: if item and not item[formset_deletion_field_name]: del item[formset_deletion_field_name] item = {k: unicode(v) for k, v in item.items()} xml_items.append(E.item("", item)) xml = E.xml(*xml_items) return etree.tostring(xml, pretty_print=False) else: initial_value = data.get('initial-%s' % name) if initial_value: return initial_value else: raise Exception(_('Error in table and initial not find')) 



рдпрджрд┐ рдПрдХ рдХреИрд╡рд┐рдЯреА рдХреЗ рд▓рд┐рдП рдирд╣реАрдВ: рд╕рд╛рдорд╛рдиреНрдп рдлреЙрд░реНрдореЗрдЯ рд╕рддреНрдпрд╛рдкрди рдХреА рдЕрд╕рдВрднрд╡рддрд╛ред
рдЖрдк рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рдлреЙрд░реНрдореЗрдЯ рдХреЛ рдпрдерд╛рд╕рдВрднрд╡ рдирд░рдо рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ, рдПрдХреНрд╕рдПрдордПрд▓ рдХреЛ рдкрдХрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдХреНрд╖реЗрддреНрд░ рдпрд╛ рдлреЙрд░реНрдо рд╕реНрддрд░ рдкрд░ рдбреЗрдЯрд╛ рдХреА рдЬрд╛рдВрдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдЖрдк рд╢рд╛рдпрдж рд╡рд┐рдЬреЗрдЯ рдореЗрдВ is_formset_valid рд╡рд┐рд╢реЗрд╖рддрд╛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕реЗ self.widget.is_formset_valid рдкреНрд░рдХрд╛рд░ рдХреЗ рдХреНрд╖реЗрддреНрд░ рд╕реЗ рдЬрд╛рдВрдЪ рд╕рдХрддреЗ рд╣реИрдВ,
рд▓реЗрдХрд┐рди рдпрд╣ рдХрд┐рд╕реА рддрд░рд╣ рдЦрд░рд╛рдм рд╣реЛ рдЧрдпрд╛ред

рдлрд╝реАрд▓реНрдб рдФрд░ рд╡рд┐рдЬреЗрдЯ рдХрд╛ рдПрдХ рд╕рдВрдпреБрдХреНрдд рдХрд╛рдо рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред
рдпрд╣рд╛рдБ рдкрд░рд┐рдгрд╛рдо рд╣реИред

рдореИрдВрдиреЗ рддрдп рдХрд┐рдпрд╛ рдХрд┐ рд╕реНрд░реЛрдд рдХреЛрдб рдХреЛ рдлрд┐рд░ рд╕реЗ рдкрдврд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реЗрд╢рд╛рди рди рдХрд░реЗрдВред
рдЗрд╕рдХреЗ рдмрдЬрд╛рдп, рддрд░реАрдХреЛрдВ рдкрд░ рдЕрддреНрдпрдзрд┐рдХ рдЯрд┐рдкреНрдкрдгреА рдХреАред
рдореБрдЦреНрдп рд╡рд┐рдЪрд╛рд░ рд╡рд┐рднрд┐рдиреНрди рдЗрдирдкреБрдЯ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдорд╛рдирдХреАрдХреГрдд рдХрд░рдирд╛ рд╣реИ:
- рдлреАрд▓реНрдб рдЗрдирд┐рд╢рд┐рдпрд▓рд╛рдЗрдЬрд╝реЗрд╢рди рдХреЗ рджреМрд░рд╛рди XML рдкреНрд░рд╛рдкреНрдд рд╣реБрдЖ
- рд╡рд┐рдЬреЗрдЯ рдХреЗ рдЖрдЙрдЯрдкреБрдЯ рдкрд░ рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде рд╢рдмреНрджрдХреЛрд╢
- рдЙрдЪрд┐рдд рд░реВрдк рд╕реЗ рддреИрдпрд╛рд░ рдбрд┐рдЬрд╛рдЗрди
{"рдлреЙрд░рдореЗрдЯ": рдлрд╝реЙрд░реНрдореЗрдЯ, "xml_initial": xml_string} рдЬреИрд╕реЗ рдПрдХрд▓ рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдХрдирд╡рд░реНрдЯ рдХрд░реЗрдВ
рдФрд░ рдлрд┐рд░ "рдкреНрд░реМрджреНрдпреЛрдЧрд┐рдХреА рдХрд╛ рдорд╛рдорд▓рд╛"

XMLTableField рдлрд╝реАрд▓реНрдб
 class XMLTableField(fields.Field): widget = widgets_custom.XMLTableWidget hidden_widget = widgets_custom.XMLTableHiddenWidget default_error_messages = {'invalid': _('Error in table')} def __init__(self, formset_class, form_prefix, *args, **kwargs): kwargs['show_hidden_initial'] = True #       super(XMLTableField, self).__init__(*args, **kwargs) self._formset_class = formset_class self._formset_prefix = form_prefix self._procss_widget_data_cache = {} self._prepare_value_cache = {} def prepare_value(self, value): u"""                    unicode,   XML,        initial  ,     POST-,   ,   ,     formset,  xml_initial   hidden_initial .      show_hidden_initial = True     ,    . """ if value is None: return {'xml_initial': value, 'formset': self._formset_class(initial=[], prefix=self._formset_prefix)} elif type(value) == unicode: value_hash = hash(value) if value_hash not in self._prepare_value_cache: initial = [] if value: xml = etree.fromstring(value) for row in xml: #    'False'  False, #    XML    , #     bool attrs = {} for k,v in row.attrib.items(): attrs[k] = False if v == 'False' else v initial.append(attrs) formset = self._formset_class(initial=initial, prefix=self._formset_prefix) self._prepare_value_cache[value_hash] = formset return {'xml_initial': value, 'formset': self._prepare_value_cache[value_hash]} elif type(value) == dict: if 'xml' not in value: formset = self._widget_data_to_formset(value) return {'xml_initial': value['initial'], 'formset': formset} return value def clean(self, value): u"""       ,  ,    formset-,    formset   XML   _formset_to_xml   ValidationError,  formset   """ formset = self._widget_data_to_formset(value, 'clean') return self._formset_to_xml(formset) def _formset_to_xml(self, formset): u"""   XML    .   _has_changed    XML   clean    cleaned_data """ if formset.is_valid(): from lxml.builder import E xml_items = [] for item in formset.cleaned_data: if item and not item.get(formset_deletion_field_name, False): if formset_deletion_field_name in item: del item[formset_deletion_field_name] item = {k: unicode(v) for k, v in item.items()} xml_items.append(E.item("", item)) xml = E.xml(*xml_items) xml_str = etree.tostring(xml, pretty_print=False) return xml_str else: raise ValidationError(self.error_messages['invalid'], code='invalid') def _widget_data_to_formset(self, value, call_from=None): u"""   POST-,   formset-   ,    prepare_value     ,     FormSet-      """ #     -   self.prepare_value formset_hash = hash(frozenset(value.items())) if formset_hash not in self._procss_widget_data_cache: formset = self._formset_class(data=value, prefix=self._formset_prefix) self._procss_widget_data_cache[formset_hash] = formset return formset else: return self._procss_widget_data_cache[formset_hash] def _has_changed(self, initial, data): u"""     .     formset   ,   XML   c  ,   initial-   XML """ formset = self._widget_data_to_formset(data) try: data_value = self._formset_to_xml(formset) except ValidationError: return True return data_value != initial 



XMLTableHiddenWidget
 class XMLTableHiddenWidget(widgets_django.HiddenInput): def render(self, name, value, attrs=None): u"""    xml_initial    render """ value = value['xml_initial'] return super(XMLTableHiddenWidget, self).render(name, value, attrs) 



XMLTableWidget
 class XMLTableWidget(widgets_django.Widget): class Media: js = (settings.STATIC_URL + 'forms_custom/xmltable.js',) def render(self, name, value, attrs=None): u"""    formset,   initial   data   ,     """ formset = value['formset'] return render_to_string('forms_custom/xmltable.html', {'formset': formset}) def value_from_datadict(self, data, files, name): u"""    ,   formset-     clean     ,  initial-,        """ formset_data = {k: v for k, v in data.items() if k.startswith(name)} initial_key = 'initial-%s' % name formset_data['initial'] = data[initial_key] return formset_data 



рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдореБрдЦреНрдп рдХрд╛рд░реНрдп рдЕрдзрд┐рдХрддрдо рдХреЙрдореНрдкреИрдХреНрдЯрдиреЗрд╕ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдирд╛ рдерд╛
XMLTableWidget - рдЯреЗрдореНрдкрд▓реЗрдЯ
 {% load base_tags %} {% load base_filters %} {{formset.management_form}} {% if formset.non_field_errors %} <div class='alert alert-danger'> {% for error in form.non_field_errors %} {{ error }}<br/> {% endfor %} </div> {% endif %} <table> {% for form in formset %} {% if forloop.first %} <tr> {% for field in form.visible_fields %} {% if field.name == 'DELETE' %} <td></td> {% else %} <td>{{field.label}}</td> {% endif %} {% endfor %} </tr> {% endif %} <tr> {% for field in form.visible_fields %} {% if field.name == 'DELETE' %} <th > <div class='hide'>{{field}}</div> <a onclick="xmltable_mark_deleted(this, '{{field.auto_id}}')" class="pointer"> <span class="glyphicon glyphicon-remove"></span> </a> </th> {% else %} <td> {{ field|add_widget_css:"form-control" }} {% if field.errors %} <span class="help-block"> {% for error in field.errors %} {{ error }}<br/> {% endfor %} </span> {% endif %} </td> {% endif %} {% endfor %} </tr> {% endfor %} </table> 



рдорд╛рдирдХ рдПрдХреНрд╕рдмреЙрдХреНрд╕ рдХреЛ "X" рдЖрдЗрдХрди рд╕реЗ рдмрджрд▓реЗрдВ
рдФрд░ рд╣рдо рд▓рд╛рдЗрди рдХреЛ рддрдм рд╣рдЯрд╛рдПрдВрдЧреЗ рдЬрдм рдЗрд╕реЗ рд╣рдЯрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЪрд┐рдиреНрд╣рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛
XMLTableWidget - рд╕реНрдХреНрд░рд┐рдкреНрдЯ
 function xmltable_mark_deleted(p_a, p_checkbox_id) { var chb = $('#' + p_checkbox_id) var row = $(p_a).parents('tr') if(chb.prop('checked')) { chb.removeProp('checked') row.css('background-color', 'white') } else { chb.attr('checked', '1') row.css('background-color', '#f2dede') } } 



рдпрд╣ рдореВрд▓ рд░реВрдк рд╕реЗ рд╕рднреА рд╣реИред
рдЕрдм рд╣рдо рдЗрд╕ рдлрд╝реАрд▓реНрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЬрдЯрд┐рд▓ рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЙрдиреНрд╣реЗрдВ рдЖрд╡рд╢реНрдпрдХрддрд╛рдиреБрд╕рд╛рд░ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ
рдФрд░ рдмрд╣реБрдд рдЬрдЯрд┐рд▓ рдкреНрд░рдгрд╛рд▓реА рдХреЛрдб рдирд╣реАрдВ

рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдХреЗрд╡рд▓ рдлреЙрд░реНрдорд╕реЗрдЯ рддреИрдпрд╛рд░ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:
XMLTableWidget
 class NestedTableForm(forms.Form): phone_type = forms.ChoiceField(label=u"", choices=[('', '---'), ('1', '.'), ('2', '.')], required=False) n_phone = forms.CharField(label=u"", required=False) is_primary = forms.BooleanField(label=u"", required=False, widget=forms.CheckboxInput(check_test=boolean_check)) nested_table_formset_class = formset_factory(NestedTableForm, can_delete=True) 



рдФрд░ рдЗрд╕ рдХреНрд╖реЗрддреНрд░ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВред

рдпрд╣рд╛рдБ django рдХреЗ рдЖрд╡реЗрджрди рдХреЗ рд╕рд╛рде рднрдВрдбрд╛рд░ рдХрд╛ рд▓рд┐рдВрдХ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдЖрдк рдЗрд╕ рдХреНрд╖реЗрддреНрд░ рдореЗрдВ рдкрд╛ рд╕рдХрддреЗ рд╣реИрдВред
рдЖрдк рдпрд╛ рддреЛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХрдиреЗрдХреНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдпрд╛ рдлрд╝реАрд▓реНрдб / рд╡рд┐рдЬреЗрдЯ / рдЯреЗрдореНрдкрд▓реЗрдЯ / рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХреЗ рдХреЛрдб рдХреЛ рдХрд╣реАрдВ рднреА рдХреЙрдкреА рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
bitbucket.org/dibrovsd/django_forms_custom/src

Source: https://habr.com/ru/post/In213701/


All Articles