Hi Django Community, I’m facing an issue in my project. I want to retrieve closest 3-5 restaurants to a User based on his location, OK!
I tried this
// Fetch City First
user_location = Point(float(city.lat), float(city.lng),srid=4326)
closest_restaurants = Restaurant.objects.filter(city=city).annotate(
... distance=Distance(
... Point('long', 'lat', srid=4326), user_location)
... ).order_by('distance')
But this Gives me following Error
raise TypeError('Invalid parameters given for Point initialization.')
TypeError: Invalid parameters given for Point initialization.
As lat , lng is DECIMAL FIELD (as in case of user_location, it gives Error if we do not convert it into float).
Please i’m stuck on this from last 2 days, tried a lot, but couldn’t resolve.
Please note that I could have done this by other things using haversine also , looping Restaurants, but I want to know if i can do this in the most optimal format.
Thanks
Side note: When posting code here, enclose the code between lines of three backtick - ` characters. This means you’ll have a line of ```, then your code, then another line of ```. This forces the forum to keep your code properly formatted. (I’ve taken the liberty of editing your original post for this.)
On this clause:
Are 'long' and 'lat' meant to be references to fields in the model? If so, they should be enclosed in an F expression.
(Otherwise, I’m not understanding what you’re trying to do here.)
But there’s another thing to consider since lat and long are Decimal Fields, Point() raises Error when we fed decimals to it. Instead when we give floats to it, it works as given below Point(float(lat), float(long), srid=4326)
Actually I want to do following
1.fetch restaurants for given city
2.fetch closest restaurants to the user location (take city lat, long for that purpose)
I want to achieve that in single query
From my post, it’s clear that I’m using annotate with Distance() to create a dynamic field distance in retrieved restaurants and then sort the restaurants in the ASC ORDER of that distance.
Since Distance() works with Point(), and I don’t have any PointField() in city or in Restaurant model, but I’ve lat and long decimal fields in both models. So I want to create Point() from lat and long. For city it’s very easy but for Restaurant table on which I’m Querying I’m unable to think how to reference lat, long and same time Cast them to float to avoid getting Invalid arguments Error raised by Point()
# fetch city from City model
user_location = Point(float(city.lat), float(city.lng),srid=4326)
closest_restaurants = Restaurant.objects.filter(city=city).annotate(
... distance=Distance(
... Point(F('long') ,F('lat') , srid=4326), user_location)
... ).order_by('distance')
Please let me know if I can provide any extra information
>>> user_loc=Point(float(city.long), float(city.lat), srid=4326)
>>> user_loc
<Point object at 0x7f88f7b87210>
>>> Restaurant.objects.filter(city=city).annotate(
... distance=Distance(
... Point(F('long'), F('lat'),srid=4326),
... user_loc
... ))
Traceback (most recent call last):
File "<console>", line 3, in <module>
File "/home/ashiq/ttw/lib/python3.10/site-packages/django/contrib/gis/geos/point.py", line 35, in __init__
raise TypeError('Invalid parameters given for Point initialization.')
TypeError: Invalid parameters given for Point initialization.
Overall The Problem reduces to the following:-
I’m UNABLE TO CREATE A Dynamic PointField on my DB Table (with lat & long fields) dynamically (while annotating the Query)
To fix the error raised by your code, you need to convert the lat and lng fields of the city model to floats before passing them to the Point() constructor. You can do this using the django.db.models.functions.Cast() function.
Here is a revised version of your code that should work:
@ashleycoder
Thanks for your Response. But this also doesn’t work.
>>> user_location = Point(
... Cast(city.lat, output_field=FloatField()),
... Cast(city.long, output_field=FloatField()),
... srid=4326,
... )
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/ashiq/ttw/lib/python3.10/site-packages/django/contrib/gis/geos/point.py", line 35, in __init__
raise TypeError('Invalid parameters given for Point initialization.')
TypeError: Invalid parameters given for Point initialization
This statement is not a query. The Cast function is a database function that gets passed through to the database in the generated SQL, it is not a function that generates a result itself in Python.
In other words, your original definition for user_location was correct - you don’t use Cast for it. What you would need to cast are the references to the long and lat fields in the Point method call in the query.
But looking at this some more, I’m not sure that’s going to be the correct construct either unless Point is a GIS function in the database, in which case you’re dealing with a name conflict. (Unfortunately, I’m not familiar enough with PostGIS to know.)
What you need to be cognizant of is where every function is being called or is trying to be executed.
If Point is a Python object, then it must be called within Python - passing that function to the database isn’t going to work. So if you’re going to annotate a value in a database, you need to call the corresponding PostGIS method to create the point in the database, and then calculated the distance using the appropriate PostGIS distance function.