Hi Karim,

On Wed, Dec 10, 2014 at 8:07 AM, Karim <[email protected]> wrote:

> Hi everyone! I have a "Services" model and I would like to get a
> QuerySet with all the services filtered based on the distance between
> the logged user and the service.
>
> I have latitude and longitude about the user logged in and I filter
> the services based on the computed distance.
>
> At the moment I build the QuerySet Service.objects.all() and after
> that I exclude the services that I don't need, but I think there is a
> better way to do that.
>
> I was thinking to use a Manager. Quoting the docs:
>
> "Adding extra Manager methods is the preferred way to add
> “table-level” functionality to your models. (For “row-level”
> functionality – i.e., functions that act on a single instance of a
> model object – use Model methods, not custom Manager methods.)"
>
> As a "eternal newbie" I ask you
>
> 1) The distance must be computed based on two parameters "long" and
> "lat". Is that possible to define a manager only for this purpose? Is
> it a good practice?
>

Only if you can perform that calculation "database-side". Since you want to
do a filtering operation, the property you want to filter on must either be
stored on the database, or computable via the database. Essentially, the
manager needs to be no more than a shorthand for a query chain using normal
Django query clauses. You can't use a method on the *model* because that
value will only exist on the Python side of the query, not the database
side.

(A quick caveat - you *could* do it by filtering on the Python side -
essentially, you filter as much as possible on the database, and then
provide a Python-side filter - essentially "[s for s in
Service.objects.all() if s.distance(some_user) < maximum_value]" - but that
puts additional load on your web server. If your list of services is short
to start with, or can be filtered to be relatively short, this might be a
viable option - YMMV)

In some cases, the database filtering can be helped by pre-computing a
filterable value and storing it as a value on the database model, so you
can use a simple Django filter on that value. However, in your case, it's
not just about the lat/long of the object being filtered, but the lat/long
of the user as well.

Managers themselves can't take arguments, so the closest you'll get is a
method *on* the manager that can return a queryset. The fact that the
method is on the Manager is largely irrelevant - that's just a convenience.
You need to be able to construct a query chain, using purely database-side
queries. Putting that method on the manager is really just a convenience to
put the query in an obvious place; being on the manager doesn't give the
query any additional power.

If you're using Django 1.7, you should also look into using custom query
sets *as* managers:

https://docs.djangoproject.com/en/1.7/releases/1.7/#calling-custom-queryset-methods-from-the-manager

This means you'll be able to access your "nearest filter" anywhere in a
query chain. After all - does it really matter if you say:

Services.objects.nearest(some_user).filter(is_fun=True)

or

Services.objects.filter(is_fun=True).nearest(some_user)

(Maybe it does in your particular case, but broadly speaking, it shouldn't).

You may also want to take a look at django.contrib.gis (also known as
GeoDjango):

https://docs.djangoproject.com/en/1.7/ref/contrib/gis/

This toolbox gives you a whole bunch of useful tools for performing
geographic queries, including computing the distance to a point,
determining if a point is inside a polygonal geographic boundary, and so
on. It's a little more difficult to set up, but it's much more powerful
(and accurate) than a simple lat/lng pair.

The Expressions API that will be part of Django 1.8 would also be useful to
you:

https://docs.djangoproject.com/en/dev/ref/models/expressions/

However, that would be riding the bleeding edge of Django, so unless you're
looking to be particularly adventurous, I'd stick to the more stable
options.


> 2) The computed value is not just useful for the QuerySet, but I need
> also that on client side so I serialize it and I send it using JSON.
> Is possible to make sure that the manager attach the field "distance"
> to the objects in the QuerySet?
>

If it's available for the database to filter, then it will automatically be
on the objects in the QuerySet, which means it will be available for
serialisation.

Yours,
Russ Magee %-)

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/CAJxq84-J--XpJL%3DJjaVqcvu1qcC7yUUxu%2BB98A8suMTNPqFYZA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to