Get max value from a set of rows - python

This question is in relation to project 2 of the cs50 course which can be found here
I have looked at the following documentation:
Django queryset API ref
Django making queries
Plus, I have also taken a look at the aggregate and annotate things.
I've created the table in the template file, which is pretty straight forward I think. The missing column is what I'm trying to fill. Image below
These are the models that I have created
class User(AbstractUser):
pass
class Category(models.Model):
category = models.CharField(max_length=50)
def __str__(self):
return self.category
class Listing(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
description = models.TextField()
initial_bid = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
date_created = models.DateField(auto_now=True)
def __str__(self):
return self.title
class Bid(models.Model):
whoDidBid = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
list_item = models.ForeignKey(Listing, default=0, on_delete=models.CASCADE)
bid = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now=True)
def __str__(self):
return_string = '{0.whoDidBid} {0.list_item} {0.bid}'
return return_string.format(self)
This is the closest I could come to after a very long time. But the result I get is just the number 2. Ref image below
Listing.objects.filter(title='Cabinet').aggregate(Max('bid'))
Where 'Cabinet' is a Listing object that I have created. And placed two bids on them.
So the question is, how do I get the Maximum bid value(i.e. 110 for this case) for a particular listing? Using the orm. I think if I used a raw sql query, I could build a dict, send it to the template with the queryset. Then while looping through the queryset, get the value for the key, where the key is the name of the listing or something along those lines. Nah, I would like to know how to do this through the ORM please.

Here's answer #1
Bid.objects.filter(list_item__title='Cabinet').prefetch_related('list_item').aggregate(Max('bid'))
What happens when you try this (sorry, I don't have any objects like this to test on):
Bid.objects.values(list_item__title).prefetch_related('list_item').annotate(Max('bid'))

Related

How do I filter django models based on product fields?

I am trying to run a filter command, using related fields; and am unsure how to go about it:
class Listing(models.Model):
name = models.CharField(max_length=150)
slug = models.SlugField()
description = models.TextField()
catchphrase = models.TextField(null=True, blank=True)
picture_0 = models.ImageField(upload_to = "mainimages")
picture_1 = models.ImageField(null=True, blank=True, upload_to = "./product/static/product/imgs")
picture_2 = models.ImageField(null=True, blank=True, upload_to = "./product/static/product/imgs")
short_term_price = models.IntegerField(default=0)
long_term_price = models.IntegerField(default=0)
tax = models.IntegerField(default=0)
tag = models.ForeignKey('Tag', on_delete=models.PROTECT)
def __str__(self):
return self.name
class Order(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.PROTECT)
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
lease_date = models.DateField()
clear_date = models.DateField()
price = models.IntegerField(default=0) #cents
def __str__(self):
return self.listing.name
def get_display_price(self):
return "{0:.2f}".format(self.price / 100)
The general idea is that the user provides a start date and an end date and Django only returns the listings that aren't already in an order in that timeframe. I am unsure how to go about the view function:
def search_products(request, start_date, end_date):
listing = Listing.objects.select_related('order').all()
I will provide an answer as if you are using the lease_date to do the filtering. There is a couple of ways to achieve this. One is:
listing_qs = Listing.objects.filter(
pk__in=Order.objects.exclude(lease_date__range(start_date,end_date)).select_related('listing').values_list('listing__pk')
)
Code breakdown:
retrieve the orders by excluding those whose lease date is in between the provided timeframe
selecting the listing's pk (via values('listing__pk')) you can select any other attribute you want
using the result of the 2 previous instructions to get the Listing objects since we have the list of pk.
Another way:
Just exclude all the Listing objects whose lease date is in between the provided timeframe
Listing.objects.exclude(order_set__lease_date__range=(start_date,end_date))
I hope this helps.

Get a list of related objects in model's __str__

I have two models with One-to-Many relationship; Listing, and Bids.
Is it possible to retrieve and display a list of Bid objects' bid_price in Listing's str method?
The code provided below crashes the server and I am not sure of the correct keywords to search for.
I understand how listing.bid_set works in the Shell or view, but I am not sure how to make it work here.
class Listing(models.Model):
title = models.CharField(max_length=64)
def __str__(self):
bid_objects = Bid.objects.all().filter(listing__id=self.id)
price_list = []
for bid_object in bid_objects:
price_list.append(bid_object.bid_price)
return f"{self.title}, {price_list}"
class Bid(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="listing_bids")
bid_price = models.DecimalField(max_digits=6, decimal_places=2)
Thanks for your help.
Since you specified related_name='listing_bids', it means you access the related Bids with self.listing_bids:
class Listing(models.Model):
title = models.CharField(max_length=64)
def __str__(self):
bid_objects = self.listing_bids.values_list('bid_price', flat=True)
return f'{self.title}, {bid_objects}'

Field 'id' expected a number but got <Listing: Ps4 Pro>

It's my first time creating a Django website with models, and in my first attempt to insert data into my table I'm getting this error.
My models are as follows:
class User(AbstractUser):
pass
#https://docs.djangoproject.com/en/3.1/topics/auth/default/
class Listing(models.Model):
listingID = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="listID")
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myListing", null=True)
watchers = models.ManyToManyField(User, blank=True, related_name="watchlist")
title = models.CharField(max_length=30)
description = models.TextField()
creation_date = models.DateField(auto_now=True)
img_url = models.URLField()
active = models.BooleanField(default=True)
def __str__(self):
return f"{self.title}"
class Bid(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.SET_NULL, related_name="bidsMadeOnMe", null=True, blank=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myBids", null=True)
price = models.FloatField()
creation_date = models.DateField(auto_now=True, null=True)
def __str__(self):
return f"Bid={self.price}"
and the view that handles the form submission is this one:
#login_required
def create_listing(request):
if request.method == "POST":
user = User.objects.get(username=request.user.username)
l = Listing(created_by=user,
title=request.POST["title"],
description=request.POST["desc"],
# https://stackoverflow.com/questions/12176585/handling-dates-over-request-get
creation_date=models.DateTimeField(auto_now_add=True),
img_url=request.POST["image_url"]
)
l.save()
b = Bid(l,
user,
request.POST["initial_bid"],
models.DateTimeField(auto_now_add=True)
)
b.save()
return render(request, "auctions/index.html")
I know the problem is the way I'm adding the data but I can't fix it. Can someone give me some light?
Your problem (well, several actually) is this:
b = Bid(l, user, request.POST["initial_bid"], models.DateTimeField(auto_now_add=True))
You're constructing a model instance by positional arguments instead of keyword arguments. This can be done, but then the invisible "id" column that has been added to the Bid model, is the first argument.
In Django we never construct models like that, but always use keyword arguments, so we're not depending on field order:
b = Bid(listing=l, user=user, ...))
Once you're solved that, your next problem is the date field.
Don't assign fields to model instances. Fields are class declarations, they don't belong on instances. Fields describe on a class (= a Model), what kind data to expect. On the instance, you assign that data.
In this case, your definition for the field is wrong on the model and on the instance you shouldn't even assign it - it will be automatically filled.
Overall, it feels like you haven't gone through Django's tutorial or did not fully understand the concepts. I suggest you go through it.

Django Querying Relation

the code did not return all of the item's name based on employee..? how to solve this probem? did the models wrong? or the query?
MODELS.PY
class Employee(models.Model):
name = models.CharField(max_length=100)
telephone_number = models.CharField(max_length=20)
address = models.TextField()
email = models.EmailField()
class Item(models.Model):
code = models.CharField(max_length=4)
name = models.CharField(max_length=100)
kind = models.CharField(max_length=100)
description = models.TextField()
class Inventory(models.Model):
employee = models.ForeignKey(Employee, on_delete=models.CASCADE)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
def get_absolute_url(self):
return reverse('inventaris-detail', kwargs={'pk': self.pk})
VIEWS.PY
how can i get all of the employee's item ?
query_set = Inventory.objects.all()
for query in query_set:
output.append([
query.employee.name,
query.item.name
])
i need something like query.employee.items_set.all() .. ?
You want to get all of the items from an employee? The following should achieve that:
employee = Employee.objects.all()[0] # Just get a random employee, you can do this with more employees too if you want
employees_items = [inventory.item for inventory in employee.inventory_set.all()]
Your code does not logically make a lot of sense though, to be honest. Most likely, there should be a field on a Item class which is a FK to Inventory. Your item class should probably be:
class Item(models.Model):
code = models.CharField(max_length=4)
name = models.CharField(max_length=100)
kind = models.CharField(max_length=100)
description = models.TextField()
inventory = models.ForeignKey(Inventory, on_delete=models.CASCADE)
(Of course this will not work since Inventory is defined after this class, but you can just put Inventory above it or use one of the other many methods to solve this problem)
Good luck!

Django next/prev instance of a model with a certain condition?

I want to find next instance of an object model but with a certain condition.
Models.py:
class Pin(models.Model):
submitter = models.ForeignKey(User)
url = models.TextField(blank=True, null=True)
price = models.DecimalField(blank=True, null=True, max_digits=10, decimal_places=2)
published = models.DateTimeField(auto_now_add=True)
I know pk of one instance to get the next instance I can do:
pin = Pin.objects.get(pk=123)
pin_next = pin.get_next_by_published()
But I want the next pin which has price not equal to null, so kind of an and condition. Next instance but with price not null. I can use a loop to keep on finding the next of next untill it has price not null. But is there any direct way?
You can pass additional lookup keyword args to the get_next_by_XXX methods, so in your above case pin.get_next_by_published(price__isnull=False) should work. If you have more complex conditions or want a non date-based ordering you'll have to write your own method.
You'll have to write the query yourself but it's fairly trivial :)
Note that since published might not be unique this might not always work as you would expect. I would recommend pk based navigation because of that.
class Pin(models.Model):
submitter = models.ForeignKey(User)
url = models.TextField(blank=True, null=True)
price = models.DecimalField(blank=True, null=True, max_digits=10, decimal_places=2)
published = models.DateTimeField(auto_now_add=True)
def others(self):
return self.objects.exclude(pk=self.pk)
def others_with_price(self):
return self.others().filter(price__isnull=False)
# By primary key:
def get_next(self):
return self.others_with_price(pk__gt=self.pk).order_by('pk')[0]
def get_prev(self):
return self.others_with_price(pk__lt=self.pk).order_by('-pk')[0]
# By published:
def get_next_published(self):
return self.others_with_price(published__gte=self.published).order_by('published')[0]
def get_prev_published(self):
return self.others_with_price(published__lte=self.published).order_by('-published')[0]

Categories

Resources