Django tables2 set default sort to be descending - django

When django-tables2 renders an unsorted table, and I want to sort by one column, I click it, the default behaviour is to sort it in ascending order.
Is there any way I can change it so that the first click sorts in descending order?

It can be done, here is how.
First you have to override the default table.html template that is used for rendering table (if you are not already using your own template). You can start with the one included in django-tables2.
Secondly, in the template, find the anchor used for sorting:
{% if column.orderable %}
<th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
{% else %}
And change this to:
{% if column.orderable %}
{% with "-"|add:column.name as sort_col_name %}
<th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
{% endwith %}
{% else %}
So we are basically just adding a - in front of the column name by default in order to force descending sort in case when the page is loaded without sorting, and turning to .opposite instead of .next. We need to do this because .next will always be defined whereas .opposite will be None on the page load without sort, and the default filter will get executed. When sorting is defined, .opposite will properly toggle the sort order.
Note that this is not tested, and if you have problems take a look into the django-tables2 source for more info.

Related

Add button against each record on django admin change list page?

I want to add a button against each row on django admin change list page. When that button is clicked, i want to make entries in some database table(all this happens on the backend) and after that the button should be disabled on the same page against that row.
How can i achieve this? I have searched a lot button there is no solution available.
You are going to have to edit the changelist_result_list template at the point where the result items are generated (near the bottom) to add an element that provokes a request. For example:
<tr class="{% cycle 'row1' 'row2' %}">
{% for item in result %}
{{ item }}
<!-- NEW STUFF --->
{% if forloop.last %}
<td>{% if pk %}CLICK ME{% endif %}</td>
{% endif%}
{% endfor %}
</tr>
(Don't forget to also update the table headers further up)
You are going to have to provide a template context variable for {{pk}} by messing with the templatetag result_list in django.contrib.admin.template_tags.admin_list -- meaning, you are creating your own templatetag for this.
Then you need to set up a view and an url conf to handle the request the links send.
Let's say you're at foo/bar and click on the link for the row with pk=2. A GET request will be send for foo/bar/do_things_with_object_2.
Your url conf needs to capture the pk (and the model, here: bar) and your view needs to do whatever it is you want on the object.

How do I remove default whitespace in template?

I'd like to list a series of items separated by commas.
But if an item after the first doesnt exist, I don't want the comma or item to appear.
So I've implemented this:
<em>{{object.item1|default_if_none:""}}</em>
{% if object.item2 %}
<em>, {{object.item2|default_if_none:""}},</em>
{% endif %}
<em>{{object.item3|default_if_none:""}}</em>
If object.item2 exists, it'll put a whitespace after item1--before the comma.
When it displays, it'll look something like:
"I_am_item_one , I_am_item_two, Item_three"
What's the best way to fix this?
edit: is it possible to for-loop through an object's properties?
{% for property in object.property %} or similar..
I am not aware of any way to loop over objects and their properties in a template. You could look into either passing the data into the template as a dictionary/list or creating a model method (as done in this answer: Django templates: loop through and print all available properties of an object?)
Once your data is iterable, you could use the following code to decide when to add a comma. I am not entirely clear if you want a comma between each item or only after the first item, so here are two options.
This will only add a comma on the first entry
{% for item in item_list %}
<em>{{ item }}</em>{% if forloop.first %}, {% endif %}
{% endfor}
This will add a comma after each item except the last one
{% for item in item_list %}
<em>{{ item }}</em>{% if not forloop.last%}, {% endif %}
{% endfor}
It is probably fairly clear, but you can use forloop.first to detect when you are on your first item and forloop.last to detect when you are on the last item of a loop.

Parsing and using list item content via Twig

I am experimenting with using Grav to create my next website. One of the things I would be able to do is to build a unordered list using data provided in Grav frontmatter from the Grav page that uses the template. Here is how I am trying to do this
---
sectionone:
listitems: "['Benefit 1','Benefit 2','Benefit 3']"
---
and then in the template somehow do the following
{% block featurelist %}
<ul>
{% set items = {{page.header.sectionone.consumers.benefits|json_decode}} %}
{% for item in {{}} %}
<li>{{item}}</li>
{% endfor %}
</ul>
{% endblock %}
However, Twig does not like this and reports back an error along the lines of
Twig_Error_Syntax
A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "punctuation" of value "{".
with the offending line being my {% set items = ... } statement. I am clearly doing something wrong here but I am a Twig newbie so I fail to see what that might be.
{% block featurelist %}
<ul>
{% set items = page.header.sectionone.consumers.benefits|json_decode %}
{% for item in items %}
<li>{{item}}</li>
{% endfor %}
</ul>
{% endblock %}
I figured this out eventually. Grav documentation is by and large very good. However, the documentation on page headers/frontmatter appears to be somewhat incomplete. There is no full description of the entire syntax that is understood by the frontmatter processor. In order to define an array in front matter all you need to do is the following
---
sectionone:
benefits:
- 'Benefit 1'
- 'Benefit 2'
- ...
---
In essence the standard markdown syntax for an unordered list. Grav's twig processor appears to convert this to a PHP array - no parsing required!

If Statement for Collection Metafield - Shopify

I have added a metafield to a collection within my Shopify store. I have a namespace, key, and value. I am looking for a liquid code snippet to check if the collection has a specific metafield key, and then if so, to output the assigned value.
I have tried the following with no success:
{% if relationship = collection.metafields.parent %}
<span>{{ relationship.parent[value] }}</span>
{% endif %}
Does anyone have any idea of how I could implement this functionality?
Thanks.
I guess if you wanted to check for the existence of a metafield, or more specifically the existence of a value within a metafield, you could do:
{% if collection.metafields.parent['metafield_name'] %}
<span>{{ collection.metafields.parent['metafield_name'] }}</span>
{% endif %}
This is taking the value of whatever is contained in the metafield and checking if it returns a truthy or falsey type value. If the value is truthy (if there's text in the metafield) it will then output that to the screen.

Breaking data into multiple display columns with Django

Overlap in terminology makes search for answers difficult for this one.
I'm looking for advice on the best way to implement a multiple-column display of my QuerySet that fills each column top to bottom over X columns. Meaning that the number of items in each column equals the QuerySet count divided by X (number of columns).
Using Offset doesn't work for this because I would like my data to grow into 4 columns without updating the offset manually. CSS floats work visually, but leave the data out of order.
Something like that should work for you, pass the number of columns as columns to the template:
{% for item in items %}
{% if forloop.first %}<div style="float:left;">{% endif %}
{{ item }}
{% if forloop.counter|divisibleby:columns %}
</div><div style="float:left">
{% endif %}
{% if forloop.last %}</div>{% endif %}
{% endfor %}
<div style="clear:both;"></div>
It seems like lazerscience's answer is on the right track - but I think the OP wants the data to alphabetically sort the data down the column and then start back at the top of the next column. Using divisibleby: works to break the line after 'X' items, but I think it'll take a more complex template to do what the OP wants.
Not sure if it's possible, but in the view could you: Get item count, divide by 'columns' and used that # in Divisibleby to break into the next DIV column (the visual flow will be CSS)
As Lazers's example is now you're constructing Rows and then breaking it into columns, leaving the sort order across and then down.
Sorry if I missed something.
-K
You'd better go and use a jQuery plugin to make some columns from a list.
Columnizer works very well for me
Here are some django template filter that split a list into multiple sub-lists:
list partition template filters at djangosnippets.org
You could use these in a django template to split a long list into multiple columns as follows:
{% load list_tags %}
<h2>Some List</h2>
{% for sub_list in full_list|rows:"3" %}
<ul>
{% for item in sub_list %}
<li>
{{item.name}}
</li>
{% endfor %}
</ul>
{% endfor %}
I included the template filters in my project in a file called list_tags.py. Adjust the {% load %} tag as necessary.