How to sort by presence of M2M (Pizza example) - django

Say I have the common pizza example:
class Topping(models.Model):
order = models.IntegerField()
name = models.CharField(max_length=250)
class Pizza(models.Model):
toppings = models.ManyToManyField(Topping)
With the following toppings:
Topping.objects.create(order=1, name="tomato")
Topping.objects.create(order=2, name="cheese")
Topping.objects.create(order=3, name="olives")
Topping.objects.create(order=4, name="salami")
Topping.objects.create(order=5, name="onion")
Topping.objects.create(order=6, name="rocket")
Now say I had a pizza with tomato, cheese and salami.
I wish to get an order list of all the toppings of the pizza according to the topping__order, along with a list of all the toppings it does not have, also ordered by topping__order.
So it's sorted by first where the pizza has the topping, and secondly by the topping__order field.
The result would be something that has the same info as this (probably in a queryset though):
{
{ "id": 1, "name": "tomato", "has_topping": True},
{ "id": 2, "name": "cheese", "has_topping": True},
{ "id": 3, "name": "salami", "has_topping": True},
{ "id": 2, "name": "olives", "has_topping": False},
{ "id": 5, "name": "onion" , "has_topping": False},
{ "id": 6, "name": "rocket", "has_topping": False},
}
Is this possible via a database query? (I can do it manually in Python via two queries)

You can do it with .order_by() function:
Pizza.objects.all().order_by('id', 'toppings__order')

Related

Group queryset by field

I am working with Django and Django REST framework. I have a model called Selection that contains field called category, when i query the model to send the result to the frontend i get it with the following structure:
[
{
"id": 1,
"category": "SHOES",
"products": 122,
"created_at": "2021-09-11",
},
{
"id": 2,
"category": "SHOES",
"products": 4,
"created_at": "2021-10-07",
},
{
"id": 3,
"category": "CLOTHES",
"products": 0,
"created_at": "2021-10-08",
},
]
I need to put the selections of the same category in an array and remove the grouped-by category, like this:
{
"SHOES": [
{
"id": 1,
"products": 122,
"created_at": "2021-09-11",
},
{
"id": 2,
"products": 4,
"created_at": "2021-10-07",
}
],
"CLOTHES": [
{
"id": 3,
"category": "CLOTHES",
"products": 0,
"created_at": "2021-10-08",
}
]
}
I considered to making it with Javascript in the frontend, but before i wanted to know if there's a way to do this from Django.
Yes, you need to do some customisation in your code.
Get all categories by using values_list('category', flat=True) with your queryset
Iterate through them filtering category one by one
response_list = []
categories = Selection.objects.values_list('category', flat=True)
for category in categories:
data = Selection.objects.filter(category=category)
data = {
category: SelectionSerializer(data, many=True).data,
}
response_list.append(data)

How to get max score of answers to a question by each user

I have these 2 models:
class Question(models.Model):
title = models.CharField(max_length=200)
# other fields
class Answer(models.Model):
user = models.ForeignKey(User)
question = models.ForeignKey(Question)
score = models.IntegerField()
each user can answer a question multiple times.
Imagine I have these answers:
{
"user": 1,
"question": 1,
"score": 50
},
{
"user": 1,
"question": 1,
"score": 100
},
{
"user": 2,
"question": 1,
"score": 100
},
{
"user": 2,
"question": 1,
"score": 200
},
{
"user": 2,
"question": 2,
"score": 100
},
{
"user": 2,
"question": 2,
"score": 200
}
I want a query to give me this result:
{
"user": 1,
"question": 1,
"max_score": 100
},
{
"user": 2,
"question": 1,
"max_score": 200
},
{
"user": 2,
"question": 2,
"max_score": 200
}
I want all of the max scores of each user to each answer.
Try this:
from django.db.models import Max
Answer.objects.all().values("user", "question").annotate(score=Max("score"))
I'm not sure how to achieve your goal with Django ORM, but you can do it with RawSQL
Answer.objects.raw("""
select a1.* from answer a1 LEFT JOIN answer a2
ON (
a1.user_id = a2.user_id and a1.score < a2.score
)
where a2.user_id isnull
""")
Explanation: you get only records from you table, that have no bigger score from same table for each user.

django-filters returns two the same models when an instance of a model has the same value

Here is my code;
models.py
class Home(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return str(self.user)
class GeneralHomeFeatures(models.Model):
home = models.ForeignKey(
Home, on_delete=models.CASCADE, related_name="general_home_features"
)
home_feature = models.CharField(max_length=100, null=True, blank=True)
def __str__(self):
return str(self.home)
serializer.py
class GeneralHomeFeaturesSerializer(serializers.ModelSerializer):
class Meta:
model = GeneralHomeFeatures
exclude = ["home"]
filterset.py
class HomeFilter(filters.FilterSet):
home_feature = filters.CharFilter(field_name="general_home_features__home_feature", lookup_expr="icontains")
class Meta:
model = Home
fields = [
"home_feature",
]
once I give GeneralHomeFeatures class the same value twice, once filtered, it returns the same instance twice. Example; I make a request to this url - http://localhost:8000/api/homes/?home_feature=Pool and it returns this;
[
{
"id": 1,
"user": "cliffaust",
"general_home_features": [
{
"id": 1,
"home_feature": "Pool"
},
{
"id": 2,
"home_feature": "Children Play Ground"
},
{
"id": 7,
"home_feature": "Pool"
}
],
},
{
{
"id": 1,
"user": "cliffaust",
"general_home_features": [
{
"id": 1,
"home_feature": "Pool"
},
{
"id": 2,
"home_feature": "Children Play Ground"
},
{
"id": 7,
"home_feature": "Pool"
}
],
},
{
"id": 3,
"user": "cliffaust",
"general_home_features": [
{
"id": 4,
"home_feature": "Pool"
},
{
"id": 6,
"home_feature": "Children Play Ground"
}
],
}
]
because home_feature of GeneralHomeFeatures has two the same value(pool), it seems like django-filter is returning the same instance twice(based on the serializer id).
I don't know if this is a fault in my code, or its just how it works. Also, is they a better way of doing something like this?

Serializer remove parent field django

{
"episode": {
"id": 6,
"channel_id": 2,
"channel": {
"id": 2,
"tags": [
"new"
]
},
{
"episode": {
"id": 7,
"channel_id": 3,
"channel": {
"id": 2,
"tags": [
"new"
]
}
},
Hey I am new to Django. I am wondering how to remove episode parent written in the serializer response and directly go into the id, channel. Episode is not useful for me here. I have a model that sets the priority of these episodes. Have excluded the priority and id field but don't know how to remove episode parent.
class TrendingEpisode(models.Model):
episode = models.ForeignKey(Episode, null=False, blank=False, on_delete=models.CASCADE)
priority = models.IntegerField(null=False, blank=False)
class Episode(models.Model):
channel = models.ForeignKey(Channel, on_delete=models.CASCADE)
tags = models.ManyToManyField(EpisodeTag)
#some other fields
Why do you use TrendingEpisode with foreignkey? Just use Episode only.
class Episode(models.Model):
channel = models.ForeignKey(Channel, on_delete=models.CASCADE)
tags = models.ManyToManyField(EpisodeTag)
#some other fields
Then you can get your serialized data like below
{
"id": 6,
"channel_id": 2,
"channel": {
"id": 2,
"tags": [
"new"
]
},
}

django get elements with biggest qty of content_set

I'm trying to do a reverse key query. The query is this: the foo elements that has the biggest quantity of bar.
Foo:
name = models.CharField('name', max_length=255)
Bar:
name = models.CharField('name', max_length=255)
foo = models.ForeignKey(Foo)
foo_1.name = foo1
foo_2.name = foo2
foo_3.name = foo3
bar_1.name = bar1
bar_1.foo = foo1
bar_2.name = bar2
bar_2.foo = foo2
bar_3.name = bar3
bar_3.foo = foo2
bar_4.name = bar4
bar_4.foo = foo3
bar_4.name = bar4
bar_4.foo = foo3
'Foo2' has 2 'bars' and 'foo3' has 2 'bars' while 'foo1' has only one 'bar'
the result i want is a list with the foos with the biggest quantity of bars and the qty of bars like this:
[{'foo': foo2, 'qty_of_bars': 2}, {'foo': foo3, 'qty_of_bars': 3}]
I've tried a lot of things, for example:
foos = Foo.objects.all()
foos_with_biggest_qty_of_bar = foo.annotate(bar_count=Count('bar')).order_by(
'-bar_count').first()
This gives only one foo, the one with the biggest qty of bars, for example 'foo3', but there may be more than one. In the case above there are 2, foo2 and foo3.
I've tried this:
foos_with_biggest_qty_of_bar = foo.annotate(bar_count=Count('bar')).order_by(
'-bar_count').first()
This gives me the biggest qty of bars related to one single foo
biggest_qty_of_bars = bars.objects.filter(foo=foos_with_biggest_qty_of_bar ).count()
foos_with_biggest_qty_of_bars = Foo.objects.all().annotate(total_bars=biggest_qty_of_bars )
This last line didnt work, i got:
'int' object has no attribute 'lookup'
I tried other thing with a for loop(it's terrible, very ugly, sorry guys for this, by it was an attempt on despair, it's driving me crazy):
qyt_of_bars_of_each_foo = Foo.object.values('name').annotate(
count_bar=Count('bar'))
biggest_number = 0
foo_with_biggest_number_of_bar = []
for qty_of_bar_of_foo in qyt_of_bars_of_each_foo :
if qyt_of_bars_of_each_foo ['count_bar'] >= biggest_number :
foo_with_biggest_number_of_bar .append(qty_of_bar_of_foo)
biggest_number = qyt_of_bars_of_each_foo ['count_bar']
Nothing gave me the result I wanted, which is a list with the foos with the biggest quantity of bars and the qty, like I said before. I'm still trying but as I said it's driving me Crazy, i'm stuck on it since yesterday. Any help I would appreciate a lot!!
Have you tried doing an annotation over the entire queryset using Count and then returning the values as a list?
foo_objects = Foo.objects.all().annotate(
qty_of_bars=Count('bar')).order_by('-qty_of_bars').values(
'name', 'qty_of_bars')
This would return a list, e.g.:
[{'name': u'Foo1', 'qty_of_bars': 3}, {'name': u'Foo2', 'qty_of_bars': 2}]
given an app module name of 'foo' and the following fixture data:
[{
"fields": {
"name": "Foo1"
},
"model": "foo.foo",
"pk": 1
}, {
"fields": {
"name": "Foo2"
},
"model": "foo.foo",
"pk": 2
}, {
"fields": {
"foo": 1,
"name": "Bar1-1"
},
"model": "foo.bar",
"pk": 1
}, {
"fields": {
"foo": 1,
"name": "Bar2-1"
},
"model": "foo.bar",
"pk": 2
}, {
"fields": {
"foo": 1,
"name": "Bar3-1"
},
"model": "foo.bar",
"pk": 3
}, {
"fields": {
"foo": 2,
"name": "Bar1-2"
},
"model": "foo.bar",
"pk": 4
}, {
"fields": {
"foo": 2,
"name": "Bar2-2"
},
"model": "foo.bar",
"pk": 5
}]
update
If you're needing to get to values where qt_of_bars is greater than 1, you can easily get to those via a list comprehension, since you can't filter on an annotated field via the ORM:
foo_objects = [{'foo': f, 'qty_of_bars': f.qty_of_bars}
for f in Foo.objects.all().annotate(
qty_of_bars=Count('bar')).order_by(
'-qty_of_bars') if f.qty_of_bars > 1]
[{'foo': <Foo: Foo1>, 'qty_of_bars': 3}, {'foo': <Foo: Foo2>, 'qty_of_bars': 2}]