I'm moving my tables to django-tables2. By now is almost everything working ok, but now I have a problem.
In my current version i use checkboxes to select items
<td><input type="checkbox" class="checkbox_delete" name="event" id="event.id"
value="{{ event.id }}" />
this way in the view i can recover the event.id using request.POST.getlist('event')
Now I'm trying to add the "value" attribute to a CheckBoxColumn
select = tables.CheckBoxColumn(attrs={'td__input': {'class': 'checkbox_delete', 'name': 'event', **'value': [A('id')]**}, 'th__input': {'id': 'selectAll'}},
empty_values=())
I've been playing with the Accesor and the record.id which I use in a templateColumn.
How can I pass the id to the value attribute??
You can simply do something like this:
id = tables.CheckBoxColumn()
This way, the column will be rendered like this
<input type="checkbox" name="id" value="X">
where X will be the value of the id field.
Now for a more complete answer:
You can add td__input to override some defaults but I don't believe what you can set it to different values for each column ! By checking the source:
https://github.com/bradleyayers/django-tables2/blob/master/django_tables2/columns/checkboxcolumn.py
you will see that in the render method in CheckBoxColumn will create an AttributeDict containing the attributes from input, td__input and some defaults, like this:
def render(self, value, bound_column): # pylint: disable=W0221
default = {
'type': 'checkbox',
'name': bound_column.name,
'value': value
}
general = self.attrs.get('input')
specific = self.attrs.get('td__input')
attrs = AttributeDict(default, **(specific or general or {}))
return mark_safe('' % attrs.as_html())
So the attrs you define will be the same in all columns since attrs.as_html will just convert the 'x':'y' dict entries to x=y...
So if you want to have total control and do whatever you like with the values for each column, just subclss CheckBoxColumn and override render (left as an excersise to the reader).
Update
Also, a very nice thing about your own render method is that you don't need to define the same parameters as the base one. This is because django-tables2 uses the getargspec function to find out which arguments your render expects and pass them to the render method. So, if you take a look at the https://github.com/bradleyayers/django-tables2/blob/master/django_tables2/rows.py you will see that the available parameters that can be passed to render, along with their values are:
available = {
'value': value,
'record': self.record,
'column': bound_column.column,
'bound_column': bound_column,
'bound_row': self,
'table': self._table,
}
So, for instance you can define your render method like:
def render(self, value, bound_column, record):
to also pass the record to it.
I found another solution here How to get information from Django_tables2 row?
Just need to define select = tables.CheckBoxColumn(accessor='pk') and it add the value as the record.id
Related
Using django-tables2 is very easy from your model to get a table. In my case I need one of my table columns to be formated based on its value.
In my current html table it would look like below:
{% if record.status|stringformat:"s" == "New" %}
<td class="bg-success></td>
If the value is New the cell background should be green.
From what I could find there are maybe 3 ways to do something like this:
Updated solutions:
1.Create a class and a css rule with the appropriate background color and add it to the column:
class MyTable(tables.Table):
status = tables.Column(attrs={"class": lambda record: record.status})
.New {
background-color: green
}
This way it works, although I think that record.status could work, without the lambda.
2.You can specify how to render a column:
class MyTable(tables.Table):
status = tables.Column()
def render_status(self, value):
if value.name == "New":
change class accordingly
The change class accordingly part eludes me.
Also you can create a custom column:
class StatusColumn(tables.Column):
def render(self, value):
if value == "New":
return format_html('<span class="text-success">{}</span>', value)
I made this work using a span tag to pass a bootstrap class to format the cell.
3.Use a TemplateColumn and pass html:
class MyTable(tables.Table):
status = tables.TemplateColumn("""
{% if record.status|stringformat:"s" == "New" %}
<td class="bg-success"></th>
{% else %}
<td class="bg-danger"></th>
{% endif %}
""")
This way a new column is created formated correctly.
I am still searching how to do this and I would appreciate any help.
Depending on what your exact needs are, there are different solutions.
1. Changing the appearance of the cell (<td></td>)
If you want to add attributes to the <td> tag, you must use something django-tables2 calls column attributes.
It supports fixed values, but also allows a callable to use some value of the record being rendered.
Say for instance, you have a record containing a field color and you want to use that color as the background color for the cell.
class Table(tables.Table):
person = tables.Column(attrs={
'td': {
'class': 'person-column',
'style': lambda record: 'background-color: {}'.format(record.color)
}
})
Or more specifically for your example:
class MyTable(tables.Table):
status = tables.Column(attrs={'td': {'class': lambda value: 'bg-success' if value == 'New' else 'bg-danger' }})
or without lambda:
def status_attr(record):
return 'bg-success' if value == 'New' else 'bg-danger'
class MyTable(tables.Table):
status = tables.Column(attrs={'td': {'class': status_attr}})
2. changing the contents of the cell
By contents I mean everything within the <td></td> tag. The API of django-tables2 allows for various ways to change the contents of a cell, which is called custom data in the documentation.
Your solutions 2 and 3 already show the ways to do that, however, you can not change the cell attributes in those ways. You might be able to achieve output looking like this <td><td class="bg-success">...</td></td>, which might look like what you want, but are not valid HTML.
When i customize search type in the filter:
django_filters.CharFilter(lookup_expr='icontains')
it adds '[field_name] contains' keyword to the end of displaying label in template {{ field.label }}. I specifically use verbose_name='Some filed' argument in my model to make it accessible in template, but filter modifies it for inexplicable reason.
Is there any option for it to be displayed as i set it in versbose_name?
UPD:
Solved this adding this to settings.py
def FILTERS_VERBOSE_LOOKUPS():
from django_filters.conf import DEFAULTS
verbose_lookups = DEFAULTS['VERBOSE_LOOKUPS'].copy()
verbose_lookups.update({
'exact': (''),
'iexact': (''),
'contains': (''),
'icontains': (''),
})
return verbose_lookups
You can add the label parameter to the filter to explicitly set the field's label:
django_filters.CharFilter(lookup_expr='icontains', label='the label')
https://django-filter.readthedocs.io/en/master/ref/filters.html#label
Also, if you don't like the way django-filters creates its filters, you can play with the FILTERS_VERBOSE_LOOKUPS setting (https://django-filter.readthedocs.io/en/master/ref/settings.html#verbose-lookups-setting). I haven't tested it but it says that you can set it to False to disable this behavior - you may want to try this instead.
I'm fighting against the django forms. When I've solved one problem a new one appears, well I got a "dynamic" html with 3 types of forms.
class NameForm(forms.Form):
text = forms.CharField(widget=forms.TextInput(attrs={'id': 'field1'}))
select = forms.ChoiceField(widget = forms.Select(attrs={'id': 'field1'}), choices = ([('default','Select option')]), initial='default')
date = forms.DateField(widget=DateTypeInput(attrs={'id': 'field1'}))
And via JavaScript I create one type or other:
var createElement={
'text': '{{ form.text }}',
'select':'{{ form.select }}',
'date': '{{ form.date }}',
}
Then I want to get all type of elements created but the problem is there are multiple inputs with name "text" or select or date.
I parse this data with method GET and this one uses a dictionary. This dict only can have one key "text".
How can I solve this problem? Thanks a lot.
You can use request.GET.getlist('text') to get multiple values as a list.
But you are probably better off using formsets for something like this.
I am passing default colors for the background cell and data from the model via context variables. But inside the template when I am parsing the model data, I want to conditionally color the individual cells.
<tr><td style="background-color:{{tarcol}};">{{poll.target}}</td></tr>
<tr><td style="background-color:{{actcol}};">{{poll.actual}}</td></tr>
The colors depend on relative values of target & actual,so I have to compare the data to decide the color.
I am thinking I should not add logic to decide the colors in views, this must be done in the templates.
Is there a better way to handle this ??
p.s:I used this question as a inspiration for the design.
django table cell color based on expression
I would relegate this task to a template tag to keep the templating as clean as possible. In a nutshell, you would create a template tag that accepts your context variables, decides the color and returns a string.
See: https://docs.djangoproject.com/en/1.5/howto/custom-template-tags/ for help and examples.
First of all create custom admin change_list template where you load custom template tag:
{% load i18n grp_tags admin_urls admin_static admin_list my_admin_list %}
Copy existing admin_list.py and change:
def items_for_result(cl, result, form):
if f is None:
...
css_class = getattr(attr, 'css_class', False)
if css_class:
row_class = mark_safe(' class="%s"' % css_class(result))
In admin.py have something like:
class Media:
css = {
'all': ('/static/custom_admin.css',)
}
...
def job_driver_car(self, obj):
return '{0} <strong>{1}</strong><br>{2}'.\
format(obj.driver_type,
obj.driver_grade,
obj.car_type)
job_driver_car.short_description = 'Driver / Car'
job_driver_car.css_class = lambda obj: 'background lightyellow' \
if obj.driver_grade is not None and 'NEW' in obj.driver_grade else ''
job_driver_car.allow_tags = True
and css itself:
table tbody tr.grp-row-odd td.background, table tbody tr.grp-row-odd th.background,
table tbody tr.grp-row-even td.background, table tbody tr.grp-row-even th.background,
table tbody tr.grp-alt td.background, table tbody tr.grp-alt th.background {
background: transparent;
}
.lightyellow { background: #ffd090 !important; }
I develop this solution because I needed to color almost every cell according to instance value. Checked on Django==1.6.2 and django-grappelli==2.5.2
For some reason I use a custom HTML-code for displaying the form. My form's input get the initial value (date), and shows the same form after submittion.
But after submit, input has no previously selected value (just empty).
I use {{ Form.Fileld.value|date:"Y-m-d" }} in the temlate to get initial and submitted values.
It seems that initial and submitted values are in different formats: "datetime" for initial values and "string" after submit.
Here is my simple test:
Form
class ShowStartEndDateForm(forms.Form):
start_date = forms.DateField(initial=date.today().replace(day=1),
label="Start",
widget=forms.DateInput(format='%Y-%m-%d'),
input_formats=['%Y-%m-%d'])
...
View
if request.method == 'POST':
form_date = ShowStartEndDateForm(request.POST)
if form_date.is_valid():
form_was_submitted = True
cd = form_date.cleaned_data
operator = cd['operators']
days=[]
for day in range(0,(cd['end_date']-cd['start_date']).days+1):
days.append(cd['start_date']+relativedelta(days=+day))
else:
form_date = ShowStartEndDateForm()
return render_to_response('epay.html', locals(),
context_instance=RequestContext(request))
Template
<!- HTML FORM here
|classname is my custom template filter.
-->
Date1: {{ form_date.start_date.value|date:"Y-m-d" }} \
({{ form_date.start_date.value|classname }})
Date2: {{ form_date.start_date.value }}
First call in browser:
Date1: 2013-10-01 (date)
Date2: 1 October 2013
After form submit:
Date1: (unicode)
Date2: 2013-10-01
What am I doing wrong? What is the proper way to access form's field initial and submitted values?
Thnak you.
You're question is a bit confusing (you should add some more of your actual code), but I know from experience that when mixing between formats, its a good convention to just do it all in one place. So instead of specifying the format on the client side like you do, specify it all on the form itself and then everything would (should) be in-sync.
class ShowStartEndDateForm(forms.Form):
start_date = forms.DateField(initial=date.today().replace(day=1),
label="Start",
widget=DateInput(format='%Y-%m-%d'),
input_formats=['%Y-%m-%d'])
i.e. you tell the the DateInput widget what form to take and you tell input_formats (for the field itself) what formats to expect (you can add more than one). Also, if you are using some sort of datepicker you of course need to make sure it is also using the right format.
Edit
I replicated this and everything was fine:
In [1]: from test.forms import *
In [2]: f = ShowStartEndDateForm()
In [3]: print f
<tr>
<th><label for="id_start_date">Start:</label></th>
<td><input id="id_start_date" name="start_date" type="text" value="2013-10-01" /></td>
</tr>
(of course I changed the indentation here so you could see it more clearly)