this is my first question. If my question is not clear, let me know :)
So I learn Django (learning by doing) for 4 Months in my project. I have one table name material, which is the table contains material name and price. Here I will describe my table:
id
materialName
price
1
Not Required
0
2
Material 1
123
3
Material 2
456
4
Material 3
900
I want to calculate the total material price by getting the price from the table. It looks ok if I only want to get 1 price only. But, I need to get 4 values of prices from what the user wants in input. Let's say the user chooses Material 3, Material 2, Not Required and Not Required. So there is 4 material price. Then, I use a queryset like this:
x = rawmaterial.objects.filter(Q(materialName = 'Material 3') | Q(materialName = 'Material 2') | Q(materialName = 'Not Required') | Q(materialName = 'Not Required')).values_list('price',flat=True)
but the result is (900, 456, 0) not like (900, 456, 0, 0). I have been trying with SQL query using OR, is there any possibility to get 4 values? Thank you :)
You can't query like that. Filter will filter out the values, not make them 0. Instead, you can use Conditional Expression. In your case, you can consider Case:
from django.db.models import Case, Value, When, F
rawmaterial.objects.annotate(
required_prices=Case(
When(materialName__in =['Material 3','Material 2', 'Not Required'], then=F('price')),
default=Value(0),
)
).values_list('required_prices', flat=True)
FYI, please follow the pep-8 style guide for naming class name (Pascal Case) and field/function names (snake case).
Related
I have a fairly simple query I'd like to make via the ORM, but can't figure that out..
I have three models:
Location (a place), Attribute (an attribute a place might have), and Rating (a M2M 'through' model that also contains a score field)
I want to pick some important attributes and be able to rank my locations by those attributes - i.e. higher total score over all selected attributes = better.
I can use the following SQL to get what I want:
select location_id, sum(score)
from locations_rating
where attribute_id in (1,2,3)
group by location_id order by sum desc;
which returns
location_id | sum
-------------+-----
21 | 12
3 | 11
The closest I can get with the ORM is:
Rating.objects.filter(
attribute__in=attributes).annotate(
acount=Count('location')).aggregate(Sum('score'))
Which returns
{'score__sum': 23}
i.e. the sum of all, not grouped by location.
Any way around this? I could execute the SQL manually, but would rather go via the ORM to keep things consistent.
Thanks
Try this:
Rating.objects.filter(attribute__in=attributes) \
.values('location') \
.annotate(score = Sum('score')) \
.order_by('-score')
Can you try this.
Rating.objects.values('location_id').filter(attribute__in=attributes).annotate(sum_score=Sum('score')).order_by('-score')
I have a django model Level for a game.
class Level(models.Model):
key = models.CharField(max_length=100)
description = models.CharField(max_length=500)
requiredPoints = models.IntegerField()
badgeurl = models.CharField(max_length=100)
challenge = models.ForeignKey(Challenge)
I now want to query for the highest level with a pointsRequired value smaller than a given value.
If i have:
Level 1: requiredPoints: 200
Level 2: requiredPoints: 800
Level 3: requiredPoints: 2000
When I enter e.g. 900 or 1999 as a query parameter, I want level 2 to be returned, when entering 10000 it should be level 3.
in sql it would look like
select pointsRequired,
abs(pointsRequired - parameter) as closest
from the_table
order by closest
limit 1
Any tips? Do I have to use an extra Query-Set? How would it look like
I don't think your SQL is correct. It should return 'level 3' if the parameter is 1999. Based on your description the SQL could be:
SELECT pointsRequired FROM the_table WHERE pointsRequired < parameter ORDER BY pointsRequired DESC LIMIT 1;
Or in Django:
try:
Level.objects.filter(requiredPoints__lt=parameter).order_by('-requiredPoints')[0]
except IndexError:
# Do something
For example if I have models.py like this:
Handler(models.Model):
model1 = ForeignKey(Model1)
model2 = ForeigKey(Model2)
user = models.ForeignKey(User)
For example there are 100 instances of Handler with Model1 id = 1, but Model 2 id for this 100 instances is in range 1 to 5. And when I do something like this:
Handles.objects.filter(model1=1).values_list('model2_id', flat=True)
It returns list of 5 id values or list of 100 id values, which are be repeated?
And if it return 100 values is there possibility to remain only one value for every repeated value?
It will return a list of 100 id values.
If you want to get the unique 5 ones, then you can do that in python.
model2_id_uniq_values = list(set(Handles.objects.filter(model1=1).values_list('model2_id', flat=True)))
It may not be the most finely tuned algorithm, and by using a set, you would lose the order. But for your purposes it appears to work.
I'm filtering a big number of users based on attributes. For example, they can be filtered by minimum gpa.
So far I've been doing the following to construct my queryset-
('gpa_gt' just means that the gpa property has to be greater than whatever is the query param)
if len(request.GET) != 0:
kwargs = {}
if request.GET['mingpa']:
kwargs['gpa__gt'] = request.GET['mingpa']
profiles = BlahUser.objects.filter(**kwargs)
Now, the users have a first school major attribute and a second school major attribute. If I filter by a major, I want the user to show up in the results if his first major OR his second major match any of my selections (I use a multiple select on the filtering). What I have now only works for one major-
if request.GET.has_key('major'):
kwargs['major__in'] = request.GET.getlist('major')
I know how to explicitly write out the query, but in my case I'm building it dynamically of sorts, and am stumped at how to do so. Any help appreciated, thanks!
You inclusively or in Django querysets with the Q object:
Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith='Who')
You can operate on them like variables so you would like something like this:
query = Q( gpa__gt=request.GET['mingpa'] )
if request.GET.has_key('major'):
query = query | Q( major__in=request.GET.getlist('major') )
profiles = BlahUser.objects.get( query )
Borgar is definitely on the right track, but I think this is a little closer to what you are
looking for:
# filter for minimum GPA
gpa_filter = Q( gpa__gt=request.GET['mingpa'] )
major_filter = Q()
if request.GET.has_key('major'):
majors = request.GET.getlist('major')
# filter for students with first OR second major in specified majors
major_filter = Q( first_major__in=majors| Q(second_major__in=majors)
# run filter (these are AND'ed together), so we get students that:
# 1. Have a first or second major in the specified list of majors AND
# 2. Have a GPA above the specified minimum
profiles = BlahUser.objects.get( gpa_filter, major_filter )
Note that you can continue to use profiles as a regular queryset (as in this contrived example):
seniors_matching_request_params = profiles.filter(status='senior')
So, just use Q objects where you need to, then continue to operate on the queryset as normal .
I'm thinking of this so far- (course being major)
if request.GET.has_key('course'):
extra = ['Q(first_course IN ' + request.GET.getlist('course') + ') | Q(second_course IN ' + request.GET.getlist('course') + ')']
profiles = sumaConnectUser.objects.extra(where=[extra])
'course' is what we call a major. I haven't checked whether the above works but it's a start..
Help? lol
I'm programming a search on a model and I have a problem.
My model is almost like:
class Serials(models.Model):
id = models.AutoField(primary_key=True)
code = models.CharField("Code", max_length=50)
name = models.CharField("Name", max_length=2000)
and I have in the database tuples like these:
1 BOSTON The new Boston
2 NYT New York journal
3 NEWTON The old journal of Mass
4 ANEWVIEW The view of the young people
If I search for the string new, what I want to have is:
first the names that start with the string
then the codes that start with the string
then the names that contain the string
then the codes that contain the string
So the previous list should appear in the following way:
2 NYT New York journal
3 NEWTON The old journal of Mass
1 BOSTON The new Boston
4 ANEWVIEW The view of the young people
The only way I found to have this kind of result is to make different searches (if I put "OR" in a single search, I loose the order I want).
My problem is that the code of the template that shows the result is really redundant and honestly very ugly, because I have to repeat the same code for all the 4 different querysets. And the worse thing is that I cannot use the pagination!
Now, since the structure of the different querysets is the same, I'm wandering if there is a way to join the 4 querysets and give the template only one queryset.
You can make those four queries and then chain them inside your program:
result = itertools.chain(qs1, qs2, qs3, qs4)
but this doesn't seem to nice because your have to make for queries.
You can also write your own sql using raw sql, for example:
Serials.objects.raw(sql_string)
Also look at this:
How to combine 2 or more querysets in a Django view?
You should also be able to do qs1 | qs2 | qs3 | qs4. This will give you duplicates, however.
What you might want to look into is Q() objects:
from django.db.models import Q
value = "new"
Serials.objects.filter(Q(name__startswith=value) |
Q(code__startswith=value) |
Q(name__contains=value) |
Q(code__contains=value).distinct()
I'm not sure if it will handle the ordering if you do it this way, as this would rely on the db doing that.
Indeed, even using qs1 | qs2 may cause the order to be determined by the db. That might be the drawback (and reason why you might need at least two queries).