Liquid Tag in Where Filter in Jekyll - jekyll

The following:
{% assign devices = site.data.equipment | where:"department","sound" | group_by:"servicelocation" %}
Is helping me build a list of sound devices from the equipment data list, grouped by location. The file resides in the _includes folder and is entitled equipment_list.html.
When I include this file in a _page, it displays exactly what I need, but I am attempting to take it one step further.
{% assign departmentname = page.name | remove:"equipment_list_" | remove:".md" %} yields the word sound based upon the _page/equipment_list_sound.md file.
{{ departmentname }} = "sound" as expected.
Can I insert this value into the where filter somehow to be able to re-use the equipment list page over and over again for different departments?
where:"department","{{departmentname}}" fails, and any variation I can imagine also fails. Is it possible?

You can use : {% assign devices = site.data.equipment | where:"department", departmentname | group_by:"servicelocation" %}

Related

Custom comparator for sorting arrays in Liquid or Jekyll

I am trying to set up a blog in Jekyll. Some (not all) of my posts have updated_on variable in its front matter which I use to store the date (YYYY-MM-DD HH:MM:SS +0530) it was last updated on.
I wish to display the posts on my index page in the decreasing order of post.updated_on if available, otherwise using post.date. To make things clear, here is pseudo-code of what I want my sort comparator to work like
comp(post a, post b){
if(a.updated_on) t1 = a.updated_on
else t1 = a.date
if(b.updated_on) t2 = b.updated_on
else t2 = b.date
return t1>t2
}
How can I achieve this kind of sorting in Liquid/Jekyll?
One fallback I have thought is to add updated_on in every post even if it was never updated since post date. Then I could do something like
{% assign sorted_posts = paginator.posts | sort: 'updated_on' | reverse %}
{% for post in sorted_posts %}
... some code here ...
{% endfor %}
But I don't want to go this way since I will have to manually add updated_on to each post where it doesn't already exists.

Queries in query set group in to one query using Django

I have a query set like the below.
<QuerySet[{'user':'xyz','id':12,'home':'qwe','mobile':1234},
{'user':'xyz','id':12,'home':'qwe','mobile':4321},
{'user':'abc','id':13,'home':'def','mobile':1233},
{'user':'abc','id':13,'home':'def','mobile':1555},]>
This QuerySet is returned by django using
users.objects.all()
For Each query in the query set, I'm drawing a user table in the front-end which shows the details of users. If the same 'user' registers with two 'mobile' numbers, it is showed as two rows in the table instead of one. My goal is to display two numbers in the same row instead of creating two rows.
I have thought of two possible solutions which are below:
Solution 1: I have thought of merging the two queries into one if the 'user' value matches in both the queries. For this we need to make lot of checks using conditional statements which works slowly when lot of users are there.
Solution 2: I have searched on Google and came up with Group By Django, but it is not working. I have tried below
query = users.objects.all().query
query.group_by = ['mobile']
results = QuerySet(query=query, model=users)
Please provide a way so that two queries can be clubbed into one based on 'user' and after clubbing 'mobile' should contain two values.
Edit: I will pass this query set to a template via view where the table is being drawn. At present, for each query, one row is the in table. Since the above query set has four queries, we will have four rows. But I want to display the information in just two rows since the 'id' and 'user' are same. Code for template is below:
{% if users %}
{% for user in users %}
{% for k,v in required_keys.items %}
<td>{{ user | get_item:k }}
{% endfor %}
{% endfor %}
{% endif %}
Required key is an dictionary which contains the keys which are used to display in table. Example:
Required_keys = {
'User Name':user,
'Contact':mobile,
'Address':home,
}
The get item is a function which gets the value when key is passed.
def get_item(dictionery,key):
return dictionery.get(key)
Edit 2: I have tried for a solution and it's not a full solution but it partially solves the problem. The solution perfectly works with one user. However if there are many users, the solution doesn't work. See below for example:
# input query set to the function which partially solves the problem
<QuerySet[{'user':'xyz','id':12,'home':'qwe','mobile':1234},
{'user':'xyz','id':12,'home':'qwe','mobile':4321},]>
# Now our team has written a function which gives the following output
<QuerySet[{'user':'xyz','id':12,'home':'qwe','mobile':1234,4321},]>
But if there are more than one user, the output is same as input, it doesn't club the queries. See the function below:
def merge_values(cls,values):
import itertools
grouped_results = itertools.groupby(values,key=lambda x:x['id'])
merged_values = []
for k,g in grouped_results:
groups=list(g)
merged_value = {}
for group in groups:
for key, val in group.iteritems():
if not merged_value.get(key):
merged_value[key] = val
elif val != merged_value[key]:
if isinstance(merged_value[key], list):
if val not in merged_value[key]:
merged_value[key].append(val)
else:
old_val = merged_value[key]
merged_value[key] = [old_value, val]
merged_values.append(merged_value)
return merged_values
The values parameter for the function is the whole query set. But this function
works if there is only one user in query set as mentioned above. But if there
are multiple users, it fails.
Edit 3: I have found why above function won't work for multiple users(don't know whether this is correct). The input query set to the function (for one user) is
<QuerySet[{'user':'xyz','id':12,'home':'qwe','mobile':1234},
{'user':'xyz','id':12,'home':'qwe','mobile':4321},]>
Since the two user queries are one after another, they are clubbed. But if there are multiple users, the query set passed to the function is
<QuerySet[{'user':'xyz','id':12,'home':'qwe','mobile':1234},
{'user':'xyz','id':13,'home':'qwe','mobile':4321},
{'user':'abc','id':12,'home':'def','mobile':1233},
{'user':'abc','id':13,'home':'def','mobile':1555},]>
In the above query set, the user with same id are not one after another, so the function failed to get desired output.
You can use the function which you have written, only change is required is need to order the values based on id.
So before taking the values from the objects, order_by('id'), then take your required values and pass it to your function to merge the values.
Eg.
query_set = Mymodel.objects.filter(name='Required').order_by('id')
values = query_set.values('id', 'field1', 'field2')
merged_values = merge_values(values)
As a start, what would be helpful is to organise your models better. You have user information duplicated in your user model. If a you know that a user has multiple mobile numbers, it may be better to create a separate model for that:
class Mobile(models.Model)
number = models.IntegerField()
user = models.ForeignKey(User)
Then, when you are make a query and you want to get all the mobile numbers, you would first get all the users using users = User.objects.all() in your view, then in your template:
{% for user in users %}
{% for mobile in user.mobile_set.all %}
<td>{{ user }}</td>
<td>{{ mobile.number }}</td>
{% endfor %}
{% endfor %}
As a side note, I feel it's better practice to name your models as a singular (User instead of Users)

Cherry-pick Jekyll collection output

Setup:
I have a Jekyll collection of 800 items
I've set output: false as I don't need individual pages for each one, and want to save time on builds
I do, however, want to be able to output: true on select items
Question
Is there a way to do this? I tried overriding the output variable in the selected item's front matter, but that didn't seem to make difference.
I do know about published: true/false, but I need for all files to be published:true for their data to remain available elsewhere in my site. This question pertains to their output as pages.
You can use two collections, one with output: true (col1) and the other with no output (col2).
Now if you want to loop over both collections in single pass, you can concatenate them :
{% assign all = site.col1 | concat: site.col2 %}
And if you have the same datas in both collections, you can sort item like this :
{% assign all = site.col1 | concat: site.col2 | sort: 'title' %}

Jekyll - Can't reverse sort by date

I'm trying to sort the 3 most recent, 'featured' posts only on my blog, but the for loop I currently have won't let me sort the collection to show the most recent posts first.
What I have below outputs the three most recent posts on my blog, but ignores the where_exp and displays both featured and non-featured posts.
If I remove the 'reverse' filter after the sort by date it will only sort featured posts, but sorts them oldest to newest. I've tried reassigning the featured-posts variable to sort by reverse-date again before the for loop but it doesn't work.
Everything I've tried so far won't let me display the three most recent, featured posts in my site, I'm hoping someone can tell me where I'm going wrong..
Post Front matter:
---
post_date: 2017-08-14 00:00:00
featured: true
---
Page For-Loop:
{% assign sorted-posts = site.posts | sort: 'post_date' | reverse %}
{% assign featured-posts = sorted-posts | where_exp:"post","post.featured" %}
{% for post in featured-posts limit:3 %}
<h2>{{ post.title | truncate: 58 }}</h2>
{% endfor %}
Output:
Three most recent posts on the website regardless of whether they're 'featured' or not.
Thanks in advance
Solved by upgrading Jekyll. I was running version 3.0.0, upgraded to 3.5.2 and the issue has been resolved.

Fetch currently logged in user relation with a given model instance

Assume we have a model called Author as such:
class Author(models.Model):
name = models.CharField(max_length=250)
We also have a feature which enables users to follow a certain author:
class UserFollow(models.Model):
author = models.ForeignKey(Author)
user = models.ForeignKey(User)
If you have a button in your UI that enables logged in users to follow / unfollow these authors. One way to check if this user is already following a certain author is to make a query on the existence of a record in UserFollow. However, when you're fetching a list of 10 / 20 .. etc authors, how would you check each author for the currently logged in user?
The usual approach would result in +X number of extra queries depending on how many items you're loading per page. Is there a more efficient way to achieve the same effect?
Thanks in advance
you can fetch all instances of UserFollow in view, pass it as a variable to the template and then you can check if given author is in this list
def my_view(request):
following = list(UserFollow.objects.filter(user=request.user))
template_context = {
'following': following
}
template:
{% for author in authors %}
{% if author in following %}
{# do some magic #}
{% endif %}
{% endfor %}