Django Filter deduce ManyToMany - python

Simple models with many to many relations on the Tag field.
class Tag(models.Model):
description = models.TextField()
class Species(models.Model):
name = models.CharField(max_length=256)
tags = models.ManyToManyField(Tag)
Now Im trying this, and as I understand, it should return a list that matches every tag_description in the tags list:
tag_list = request.GET.get('q').split(',')
species = Species.objects.filter(reduce(and_, [Q(tags__description=c) for c in tag_list]))
But its returning an empty list. It should be returning some objects.
It works when I give it only one tag
any idea why this is happening?
Im using it as was shown in this answer:
https://stackoverflow.com/a/8636836/228660

As you can actually see in the comments of that question, it doesn't actually work ;)
The problem is that you will be using the same join (which means the same object) for all filters. Unless all items in tag_list are identical this will never work. The problem lies within the Q objects instead of full filtered queries.
tag_list = request.GET.get('q').split(',')
# Generate a query with all species
all_species = Species.objects.all()
# Generate a list with the separately filtered species
filtered_species = [all_species.filter(tags__description=c) for c in tag_list]
# Join the filtered species together using the same method :)
species = reduce(and_, filtered_species, all_species)

I think another way of doing this would be:
tag_list = request.GET.get('q').split(',')
species = Species.objects.all().distinct()
for tag in tag_list:
species = species.filter(tags__description=tag)
else:
species = Species.objects.none()

Related

How can I paginate EmbeddedDocumentListField objects in Mongoengine?

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.

How to limit fields returned in django queryset per field

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 }}

Django - select model linked to itself

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]

How do I add choices to google datastore StringListProperty?

Okay here is the problem. I have this code
list_categories = [None,"mathematics","engineering","science","other"]
class Books(db.Model)
title = db.StringProperty(required=True)
author = db.StringProperty()
isbn = db.StringProperty()
categories = db.StringListProperty(default=None, choices = set(list_categories))
what i want to do here is have my book.categories be a SUBSET of list categories, for example
i have a book whose categories should be 'engineering' and 'mathematics', but when I set
book.categories = ['engineering','mathematics']
it webapp2 gives me an error
BadValueError: Property categories is ['engineering','mathematics']; must be one of set([None,"mathematics","engineering","science","other"])
My initial guess here is that i must set my list_choices to be a POWERSET of [None,"mathematics","engineering","science","other"], but this is too inefficient.
Does anyone know a workaround for this?
The reason for the error (as I'm sure you've guessed) is that StringListProperty does not do any special handling of the choices keyword argument - it simply passes it along to the ListProperty constructor, which in turn passes it to the Property constructor, where it is evaluated:
if self.empty(value):
if self.required:
raise BadValueError('Property %s is required' % self.name)
else:
if self.choices:
match = False
for choice in self.choices:
if choice == value:
match = True
if not match:
raise BadValueError('Property %s is %r; must be one of %r' %
(self.name, value, self.choices))
The issue is that it iterates through each choice individually, but it is comparing it to your entire list (value), which will never result in a match since a string won't equal a list (again, you know this :) ).
My suggestion would be to modify how you assign the list to the property. For instance, instead of:
book.categories = ['engineering','mathematics']
Try something like this:
for category in ['engineering','mathematics']:
book.categories.append(category)
Since the ListProperty contains a list, you can append each item individually so that it passes the test in the aforementioned code. Note that in order to get this to work in my tests, I had to set up the model in a slightly different way - however if you can get to the error you mentioned above, then the append method should work fine.
It makes it a little less straightforward, I agree, but it should circumvent the issue above and hopefully work.
Create a many to many relationship using list of keys. Use the categories property in class Book as a list of keys of class Category.
class Book(db.Model)
title = db.StringProperty(required=True)
author = db.StringProperty()
isbn = db.StringProperty()
# List Of Keys
categories = db.ListProperty(db.Key)
class Category(db.Model)
name = db.StringProperty(choices = ('science', 'engineering', 'math'))
For more info and code samples about modeling check out: https://developers.google.com/appengine/articles/modeling

Django help - how to get data from foreign keys

Given the following model setup (stripped down):
class Object(models.Model):
name = models.CharField(max_length=255)
class ObjectVersion(models.Model):
Cabinet = models.ForeignKey(Object)
Say I had a bunch of ObjectVersion entries that I'd filtered called "filteredObjects". How would I get the names of those objects into a vector?
You can use either values_list or values. The docs are available on the Django QuerySet API reference here.
>>> ObjectVersion.objects.filter(...).values('Cabinet__name')
[{'Cabinet__name':'foo'}, {'Cabinet__name':'foo2'}, ...]
or
>>> ObjectVersion.objects.filter(...).values_list('Cabinet__name')
[('foo',), ('foo2',), ...]
so you are saying that you have a list called filteredObjects?
objNames = [fo.name for fo in filteredObjects]
result = ObjectVersion.objects.filter(your filters here).values_list('Cabinet__name', flat=True)
Output:
result = ['ver_1', 'ver_2'..]

Categories

Resources