Here are my classes. What is a good way to query the User for the embedded Review objects and paginate it somehow?
class Review(EmbeddedDocument):
review_body = StringField(required=True)
review_stars = FloatField(required=True, default=0)
reviewer = LazyReferenceField('User')
review_date = DateTimeField()
class User(Document):
userreviews = EmbeddedDocumentListField(Review)
I'm sure I can get the total easily with .count() but I'm not sure how to skip and query for only a portion of the objects.
The answer, it seems, lies in the way I was querying the database. I was doing it incorrectly for what I was trying to accomplish.
What wasn't working:
userprofile: user.User = user.User.objects(
username=request.form["username"]).get()
all_reviews = userprofile.userreviews # work with objects here
What should work:
Using .filter() and .slice() I can get the embedded Review objects like so:
reviews = user.User.objects.filter(username=request.form["username"]).fields(
slice__userreviews=[0, 2])
for r in reviews:
print(r.userreviews)
This will return 2 review objects starting at the index of 0. Then I just need to use .count() to get the total amount and I should have the 3 elements I need for pagination.
Related
I have rows of products, that tend to share some information, such as the same images (an image may show multiple products), descriptions, manufacturer, etc.
I would like to choose some fields to only show the information once. I can't use distinct for this (I don't think) as the information is not always identicle, such as with the description, but may be only subtly different enough that I don't want to repeat it.
At the moment, I am using a for loop to display fields from each result, which ends up with something like this:
This is the code I am using in my view:
def collection_detail(request, name=None):
template = loader.get_template('/webapps/mywebapp/furniture/main_page/templates/main_page/detail.html')
products = product.objects.filter(id=name)
cart_product_form = CartAddProductForm()
context={'products': products,
'cart_product_form': cart_product_form}
return HttpResponse(template.render(context))
What would be the appropriate way to do this in a view, ignoring subsequent rows for some fields?
edit: Example queryset results
collection_name|manufacturer|product_type|description|image_url
----------------------------------------------------------------
Testing |FakeCo |Bed |pretty nice|/img/1.jpg
Testing |FakeCo |Desk |pretty bad |/img/2.jpg
Testing |FakeCo |Nightstand |pretty ok |/img/1.jpg
Testing |FakeCo |Draws |pretty nice|/img/3.jpg
In the example of the above data, I want to show the collection name and manufacturer just once, so the first result would do, each product type, the first description regardless of what it says, and each image that is distinct and not a duplicate.
products = product.objects.filter(id=name)
collection_name = []
manufacturer = []
product_type = []
description = []
image_url = []
for product in products:
collection_name.append(product.collection_name)
manufacturer.append(product.manufacturer)
product_type.append(product.product_type)
description.append(product.description)
image_url.append(product.image_url)
collection_name = set(collection_name)
manufacturer = set(manufacturer)
product_type = set(product_type)
description = set(description)
image_url = set(image_url)
It is little long but it will solve your problem. "set" will make all common names once in the list.
By this
products = product.objects.filter(id=name)
it will return a queryset, that means it consists of several products information.
If you want to get only single objects information change the query like below:
products = product.objects.filter(id=name).first()
or,
products = product.objects.filter(id=name)[:1]
Update
If you want to display some fields one value then use this:
{{ value|first }}
I have a model that tracks the number of impressions for ads.
class Impression(models.Model):
ad = models.ForeignKey(Ad, on_delete=models.CASCADE)
user_ip = models.CharField(max_length=50, null=True, blank=True)
clicked = models.BooleanField(default=False)
time_created = models.DateTimeField(auto_now_add=True)
I want to find all the user_ip that has more than 1000 impressions. In other words, if a user_ip comes up in more than 1000 instances of Impression. How can I do that? I wrote a function for this but it is very inefficient and slow because it loops over every impression.
def check_ip():
for i in Impression.objects.all():
if Impression.objects.filter(user_ip=i.user_ip).count() > 1000:
print(i.user_ip)
You should be able to do this in one query with aggregation.. it is possible to filter on aggregate values (like Count()) as follows:
from django.db.models import Count
for ip in Impression.objects.values('user_ip').annotate(ipcount=Count('user_ip')).filter(ipcount__gt=1000):
# do something
Django querysets have an annotate() method which supports what you're trying to do.
from django.db.models import Count
Impression.objects.values('user_ip')\
.annotate(ip_count=Count('user_ip'))\
.filter(ip_count__gt=1000)
This will give you a queryset which returns dictionaries with 'user_ip' and 'ip_count' keys when used as an iterable.
To understand what's happening here you should look at Django's aggregation guide: https://docs.djangoproject.com/en/1.11/topics/db/aggregation/ (in particular this section which explains how annotate interacts with values)
The SQL generated is something like:
SELECT "impression"."user_ip", COUNT("impression"."user_ip") AS "ip_count"
FROM "impression"
GROUP BY "impression"."ip"
HAVING COUNT("impression"."ip") > 1000;
Jello!
I'm working with django right now and I have a problem which I suspect has a simple solution, but I've had a lot of difficulty trying to find it (most of the relevant keywords tend to point google to the wrong places).
I'm looking to filter a query based on the criteria that: A model has a field that points to itself.
I'll give an example:
class Person(models.Model):
name = models.CharField(max_length=20)
parent = models.ForeignKey('app.Person', null=True)
gramps = Person.objects.create(name='Tod', parent=None)
pops = Person.objects.create(name='Hank', parent=gramps)
sonny_boy = Person.objects.create(name='Tim', parent=pops)
temporal_paradox_dude = Person.objects.create(name='Emmett')
temporal_paradox_dude.parent = temporal_paradox_dude
temporal_paradox_dude.save()
temporal_paradox_dude_2 = Person.objects.create(name='Arnold')
temporal_paradox_dude_2.parent = temporal_paradox_dude_2
temporal_paradox_dude_2.save()
So the question is, in this case, how can I query the Person table for only people who are temporally paradoxical?
>>> Person.objects.filter(parent=SOME_WAY_OF_DECLARING_ITSELF)
[ 'Emmett', 'Arnold' ]
What do I need to replace SOME_WAY_OF_DECLARING_ITSELF with in order to get models that link to themselves?
Thanks!
I think you looking for something like this;
from django.db.models import F
Person.objects.filter(parent_id=F('id'))
You could do a simple query and then filter by iterating through that query:
results = [x for x in Person.objects.all() if x.parent == x]
I don't have much experience with Django (I'm using 1.3) so I have the feeling on the back of my head that this is a dumb question... But anyway:
I have models like this:
class User(models.Model):
name = models.CharField()
class Product(models.Model):
name = models.CharField()
public = models.BooleanField()
class Order(models.Model):
user = models.ForeignKey(User)
product = models.ManyToManyField(Product, through='OrderProduct')
class OrderProduct(models.Model):
product = models.ForeignKey(Product)
order = models.ForeignKey(Order)
expiration = models.DateField()
And let's say I do some query like this
Product.objects.filter(order__status='completed', order__user____id=2)
So I'd get all the products that User2 bought (let's say it's just Product1). Cool. But now I want the expiration for that product, but if I call Product1.orderproduct_set.all() I'm gonna get every entry of OrderProduct with Product1, but I just want the one returned from my queryset.
I know I can just run a different query on OrderProducts, but that would be another hit on the database just to bring back data the query I ran before can already get. .query on it gives me:
SELECT "shop_product"."id", "shop_product"."name"
FROM "shop_product"
INNER JOIN "shop_orderproducts" ON ("shop_product"."id" = "shop_orderproducts"."product_id")
INNER JOIN "shop_order" ON ("shop_orderproducts"."order_id" = "shop_order"."id")
WHERE ("shop_order"."user_id" = 2 AND "shop_order"."status" = completed )
ORDER BY "shop_product"."ordering" ASC
If I could SELECT * instead of specific fields I'd have all the data that I need in one query. Is there anyway to build that query and get only the data related to it?
EDIT
I feel I need to clarify some points, I'm sorry I haven't been clearer:
I'm not querying against OrderProduct because some products are public and don't have to be bought but I still have to list them, and they'd not be returned by a query against OrderProduct
The result I'm expecting is a list of products, along with their Order data (in case they have it). In JSON, it'd look somewhat like this
[{id: 1, order: 1, expiration: 2013-03-03, public: false},
{id: 1, order: , expiration: , public: true
Thanks
I'm gonna get every entry of OrderProduct with Product1, but I just
want the one returned from my queryset.
You just want which "one"? Your query is filtering on the Product model, so all Users, Orders, and OrderProducts associated with each of the Products in the returned queryset will be accessible.
If you want one specific OrderProduct, then you should be filtering as op = OrderProduct.objects.filter(xxxxx) and then accessing the models up the chain like so:
op.product, op.order, etc.
I would have suggested the method prefetch_related, but this isn't available in Django 1.3.
Dan Hoerst is right about selecting from OrderProduct, but that still hits the database more than necessary. We can stop that by using the select_related method.
>>> from django.db import connection
>>> len(connection.queries)
0
>>> first_result = OrderProduct.objects.select_related("order__user", "product")
... .filter( order__status="completed",
... order__user__pk=2 )[0]
>>> len(connection.queries)
1
>>> name = first_result.order.user.name
>>> len(connection.queries)
1
>>> product_name = first_result.product.name
>>> len(connection.queries)
1
class Price(models.Model):
date = models.DateField()
price = models.DecimalField(max_digits=6, decimal_places=2)
product = models.ForeignKey("Product")
class Product(models.Model):
name = models.CharField(max_length=256)
price_history = models.ManyToManyField(Price, related_name="product_price", blank=True)
I want to query Product such that I return only those products for whom the price on date x is higher than any earlier date.
Thanks boffins.
As Marcin said in another answer, you can drill down across relationships using the double underscore syntax. However, you can also chain them and sometimes this can be easier to understand logically, even though it leads to more lines of code. In your case though, I might do something that would look this:
first you want to know the price on date x:
a = Product.objects.filter(price_history__date = somedate_x)
you should probably test to see if there are more than one per date:
if a.count() == 1:
pass
else:
do something else here
(or something like that)
Now you have your price and you know your date, so just do this:
b = Product.objects.filter(price_history__date__lt = somedate, price_history__price__gt=a[0].price)
know that the slice will hit the database on its own and return an object. So this query will hit the database three times per function call, once for the count, once for the slice, and once for the actual query. You could forego the count and the slice by doing an aggregate function (like an average across all the returned rows in a day) but those can get expensive in their own right.
for more information, see the queryset api:
https://docs.djangoproject.com/en/dev/ref/models/querysets/
You can perform a query that spans relationships using this syntax:
Product.objects.filter(price_history__price = 3)
However, I'm not sure that it's possible to perform the query you want efficiently in a pure django query.