Groupby filter ordering - jinja2

Is it possible to make the groupby filter order in descending order? I have a list of dictionaries from a SQL query and I want to group by date in descending date order. From the manual:
{% for group in persons|groupby('gender') %}
<li>{{ group.grouper }}<ul>
{% for person in group.list %}
<li>{{ person.first_name }} {{ person.last_name }}</li>
{% endfor %}</ul></li>
{% endfor %}

Try applying the reverse filter to the groupby('attribute') filter (e.g., {% for group in persons|groupby('gender')|reverse %})

Related

DBT join multiple tables

I am learning DBT, specifically dbt-mysql. I am having trouble combining several tables into one table.
What I want to do:
Group By several columns by the last_updated (timestamp) date of the table and then combine those columns into a single table by the split last_updated field. Here is how I want my data to end up:
Here is my staging model (which I think should be straight selects from the database):
staging/clients/stg_clients_fields.sql
SELECT id, created, last_updated, service, order_count, spent_count, deleted, country
FROM client_database.clients
Then I have intermediate models (which I think should reconstruct data for my needs):
intermediate/clients/clients_last_updated_grouped.sql
SELECT YEAR(last_updated) as year_updated, MONTH(last_updated) as month_updated, COUNT(id) as client_count
FROM {{ ref('stg_clients_fields') }}
GROUP BY YEAR(last_updated), MONTH (last_updated)
intermediate/clients/clients_deleted_grouped.sql
SELECT YEAR(last_updated) as year_updated, MONTH(last_updated) as month_updated, COUNT(id) as deleted
FROM {{ ref('stg_clients_fields') }}
WHERE deleted = 1
GROUP BY YEAR(last_updated), MONTH (last_updated)
intermediate/clients/clients_service_grouped.sql
SELECT YEAR(last_updated) as year_updated, MONTH(last_updated) as month_updated, COUNT(id) as service
FROM {{ ref('stg_clients_fields') }}
WHERE service IS NOT NULL
GROUP BY YEAR(last_updated), MONTH (last_updated)
And other columns follow the same pattern based on their WHERE clauses.
Now I need to create a marts model that would use all previously created data and put it in one single table.
At this point, I end up with several tables that have the last_updated field separated and the specific column value next to the date.
How can I now combine all these tables that they would join on the last_updated split into to columns field?
Or perhaps there is a better solution to group data by year and month and get individual column values based on conditions?
I am new to DBT so all the help and all advice are welcome!
since clients_last_updated_grouped doesn't have a where condition, it's guaranteed to have all of the year/month combinations found in the other models. This makes it much easier. You can just select from that model and join the other models on year and month:
with
updated as (select * from {{ ref('clients_last_updated_grouped') }} ),
deleted as (select * from ),
service as (select * from ),
joined as (
select
updated.year,
updated.month,
updated.client_count,
coalesce(deleted.deleted, 0) as deleted_count,
coalesce(service.service, 0) as service_count
from
updated
left join deleted on updated.year = deleted.year and updated.month = deleted.month
left join service on updated.year = service.year and updated.month = service.month
)
select *
from joined
If your database doesn't support CTEs (with ...), this becomes:
select
updated.year,
updated.month,
updated.client_count,
coalesce(deleted.deleted, 0) as deleted_count,
coalesce(service.service, 0) as service_count
from
{{ ref('clients_last_updated_grouped') }} as updated
left join {{ ref('clients_deleted_grouped') }} as deleted on updated.year = deleted.year and updated.month = deleted.month
left join {{ ref('clients_service_grouped') }} as service on updated.year = service.year and updated.month = service.month
If it's not the case that clients_last_updated_grouped has every month/year combination of the other tables, you would need to first construct a "date spine", and then left join all 3 tables to that date spine.

Left Join to the latest row using django

I have two tables.
Products [id, name, category]
Sales [product, date, price]
I want to get every Product with the latest row from Sales. If there is no row in sales, I still want to get that product row. The rows are foreign key related.
What is the Django way of doing such a left join?
Probably you can use Subquery:
from django.db.models import Subquery, OuterRef
sales = Sales.objects.filter(product=OuterRef('pk')).order_by('-date')
products = Product.objects.annotate(latest_sale = Subquery(sales.values('price')[:1]))
you can get the Sales object in view and render the object in template. for example you can do this.
models.py
class Course(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=100)
def __str__(self):
return self.title
class Sales(models.Model):
product = models.ForeignKey(Course, on_delete=models.CASCADE, null=True)
price = models.IntegerField()
date = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return self.product.title
views.py
order_by(-date) gets you the latest item from the database
def sales_view(request):
sales = Sales.objects.all().order_by(-date)
return render(request, 'template.html', {'sales':sales})
Now, in your template you can load the template.
{% for sale in sales %}
{{sale.product.name}}
{{sale.price}}
{{sale.date}}
{% endfor %}
This way you get the product object even if there are no sales object. Hope you get some idea from it.

MySql error when trying to combine two tables

Hi i am getting the error below when i am tryin to combine two tables, Post and User
error: extraneous input 'post' expecting {, ';', K_ALTER, K_ANALYZE, > > K_ATTACH, K_BEGIN, K_COMMIT, K_CREATE, K_DELETE, K_DETACH,
K_DROP, K_END, K_EXPLAIN, K_INSERT, K_PRAGMA, K_REINDEX, K_RELEASE,
K_REPLACE, K_ROLLBACK, K_SAVEPOINT, K_SELECT, K_UPDATE, K_VACUUM,
K_VALUES, K_WITH, UNEXPECTED_CHAR}
I have a table that has a number of posts which consists of a title, body and by which user created that post,
The user table is a list of users details such as there names and addresses.
I am trying to create a resultset that outputs the post details such as the title and body, plus the user's username attached to it(the post table only has a user id reference)
This is my query i tried
SELECT post.title AS title, post.body AS body, post.username AS username FROM post, user, WHERE user.id = post.userId
My sql skills are a bit rusty but i believe the above use case query can be done?
It sounds like you want a table join. I assume when you wrote post.username you mean user.username, since you later say that the post table only has a userId reference.
Something like this might be what you want:
SELECT post.title, post.body, user.username
FROM post
INNER JOIN user
ON post.userId=user.id;
you should try something like this:
SELECT p.title AS title,p.body AS body,p.username AS username
FROM post p
JOIN user u ON u.id = p.userId
WHERE u.id = 'to your user id'
Your Query looks good but remove the comma before where clause:
from
SELECT post.title AS title, post.body AS body, post.username AS username FROM post, user, WHERE user.id = post.userId
to
SELECT post.title AS title, post.body AS body, post.username AS username FROM post, user WHERE user.id = post.userId

AVG/COUNT MySQL query NOT working

I am not very experienced in MySQL queries so I might be doing something wrong.
Simplified my query is like this:
SELECT item.*, AVG(itemRating.rating) as 'rating', COUNT(itemRating.rating) as 'ratingCount'
FROM item, itemRating
WHERE item.id IN (...)
AND itemRating.item_fk = item.id
GROUP BY itemRating.item_fk
It works fine, except for when an item has no rating (no record in the itemRating table). Is there any way I can solve this without losing information?
SELECT item.id,
AVG(itemRating.rating) as 'rating',
COUNT(itemRating.rating) as 'ratingCount'
FROM item
LEFT JOIN itemRating ON itemRating.item_fk = item.id
WHERE item.id IN (...)
GROUP BY item.id

Order table according to key in foreign key table

I have table users, and table points.
Table points has foreign key user_id, so I did this in my User Eloquent model:
public function points()
{
return $this->hasMany('Point', 'user_id');
}
So now I can get number of points for one user by doing:
$user->points()->count();
But what I want to do is order all users according to points they won and then paginate users by 20 per page.
You will start from the inverse relationship:
$points = Point::with('User')
->orderBy('votes', 'desc')
->skip($offset)
->take($page_size); // or use ->paginate();
and then in your view:
#foreach($points as $point)
{{ $point->votes }} | {{ $point->user->name }}
#endforeach
Note: make sure you define the inverse relationship in the Point model.