class TestTable(Table): a = Column.number( sortable=False, query__show=True, query__gui__show=True ) # turn off sorting to not get the link with random query params b = Column.substring(query__show=True, query__gui__show=True) class Meta: sortable = False
class FooTable(Table): a = Column.number( ) # This is a shortcut that results in the css class "rj" (for right justified) being added to the header and cell b = Column() c = Column(cell__format=lambda table, column, row, value: value[-1] ) # Display the last value of the tuple sum_c = Column(cell__value=lambda table, column, row: sum(row.c), sortable=False) # Calculate a value not present in Foo
class TestTable(Table): class Meta: endpoint__data = lambda table, key, value: [{ cell.bound_column.name: cell.value for cell in row } for row in table] foo = Column() bar = Column()
class TestTable(Table): class Meta: attrs = { 'class': lambda table: 'listview', 'id': lambda table: 'table_id', } foo = Column() bar = Column.number()
class TestTable(Table): foo__a = Column.number() foo__b = Column() foo = Column.choice_queryset( model=Foo, choices=lambda table, column: Foo.objects.all(), query__show=True, bulk__show=True, query__gui__show=True)
class BarTable(Table): select = Column.select( ) # Shortcut for creating checkboxes to select rows b__a = Column.number( # Show "a" from "b". This works for plain old objects too. query__show=True, # put this field into the query language query__gui__show=True ) # put this field into the simple filtering GUI c = Column( bulk__show=True, # Enable bulk editing for this field query__show=True, query__gui__show=True)
class TestTable(Table): class Meta: attrs = { 'class': 'listview', 'id': 'table_id', } foo = Column() bar = Column.number() icon = Column.icon('history', is_report, group="group") edit = Column.edit(is_report, group="group") delete = Column.delete(is_report)
def explicit_table(): columns = [ Column(name="foo"), Column.number(name="bar"), ] return Table(data=get_data(), columns=columns, attrs=lambda table: { 'class': 'listview', 'id': 'table_id' })
class TestTable(NoSortTable): icon = Column.icon(is_report) edit = Column.edit(is_report) delete = Column.delete(is_report) download = Column.download(is_report) run = Column.run(is_report) select = Column.select(is_report) boolean = Column.boolean(is_report) link = Column.link(cell__format="Yadahada name") number = Column.number()
def list(request, model, title): return render_table_to_response( request=request, table__model=model, table__column__name__cell__url=lambda row, **_: f'{row.pk}/', table__extra_fields=[ Column.edit(after=0, cell__url=lambda row, **_: f'{row.pk}/edit/'), Column.delete(after=0, cell__url=lambda row, **_: f'{row.pk}/delete/'), ], template='wiki/list.html', context=dict(title=evaluate(title, model=model), ), links=[ Link(f'Create {model._meta.verbose_name}', attrs__href='create/') ])
def foreign_key_factory(model_field, **kwargs): setdefaults_path( kwargs, choices=model_field.foreign_related_fields[0].model.objects.all(), model=model_field.foreign_related_fields[0].model, ) return Column.choice_queryset(model_field=model_field, **kwargs)
def foreign_key_factory(model_field, **kwargs): setdefaults( kwargs, dict(choices=model_field.foreign_related_fields[0].model. objects.all())) kwargs['model'] = model_field.foreign_related_fields[0].model return Column.choice_queryset(**kwargs)
def explicit_table(): columns = [ Column(name="foo"), Column.number(name="bar"), ] return Table(data=get_data(), columns=columns, attrs=lambda table: {'class': 'listview', 'id': 'table_id'})
class TestTable(Table): foo = Column.choice_queryset( model=Foo, choices=lambda table, column: Foo.objects.all(), query__gui__extra__endpoint_attr='b', query__show=True, bulk__show=True, query__gui__show=True)
class TestTable(NoSortTable): class Meta: attrs__class__classy = True attrs__foo = lambda table: 'bar' row__attrs__class__classier = True row__attrs__foo = lambda table: "barier" yada = Column()
class FooTable(Table): foo = Column.choice_queryset( query__show=True, query__gui__show=True, bulk__show=True, choices=lambda table, column: Foo.objects.filter(a=1)) class Meta: model = Foo
class TestTable(NoSortTable): class Meta: attrs = {'class': 'classy', 'foo': lambda table: 'bar'} row__attrs = { 'class': 'classier', 'foo': lambda table, row: "barier" } yada = Column()
class BarTable(Table): select = Column.select( ) # Shortcut for creating checkboxes to select rows b__a = Column.number( ) # Show "a" from "b". This works for plain old objects too. b = Column.choice_queryset( show=False, choices=Foo.objects.all(), model=Foo, bulk__show=True, query__show=True, query__gui__show=True, ) c = Column(bulk=True) # The form is created automatically d = Column( display_name='Display name', css_class={'css_class'}, url='url', title='title', sortable=False, group='Foo', auto_rowspan=True, cell__value=lambda table, column, row: row.b.a // 3, cell__format=lambda table, column, row, value: '- %s -' % value, cell__attrs__class__cj=True, cell__attrs__title='cell title', cell__url='url', cell__url_title='cell url title') e = Column(group='Foo', cell__value='explicit value', sortable=False) f = Column(show=False, sortable=False) g = Column(attr='c', sortable=False) django_templates_for_cells = Column( sortable=False, cell__value=None, cell__template='kitchen_sink_cell_template.html') class Meta: model = Bar
def inline_edit_select(**kwargs): def inline_edit_select_cell_format(table, column, row, value): options = '<option value=""></option>' + "\n".join( [ '<option value="%s"%s>%s</option>' % (choice.pk, ' selected="selected"' if choice == value else "", choice) for choice in evaluate(kwargs["choices"], table=table, column=column, row=row, value=value) ] ) return mark_safe( '<select class="inline_editable_select" edit_url="%sedit/%s/" id="%s">%s</select>' % (row.get_absolute_url(), column.name, row.pk, options) ) setdefaults(kwargs, dict(cell__format=inline_edit_select_cell_format, query__show=True)) return ColumnBase.choice_queryset(**kwargs)
def test_query_namespace_inject(): class FooException(Exception): pass def post_validation(form): del form raise FooException() with pytest.raises(FooException): foo = Table(data=[], model=Foo, request=Struct(method='POST', POST={'-': '-'}, GET=Struct(urlencode=lambda: None)), columns=[ Column(name='foo', query__show=True, query__gui__show=True) ], query__gui__post_validation=post_validation) foo.prepare(foo.request)
def inline_edit_select(**kwargs): def inline_edit_select_cell_format(table, column, row, value): options = '<option value=""></option>' + '\n'.join([ '<option value="%s"%s>%s</option>' % (choice.pk, ' selected="selected"' if choice == value else '', choice) for choice in evaluate(kwargs['choices'], table=table, column=column, row=row, value=value) ]) return mark_safe( '<select class="inline_editable_select" edit_url="%sedit/%s/" id="%s">%s</select>' % (row.get_absolute_url(), column.name, row.pk, options)) setdefaults( kwargs, dict( cell__format=inline_edit_select_cell_format, query__show=True, )) return ColumnBase.choice_queryset(**kwargs)
def render_room(request, room_pk, **kwargs): # TODO: @dispatch on this view, and params to be able to customize rendering of the room room = get_object_or_404(Room, pk=room_pk) user_time = get_user_time(user=request.user, identifier=f'forum/room:{room.pk}') show_hidden = bool_parse(request.GET.get('show_hidden', '0')) def unread_from_here_href(row: Message, **_): params = request.GET.copy() params.setlist('unread_from_here', [row.last_changed_time.isoformat()]) return mark_safe('?' + params.urlencode() + "&") if 'time' in request.GET: unread2_time = datetime.fromisoformat(request.GET['time']) else: unread2_time = datetime.now() # NOTE: there's a set_user_time at the very bottom of this function if 'unread_from_here' in request.GET: user_time = datetime.fromisoformat(request.GET['unread_from_here']) # TODO: show many pages at once if unread? Right now we show the first unread page. start_page = None if 'page' not in request.GET: # Find first unread page try: first_unread_message = Message.objects.filter( room=room, last_changed_time__gte=user_time).order_by('path')[0] messages_before_first_unread = room.message_set.filter( path__lt=first_unread_message.path).count() start_page = messages_before_first_unread // PAGE_SIZE except IndexError: pass messages = Message.objects.filter(room__pk=room_pk).prefetch_related( 'user', 'room') if not show_hidden: messages = messages.filter(visible=True) def is_unread(row, **_): return row.last_changed_time >= user_time def is_unread2(row, **_): return row.last_changed_time >= unread2_time and not is_unread(row=row) def preprocess_data(data, table, **_): data = list(data) first_new = None for d in data: if is_unread(row=d): first_new = d break table.extra.unread = first_new is not None first_new_or_last_message = first_new if first_new_or_last_message is None and data: first_new_or_last_message = data[-1] if first_new_or_last_message is not None: # This is used by the view first_new_or_last_message.first_new = True return data result = render_table( request, template=get_template('forum/room.html'), paginator=RoomPaginator(messages), context=dict( obj=room, # required for header.html room=room, show_hidden=show_hidden, time=unread2_time or user_time, is_subscribed=is_subscribed(user=request.user, identifier=f'forum/room:{room.pk}'), is_mobile=request.user_agent.is_mobile, **kwargs, ), table__data=messages, table__exclude=['path'], table__extra_fields=[ Column(name='unread_from_here_href', attr=None, cell__value=unread_from_here_href), ], table__preprocess_data=preprocess_data, table__header__template=Template(''), table__row__template=get_template('forum/message.html'), table__row__attrs=dict( class__indent_0=lambda row, **_: row.indent == 0, class__message=True, class__current_user=lambda row, **_: request.user == row.user, class__other_user=lambda row, **_: request.user != row.user, class__unread=is_unread, class__unread2=is_unread2, ), table__attrs__cellpadding='0', table__attrs__cellspacing='0', table__attrs__id='first_newtable', table__attrs__align='center', table__attrs__class__roomtable=True, table__paginator__template='forum/blank.html', page=start_page, ) if 'unread_from_here' not in request.GET: user_time = datetime.now() set_user_time(user=request.user, identifier=f'forum/room:{room.pk}', time=user_time) return result
class TestTable(NoSortTable): foo = Column(display_name="Bar")
class TestTable(NoSortTable): foo = Column(auto_rowspan=True)
def setup_db_compat_django(): from tri.table import Column, register_column_factory try: # noinspection PyUnresolvedReferences from django.db.models import IntegerField, FloatField, TextField, BooleanField, AutoField, CharField, CommaSeparatedIntegerField, DateField, DateTimeField, DecimalField, EmailField, URLField, TimeField, ForeignKey, ManyToOneRel, ManyToManyField, ManyToManyRel # from django.db.models.fields.reverse_related import ManyToManyRel as ManyToManyRel_Related except ImportError: pass else: # The order here is significant because of inheritance structure. More specific must be below less specific. register_column_factory(CharField, lambda model_field, **kwargs: Column(**kwargs)) register_column_factory(URLField, lambda model_field, **kwargs: Column.url(**kwargs)) register_column_factory(TimeField, lambda model_field, **kwargs: Column.time(**kwargs)) register_column_factory(EmailField, lambda model_field, **kwargs: Column.email(**kwargs)) register_column_factory(DecimalField, lambda model_field, **kwargs: Column.decimal(**kwargs)) register_column_factory(DateField, lambda model_field, **kwargs: Column.date(**kwargs)) register_column_factory(DateTimeField, lambda model_field, **kwargs: Column.datetime(**kwargs)) register_column_factory(CommaSeparatedIntegerField, lambda model_field, **kwargs: Column.comma_separated(parent_field=Column.integer(**kwargs))) register_column_factory(BooleanField, lambda model_field, **kwargs: Column.boolean(**kwargs)) register_column_factory(TextField, lambda model_field, **kwargs: Column.text(**kwargs)) register_column_factory(FloatField, lambda model_field, **kwargs: Column.float(**kwargs)) register_column_factory(IntegerField, lambda model_field, **kwargs: Column.integer(**kwargs)) register_column_factory(AutoField, lambda model_field, **kwargs: Column.integer(**setdefaults(kwargs, dict(show=False)))) register_column_factory(ManyToOneRel, None) register_column_factory(ManyToManyField, lambda model_field, **kwargs: Column.multi_choice_queryset(**setdefaults(kwargs, dict(choices=model_field.rel.to._default_manager.all())))) register_column_factory(ManyToManyRel, None) # register_field_factory(ManyToManyRel_Related, None) def foreign_key_factory(model_field, **kwargs): setdefaults(kwargs, dict( choices=model_field.foreign_related_fields[0].model.objects.all() )) kwargs['model'] = model_field.foreign_related_fields[0].model return Column.choice_queryset(**kwargs) register_column_factory(ForeignKey, foreign_key_factory)
def setup(): from tri.table import Column from django.db.models import IntegerField, FloatField, TextField, BooleanField, AutoField, CharField, CommaSeparatedIntegerField, DateField, DateTimeField, DecimalField, EmailField, URLField, TimeField, ForeignKey, ManyToOneRel, ManyToManyField, ManyToManyRel # from django.db.models.fields.reverse_related import ManyToManyRel as ManyToManyRel_Related def foreign_key_factory(model_field, **kwargs): setdefaults(kwargs, dict( choices=model_field.foreign_related_fields[0].model.objects.all() )) kwargs['model'] = model_field.foreign_related_fields[0].model return Column.choice_queryset(**kwargs) # The order here is significant because of inheritance structure. More specific must be below less specific. global _column_factory_by_django_field_type _column_factory_by_django_field_type = OrderedDict([ (CharField, lambda model_field, **kwargs: Column(**kwargs)), (URLField, lambda model_field, **kwargs: Column.url(**kwargs)), (TimeField, lambda model_field, **kwargs: Column.time(**kwargs)), (EmailField, lambda model_field, **kwargs: Column.email(**kwargs)), (DecimalField, lambda model_field, **kwargs: Column.decimal(**kwargs)), (DateField, lambda model_field, **kwargs: Column.date(**kwargs)), (DateTimeField, lambda model_field, **kwargs: Column.datetime(**kwargs)), (CommaSeparatedIntegerField, lambda model_field, **kwargs: Column.comma_separated(parent_field=Column.integer(**kwargs))), (BooleanField, lambda model_field, **kwargs: Column.boolean(**kwargs)), (TextField, lambda model_field, **kwargs: Column.text(**kwargs)), (FloatField, lambda model_field, **kwargs: Column.float(**kwargs)), (IntegerField, lambda model_field, **kwargs: Column.integer(**kwargs)), (AutoField, lambda model_field, **kwargs: Column.integer(**setdefaults(kwargs, dict(show=False)))), (ManyToOneRel, None), (ManyToManyField, lambda model_field, **kwargs: Column.multi_choice_queryset(**setdefaults(kwargs, dict(choices=model_field.rel.to._default_manager.all())))), (ManyToManyRel, None), # (ManyToManyRel_Related, None), (ForeignKey, foreign_key_factory), ])
class TestTable(NoSortTable): foo = Column(cell__template='test_cell_template.html', cell__format=explode, cell__url=explode, cell__url_title=explode)
class TestTable(NoSortTable): class Meta: header__template = None foo = Column()
class MyTable(Table): class Meta: columns = [Column(name='foo')] bar = Column()
def test_datetime(): x = Column.datetime() assert getattr_path(x, "query__class") == Variable.datetime assert getattr_path(x, "bulk__class") == Field.datetime
def test_float(): x = Column.float() assert getattr_path(x, "query__class") == Variable.float assert getattr_path(x, "bulk__class") == Field.float
class TestTable(Table): a = Column.number( sortable=False, bulk__show=True ) # turn off sorting to not get the link with random query params b = Column(bulk__show=True)
class TestTable(NoSortTable): foo = Column(title="Some title")
def test_email(): x = Column.email() assert getattr_path(x, "query__class") == Variable.email assert getattr_path(x, "bulk__class") == Field.email
class TestTable(NoSortTable): foo = Column() bar = Column() class Meta: row__template = lambda table: 'test_table_row.html'
def foreign_key_factory(model_field, **kwargs): setdefaults(kwargs, dict( choices=model_field.foreign_related_fields[0].model.objects.all() )) kwargs['model'] = model_field.foreign_related_fields[0].model return Column.choice_queryset(**kwargs)
class MyTable(Table): foo = Column(after='bar') bar = Column()
def explicit_table(): columns = [Column(name="foo"), Column.number(name="bar")] return Table(data=get_data(), columns=columns, attrs__class__another_class=True, attrs__id="table_id")
class TestTable(NoSortTable): sentinel1 = 'sentinel1' sentinel2 = Column(cell__value=lambda table, column, row: '%s %s %s' % (table.sentinel1, column.name, row.sentinel3))
class Meta: columns = [Column(name='foo')]
def test_integer(): x = Column.integer() assert getattr_path(x, "query__class") == Variable.integer assert getattr_path(x, "bulk__class") == Field.integer