11from collections import defaultdict
2+ import json
23from typing import (
4+ Any ,
35 Dict ,
46 Optional ,
57 Type ,
8+ Union ,
69)
710
811from django .db .models import QuerySet
912from django .http import HttpResponseRedirect
10- from django .template import (
11- Context ,
12- Template ,
13- )
13+ from django .template import Context
1414from django .utils .safestring import mark_safe
1515from django .utils .translation import gettext_lazy
1616
17- from iommi ._web_compat import render_template
17+ from iommi ._web_compat import (
18+ render_template ,
19+ Template ,
20+ )
1821from iommi .action import (
1922 Action ,
2023 Actions ,
2124 group_actions ,
2225)
23- from iommi .asset import Asset
26+ from iommi .sort_after import LAST
2427from iommi .base import (
2528 MISSING ,
2629 NOT_BOUND_MESSAGE ,
4649 Field ,
4750 Form ,
4851)
52+ from iommi .from_model import member_from_model
4953from iommi .member import (
5054 bind_member ,
5155 bind_members ,
5761 refinable ,
5862 EvaluatedRefinable ,
5963)
60- from iommi .shortcut import with_defaults
64+ from iommi .shortcut import (
65+ Shortcut ,
66+ with_defaults ,
67+ )
6168from iommi .struct import Struct
6269from iommi .table import (
70+ _column_factory_by_field_type ,
6371 Cell ,
6472 Cells ,
6573 Column ,
6674 Table ,
6775)
6876from iommi .evaluate import evaluate
6977
78+ from ._db_compat import base_defaults_factory
79+
80+ _edit_column_factory_by_field_type = {}
81+
82+
83+ def register_edit_column_factory (django_field_class , * , shortcut_name = MISSING , factory = MISSING , ** kwargs ):
84+ assert shortcut_name is not MISSING or factory is not MISSING
85+ if factory is MISSING :
86+ factory = Shortcut (call_target__attribute = shortcut_name , ** kwargs )
87+
88+ _edit_column_factory_by_field_type [django_field_class ] = factory
89+
7090
7191class EditCell (Cell ):
7292 def get_path (self ):
@@ -158,6 +178,23 @@ class EditColumn(Column):
158178
159179 field : Field = Refinable ()
160180
181+ @classmethod
182+ @dispatch (
183+ filter__call_target__attribute = 'from_model' ,
184+ bulk__call_target__attribute = 'from_model' ,
185+ )
186+ def from_model (cls , model = None , model_field_name = None , model_field = None , ** kwargs ):
187+ return member_from_model (
188+ cls = cls ,
189+ model = model ,
190+ factory_lookup = {** _column_factory_by_field_type , ** _edit_column_factory_by_field_type },
191+ factory_lookup_register_function = register_edit_column_factory ,
192+ model_field_name = model_field_name ,
193+ model_field = model_field ,
194+ defaults_factory = base_defaults_factory ,
195+ ** kwargs ,
196+ )
197+
161198 def on_refine_done (self ):
162199 super (EditColumn , self ).on_refine_done ()
163200 self .field = None
@@ -220,10 +257,17 @@ def hardcoded(cls, **kwargs):
220257
221258 @classmethod
222259 @with_defaults (
260+ # TODO this would be better, but it doesn't work and idk why
261+ # header__template=Template('<th{{ header.attrs }}></th>'),
223262 sortable = False ,
224- # field__call_target__attribute='hidden', # TODO TypeError(Field object has no refinable attribute(s): "call_target".)
263+ # TODO
264+ # field__call_target__attribute='hidden',
265+ # call_target raises TypeError(Field object has no refinable attribute(s): "call_target".)
266+ # so "temporary" fix to set attrs__type=hidden (works for inputs only):
267+ field__input__attrs__type = 'hidden' ,
225268 field__include = True ,
226- display_name = gettext_lazy ('Reorder' ),
269+ cell__attrs__title = gettext_lazy ('Drag and drop to reorder' ),
270+ after = LAST ,
227271 ** {
228272 'cell__attrs__class__reordering-handle-cell' : True ,
229273 'field__input__attrs__data-reordering-value' : True ,
@@ -378,7 +422,7 @@ class EditTable(Table):
378422 form_class : Type [Form ] = Refinable ()
379423 parent_form : Optional [Form ] = Refinable ()
380424 edit_actions : Dict [str , Action ] = RefinableMembers ()
381- reorderable : bool = EvaluatedRefinable ()
425+ reorderable : Union [ bool , Dict [ str , Any ], None ] = EvaluatedRefinable ()
382426
383427 class Meta :
384428 form_class = Form
@@ -404,8 +448,7 @@ class Meta:
404448 create_form = EMPTY
405449 container__children__text__template = 'iommi/table/edit_table_container.html'
406450
407- reorderable = lambda table , ** _ : not table .sortable
408- tbody__attrs = {'data-reorderable' : lambda table , ** _ : evaluate (table .reorderable , ** table .iommi_evaluate_parameters ()) or None }
451+ reorderable = False
409452
410453 bulk__include = True
411454
@@ -508,6 +551,29 @@ def on_refine_done(self):
508551 if self .create_form is not None :
509552 self .create_form = self .create_form .refine_defaults (fields = declared_fields ).refine_done ()
510553
554+ def bind (self , * , parent = None , request = None ):
555+ result = super (EditTable , self ).bind (parent = parent , request = request )
556+
557+ if result is None :
558+ return result
559+
560+ reorder_handles = []
561+ for column_name , column in items (result .columns ):
562+ if 'reorder_handle' in getattr (column , 'iommi_shortcut_stack' , []):
563+ reorder_handles .append (column_name )
564+ if reorder_handles :
565+ assert len (reorder_handles ) == 1 , "You cannot have multiple EditColumn.reorder_handle in an EditTable!"
566+ if not result .reorderable :
567+ result .reorderable = True
568+ result .sortable = False
569+ if result .reorderable :
570+ result .tbody .attrs ['data-reorderable' ] = json .dumps (result .reorderable ) if isinstance (result .reorderable , dict ) else result .reorderable
571+
572+ if result .sortable and result .reorderable :
573+ raise NotImplementedError ("sortable and reorderable cannot be used simultaneously" )
574+
575+ return result
576+
511577 def on_bind (self ) -> None :
512578 super (EditTable , self ).on_bind ()
513579 bind_members (self , name = 'edit_actions' )
@@ -532,18 +598,6 @@ def on_bind(self) -> None:
532598
533599 self .tbody .children .text = _EditTable_Lazy_tbody (self )
534600
535- reorder_handles = []
536- for column_name , column in items (self .columns ):
537- if 'reorder_handle' in getattr (column , 'iommi_shortcut_stack' , []):
538- reorder_handles .append (column_name )
539- if reorder_handles :
540- assert len (reorder_handles ) == 1 , "You cannot have multiple EditColumn.reorder_handle in an EditTable!"
541- self .reorderable = True
542- self .sortable = False
543-
544- if self .sortable and self .reorderable :
545- raise ValueError ("sortable and reorderable cannot be used simultaneously" )
546-
547601 def is_valid (self ):
548602 return not self .edit_errors and not self .create_errors
549603
0 commit comments