Sort by generic relation? - python

I have two models Post and Comment. Both can be given a Point through an extra ratings app. The Point has a generic relationship to any object necessary and a value.
Now I've created a list of Post and Comment objects using:
result_list = sorted(chain(post_list, comment_list),key=attrgetter('pub_date',))
The big question is: How can I sort by assigned Points instead of pub_date?
I'm looking for a Solution that does not modify Post, Comment or Point, because Point is in an extra app and I want all of them to work independent of each other. There is a third app that combines them though, so I can have a new view that does the trick. But I don't know how to do that.

something along the line of the following should do the trick:
result_list = sorted(
chain(post_list, comment_list),
key=lambda x: sum(
[
point.value for point in Point.objects.filter(
point_for_id=x.id,
point_for_class=ContentType.objects.get_for_model(x)
],
0
)
)
May I however suggest that you man up and modify the points app, a related_query_name would make this much simpler.

Related

Duplicating a model while searching [duplicate]

I've followed django tutorial and arrived at tutorial05.
I tried to not show empty poll as tutorial says, so I added filter condition like this:
class IndexView(generic.ListView):
...
def get_queryset(self):
return Question.objects.filter(
pub_date__lte=timezone.now(),
choice__isnull=False
).order_by('-pub_date')[:5]
But this returned two objects which are exactly same.
I think choice__isnull=False caused the problem, but not sure.
choice__isnull causes the problem. It leads to join with choice table (to weed out questions without choices), that is something like this:
SELECT question.*
FROM question
JOIN choice
ON question.id = choice.question_id
WHERE question.pub_date < NOW()
You can inspect query attribute of QuerySet to be sure. So if you have one question with two choices, you will get that question two times. You need to use distinct() method in this case: queryset.distinct().
Just use .distinct() at the end of your ORM.
A little late to the party, but I figured it could help others looking up the same issue.
Instead of using choice__isnull=False with the filter() method, use it with exclude() instead to exclude out any questions without any choices. So your code would look something like this:
...
def get_queryset(self):
return Question.objects.filter(pub_date__lte=timezone.now()).exclude(choice__isnull=True).order_by('-pub_date')[:5]
By doing it this way, it will return only one instance of the question. Be sure to use choice_isnull=True though.
Because you created two objects with same properties. If you want to ensure uniqueness, you should add validation in clean and add unique index on identifier field too.
Besides filter returns all the objects that match the criteria, if you are expecting only one item to be returned, you should use get instead. get would raise exception if less or more than 1 item is found.

Listing the title of created items in a choice field of another item in plone

I have a problem with my Plone item I cannot solve. 'Car' is supposed to create a list of all instances of 'Colour'.
All 'Colour' instances are in a given container. I cannot make it static because I want to add more 'Colour' instances in the future.
I tried selecting each item in my container and add it to my vocabularylist. I only need the id/title of my object, but I always end up with a giant stacktrace of failures.
In the end I want to choose a colour out of the given instances on creating a new 'Car' instance similar to a dropdown.
I have read the docs but cannot find a solution and this is my best idea.
I am also not a python programmer and this is my first plone project. I can add the complete failure list later if you need it.
I appreciate every bit of help. Thank you.
```colour= schema.Choice(
title=u"Colour",
description=u"Paintjob",
vocabulary=givecolour(),
required=False
)
#provider(IContextSourceBinder)
def givecolour():
colourlist = self.context.portal_catalog(path={"query" : "/madb-entw/it/colourcontainer", "depth" : 1})
list = []
i = 0
for colour in colourlist:
list.append(
SimpleVocabulary.createTerm(
colourlist.[i].getObject().id
)
)
i += 1
return SimpleVocabulary(list)```
Please always add your traces, so that we can help you better.
There is also the official community.plone.org forum, where are more people can help you.
I recommend you to use the plone.api to find your objects, this is a bit easier and well doumented.
something like this:
from plone import api
color_brains = api.content.find(context=api.content.get(path='/madb-entw/it/colourcontainer'), depth=1, portal_type='Color')
# no need to do getOject() here, get the id/title directly from the catalog brain
colors = [(color.id, color.Title) for color in color_brains]
One note to your query:
colourlist = self.context.portal_catalog(path={"query" :
"/madb-entw/it/colourcontainer", "depth" : 1})
Path has to be absolute, which means it includes the Plone site id and this can be different in another Plone site.
So an absolute path is not a good idea here, better get the portal object and traverse your path relative from there.
If madb-entw is your Plone site id:
portal.restrictedTraverse('it/colourcontainer')
or better as above, use plone.api.content.get(path='/it/colourcontainer')
Which is cleaner and easier.

Extra credit in Django tutorial

While going through the Django tutorial, I've decided to implement some "ideas for more tests" in part 5, namely
For example, it’s silly that Questions can be published on the site that have no Choices. So, our views could check for this, and exclude such Questions.
I've implemented this check in views.py by changing the IndexView.get_queryset function as follows
def get_queryset(self):
#Return the last five published questions with nonzero choice_set
result = [];
for q in Question.objects.all():
if q.choice_set.count() > 0:
result.append(q)
return sorted( result, key=attrgetter('pub_date'), reverse=True )[0:5]
Is there a more elegant way of doing this? Something along the lines of return Question.objects.filter(choice_set.count()>0).order_by('-pub_date')[:5] (which does not work for probably obvious reasons).
EDIT 1
Another possible answer would be return Question.objects.filter(pk__in=[x.question.pk for x in Choice.objects.all()]).order_by('-pub_date')[:5] (inspired by this). It still won't allow exclusion of questions with a single choice, like #catavaran suggested.
You can exclude all questions where choice=None, and it will exclude all questions without choices:
qs = Question.objects.exclude(choice=None).order_by'(-pub_date')[:5]
Or you can use choice__isnull=False, but this can return duplicate entries. Use .distinct() to counter that:
qs = Question.objects.filter(choice__isnull=False).distinct()
You can use aggregation:
from django.db.models import Count
Question.objects.annotate(num_choices=Count('choice')) \
.filter(num_choices__gt=0).order_by('-pub_date')[:5]
In fact poll with only one choice does not makes sense too :-) So it is better to use filter(num_choices__gt=1) instead of filter(num_choices__gt=0).

How does the get_list tastypie function work?

I'm trying to use the get_list tastypie function but I can't make it work. I've looked for documentation about that but I can't find it.
Whatever, I've a list of item ids and an ItemResource. I'm trying to return a list of serialized objects.
So I just want to do something like that :
item_resource = ItemResource()
item_ids = my_item_id_list
return item_resource.get_list(request, id=item_ids)
But of course it's not working.
What would be the correct syntax to do that ?
Thx !
Unless your ItemResource accepts filters (more here), you have to copy-paste all the stuff from here, lines #1306 - #1313.
The point is that get_list results get filtered only by obj_get_list (initial filters), and apply_filters (request-specific filters) so you have to skip directly to the serialization part (you can include the pagination part, if needed).
This is one of the cases where django-restframework appears to be better than django-tastypie - it refactores serialization out into a separate class, avoiding the code duplication.

Django - how to filter using QuerySet to get subset of objects?

According to documentation:
filter(**kwargs) Returns a new QuerySet containing objects that match
the given lookup parameters.
The lookup parameters (**kwargs) should be in the format described in
Field lookups below. Multiple parameters are joined via AND in the
underlying SQL statement.
Which to me suggests it will return a subset of items that were in original set.
However I seem to be missing something as below example does not behave as I would expect:
>>> kids = Kid.objects.all()
>>> tuple(k.name for k in kids)
(u'Bob',)
>>> toys = Toy.objects.all()
>>> tuple( (t.name, t.owner.name) for t in toys)
((u'car', u'Bob'), (u'bear', u'Bob'))
>>> subsel = Kid.objects.filter( owns__in = toys )
>>> tuple( k.name for k in subsel )
(u'Bob', u'Bob')
>>> str(subsel.query)
'SELECT "bug_kid"."id", "bug_kid"."name" FROM "bug_kid" INNER JOIN "bug_toy" ON ("bug_kid"."id" = "bug_toy"."owner_id") WHERE "bug_toy"."id" IN (SELECT U0."id" FROM "bug_toy" U0)'
As you can see in above subsel ends up returning duplicate records, which is not what I wanted. My question is what is the proper way to get subset? (note: set by definition will not have multiple occurrences of the same object)
Explanation as to why it behaves like that would be also nice, as to me filter means what you achieve with filter() built-in function in Python. Which is: take elements that fulfill requirement (or in other words discard ones that do not). And this definition doesn't seem to allow introduction/duplication of objects.
I know can aplly distinct() to the whole thing, but that still results in rather ugly (and probably slower than could be) query:
>>> str( subsel.distinct().query )
'SELECT DISTINCT "bug_kid"."id", "bug_kid"."name" FROM "bug_kid" INNER JOIN "bug_toy" ON ("bug_kid"."id" = "bug_toy"."owner_id") WHERE "bug_toy"."id" IN (SELECT U0."id" FROM "bug_toy" U0)'
My models.py for completeness:
from django.db import models
class Kid(models.Model):
name = models.CharField(max_length=200)
class Toy(models.Model):
name = models.CharField(max_length=200)
owner = models.ForeignKey(Kid, related_name='owns')
edit:
After a chat with #limelight the conclusion is that my problem is that I expect filter() to behave according to dictionary definition. And i.e. how it works in Python or any other sane framework/language.
More precisely if I have set A = {x,y,z} and I invoke A.filter( <predicate> ) I don't expect any elements to get duplicated. With Django's QuerySet however it behaves like this:
A = {x,y,z}
A.filter( <predicate> )
# now A i.e. = {x,x}
So first of all the issue is inappropriate method name (something like match() would be much better).
Second thing is that I think it is possible to create more efficient query than what Django allows me to. I might be wrong on that, if I will have a bit of time I will probably try to check if that is true.
This is kind of ugly, but works (without any type safety):
toy_owners = Toy.objects.values("owner_id") # optionally with .distinct()
Kid.objects.filter(id__in=toy_owners)
If performance is not an issue, I think #limelights is right.
PS! I tested your query on Django 1.6b2 and got the same unnecessary complex query.
Instead DISTINCT you can use GROUP BY (annotate in django) to get distinct kids.
toy_owners = Toy.objects.values_list("owner_id", flat=True).distinct()
Kid.objects.only('name').filter(pk__in=toy_owners).annotate(count=Count('owns'))

Categories

Resources