Filter based on result of model method

2,758 views
Skip to first unread message

Wiiboy

unread,
Jul 3, 2010, 1:20:35 PM7/3/10
to Django users
Is there a way to filter based on the result of a model instance
method?

I've got a Permission model with a one-to-one relationship with an
Article. The Permission model stores the different groups that the
Article author said could read their article. There's about 6 groups
though, so I wrote an instance method that calculates whether a given
user can view an Article.

I want to filter based on whether that instance method returns true or
not. I could do something like the following, but it seems very
inefficient.

def myview(request):
q = Article.objects.all()
for item in q:
if not item.permission.can_view_article(request.user):
q = q.exclude(item.pk)

Wiiboy

unread,
Jul 6, 2010, 2:11:09 AM7/6/10
to Django users
Anyone?

Mark Linsey

unread,
Jul 6, 2010, 2:33:24 AM7/6/10
to django...@googlegroups.com
Perhaps someone with much more Django experience will correct me, but I am 99% sure that this is impossible.

As I understand it, parameters passed to filter() are translated into SQL statements.  An arbitrary python method that you write for your object can do any number of things, which might not be translatable into SQL.  So I don't see how any "filter on the result of any method" feature would work, other than doing exactly the implementation you describe.

However, your specific use case sounds like it should be possible to avoid iterating over everything by changing your model.  Is there a reason why each Permission model can't have a ManyToMany field with the set of users allowed by that permission model?

On Mon, Jul 5, 2010 at 11:11 PM, Wiiboy <jord...@gmail.com> wrote:
Anyone?

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.


Wiiboy

unread,
Jul 6, 2010, 8:47:48 PM7/6/10
to Django users

> As I understand it, parameters passed to filter() are translated into SQL
> statements.  An arbitrary python method that you write for your object can
> do any number of things, which might not be translatable into SQL.  So I
> don't see how any "filter on the result of any method" feature would work,
> other than doing exactly the implementation you describe.

Curses, that's what I thought.

> However, your specific use case sounds like it should be possible to avoid
> iterating over everything by changing your model.  Is there a reason why
> each Permission model can't have a ManyToMany field with the set of users
> allowed by that permission model?

That'd be really hard to maintain, I think. I'd have to evaluate
whether the current user could view _every_ article every time they
changed their profile, registered, etc.

euan.g...@gmail.com

unread,
Jul 7, 2010, 6:03:31 AM7/7/10
to Django users
As stated there is no way to filter on any non-field attribute,
property or method.

However, don't despair. If the result of your permission check can be
expressed as another Django query then it should be possible to
construct something in the ORM that uses this. That said, we have had
trouble with highly complex permissions and large querysets and have
had to come up with a permissions caching mechanism so that our site
doesn't do too many queries per page.

Good luck, Euan

Matthias Kestenholz

unread,
Jul 7, 2010, 6:08:52 AM7/7/10
to django...@googlegroups.com
On Sat, Jul 3, 2010 at 7:20 PM, Wiiboy <jord...@gmail.com> wrote:
> Is there a way to filter based on the result of a model instance
> method?
>
> I've got a Permission model with a one-to-one relationship with an
> Article.  The Permission model stores the different groups that the
> Article author said could read their article.  There's about 6 groups
> though, so I wrote an instance method that calculates whether a given
> user can view an Article.
>
> I want to filter based on whether that instance method returns true or
> not. I could do something like the following, but it seems very
> inefficient.
>

As other have already written, it's not possible to filter on the
result of a model method.

There's at least one thing you can do differently to speed up this method a bit.

> def myview(request):
>   q = Article.objects.all()
>   for item in q:
>       if not item.permission.can_view_article(request.user):
>           q = q.exclude(item.pk)
>

Everytime you call exclude (btw, this should be q =
q.exclude(pk=item.pk), no?), a new queryset instance is constructed.
This is probably not neccessary. You could do this instead, assuming
permission is another database model linked with a foreign key:

articles = [article for article in
Article.objects.all().select_related('permission') if
article.permission.can_view_article(request.user)]

At least you'll hit the article table only once this way.

Wiiboy

unread,
Jul 7, 2010, 9:43:12 AM7/7/10
to Django users

> Everytime you call exclude (btw, this should be q =
> q.exclude(pk=item.pk), no?),

Yes =) I wrote that quickly with my post.

> articles = [article for article in
> Article.objects.all().select_related('permission') if
> article.permission.can_view_article(request.user)]

Good idea.

Wiiboy

unread,
Jul 7, 2010, 2:42:02 PM7/7/10
to Django users

> articles = [article for article in
> Article.objects.all().select_related('permission') if
> article.permission.can_view_article(request.user)]

The only problem I see is that is that 'articles' is a list, not a
queryset. The reason I was using q.exclude(pk=item.pk) is because the
return value is still a queryset. But that's going to result in a
huge number of DB hits....curses.
Reply all
Reply to author
Forward
0 new messages