How do I customize a column based on its value? - django

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.

Related

Set a variable through a drop down list in django

def scan(request):
scan = Stock.objects.all()
hammer = Stock.objects.filter(Hammer=100)
belthold = Stock.objects.filter(Belthold=100)
context = {'All':scan, 'Belthold':belthold, 'Hammer':hammer
}
return render(request, 'scanner/scan.html', context)
This is my views.py file. I want to add a dropdown list to my html page containing the context dictionary which changes the query filters according.
This is my current html page:
{% for i in Hammer %}
<tr>
<td>{{i.script}}</td>
<td>{{i.open}}</td>
<td>{{i.high}}</td>
<td>{{i.low}}</td>
<td>{{i.close}}</td>
<td>{{i.Volume}}</td>
<td>{{i.Change}}</td>
</tr>
{% endfor %}
The 'Hammer' need to be replaced by whatever selection is made from the dropdown list.
THANKS!!
You cannot dynamically change Hammer in a template for loop. You can use the approach below or change the approach as to how you want everything displayed.
You have to use Javascript for that. You first need to give id's to all the tags you have in the for loop.
<td id="{{i.script}}_myid"> ...
Then with Javascript access the change in the dropdown menu's value and then update the respective td's accordingly:
document.getElementbyId('dropdown').addEventListener('change', function(e) {
// Change td's here: document.getElementbyId(..._myid).innerHTML = "" })
To pass Hammer in your JS you can either do an inline script (which many dont recommend) or a separate JS file added as a script tag below. You can use serializers to pass on the objects from Python to JS.

Using "record" in attributes Django Tables 2

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

How to conditionally color table cells in Django Templates?

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

django-taggit create tagcloud from queryset

I could not find an answer for this.. So here my question. For a new project i'd like to use django-taggit.
Does someone have a suggestion on how to create a tag-cloud based on the current queryset?
The desired behavior is to 'start' with an unfiltered list - then allow narrowing town the results with applying filters and tags. At the beginning the tag-cloud shows e.g. the 50 most common tags. After choosing a tag (or other criteria) the tag-cloud should only display the remaining possibilities.
I know that django-tagging offers Tag.objects.usage_for_queryset() for this situation. But I would prefer to use '-taggit' over '-tagging'.
django-taggit-templatetags appears to be the 'go-to' place for a tagcloud for django-taggit.
It doesn't appear to handle querysets though. :(
So, I've added:
#register.inclusion_tag('taggit_templatetags/tagcloud_include_qs.html')
def include_tagcloud_qs(queryset):
try:
queryset = queryset.annotate(num_times=Count('taggeditem_items'))
except FieldError:
queryset = queryset.annotate(num_times=Count('taggit_taggeditem_items'))
num_times = queryset.values_list('num_times', flat=True)
weight_fun = get_weight_fun(T_MIN, T_MAX, min(num_times), max(num_times))
queryset = queryset.order_by('name')
for tag in queryset:
tag.weight = weight_fun(tag.num_times)
return {"tags": queryset}
to
templatetags/taggit_extras.py
And this to a new file at taggit_templatetags/tagcloud_include_qs.html
<div>
{% for tag in tags %}
<font size={{tag.weight|floatformat:0}}>{{tag}}</font>
{% endfor %}
</div>
I'm using it like this in my templates:
{% include_tagcloud_qs my_queryset %}
I haven't spent much time looking at the django-taggit-templatetags code, so feel free to update this with a better solution!
PS:
I'm getting a queryset in my view like this:
my_queryset = Tag.objects.filter(foo__bar=baz).distinct()
This answer shows how to build a tag cloud. You'd create a queryset in your view according to your parameters, generate a dictionary, and render it in your templates as shown in that answer.
I would suggest using django-tagging. It is well documented. I have created tag clouds with it. You can access tag clouds via model, model instance, etc via template tags that are easy to load. This is a little hackish but using the .counts method you can hack up some css to increase the size of each font as you would see in a real tag cloud. Django-tagging actually excels in this area as it has a default template tag with formatting options for everything you have described.
I've added a TagBase.get_for() function in https://github.com/twig/django-taggit/commit/42cd4e04f00496103f295c0afd8297074be50dcf
This basically fetches the Tags used for a given queryset, and from there you can do what you need to do.

Set background color for a field depending on its value

I have a Customer table and one of the fields here is for Status....I want to have the background colour of the cell containing this field to be set according to it's value e.g green for the status "Closed"; yellow for the status "Pending" and so on.
What's the best way to do this...possibly something that'll be easy to modify if need be in future?
On your css create class for td as follows, considering you want to display your customer table information in a html table,
td.closed{ color: green; }
td.pending{ color: yellow; }
Now on your template you can loop your database table on your html table and on the td you can call the css as follows:
<td class="{{ status|lower }}">{{ status }}</td>
Hope that helps
Last time i did something like this i had enumerated values for statues that had to be translated to other languages so i could not afford to depend on the string value of status as class in CSS, so i used choices like this:
Define choices in your models.py
class SomeModel(models.Model):
STATUS_CHOICES = (
(1, _('Pending')),
(2, _('Closed')),
)
status = models.IntegerField(choices=STATUS_CHOICES)
With HTML looking something like this:
<td class="color_{{ model_instance.status }}">
{{ model_instance.status.get_status_display }}
</td>
And your CSS should be similar to:
td.color_1{ color: green; }
td.color_2{ color: yellow; }
Basically you could change the status string value without changing CSS, same could be done if the status is enumerated value connected by foreign key for example. There are other ways to do it, but the answer mmrs151 gave is simplest and sufficient in most cases.