问题
I have an application that has been storing longitude and latitude now i want to integrate it with geodjango the application looks like this.
class Location(models.Model):
#other fields here
lat = models.CharField(blank=True, max_length=100)
lng = models.CharField(blank=True, max_length=100)
now I will like to filter locations based on a distance say filter all location 1km away from location with pk = 1 using django.contrib.gis.measure import D and GEOSGeometry or should i refactor the model to have point rather than longitude and latitude so i can do something like this :
Location.objects.filter(point__dwithin=(D(km=5)))
any advice and recommendations will do.
回答1:
Using south... it would take 3 migrations to address this. (Link: How to install South]
1) Add a PointField
called point
:
from django.contrib.gis.db import models <-- NEW
# ...
class Location(models.Model):
#other fields here
lat = models.CharField(blank=True, max_length=100)
lng = models.CharField(blank=True, max_length=100)
point = models.PointField(null=True) <-- NEW
2) Perform a schemamigration
automagically
patrick@localhost:~$ python manage.py schemamigration my_app_name --auto
3) Perform a custom datamigration
where you will create a point on each element from the existing CharField
attributes
patrick@localhost:~$ python manage.py datamigration my_app_name latlng_to_point
Edit the file that is created by the datamigration
management command:
# my_app_name/migrations/0034__latlng_to_point.py
from django.contrib.gis.geos import Point
# ...
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
# Note: Remember to use orm['appname.ModelName'] rather than "from appname.models..."
for location in orm['my_app_name.Location'].objects.all():
location.point = Point(float(location.lng), float(location.lat), srid=4326)
location.save()
def backwards(self, orm):
"Write your backwards methods here."
for location in orm['data.Location'].objects.all():
location.point = None
location.save()
4) Delete the CharField
from your model and remove null=True
if you wish:
class Location(models.Model):
# other fields here
# DELETED lat = models.CharField(blank=True, max_length=100)
# DELETED lng = models.CharField(blank=True, max_length=100)
point = models.PointField(null=False) <-- NEW: null=False
5) Create another schemamigration automatically
patrick@localhost:~$ python manage.py schemamigration my_app_name --auto
6) Run the migrations successfully! This is when changes are written to your DB, etc.
patrick@localhost:~$ python manage.py migrate my_app_name
回答2:
Schema Migration
Fast forward 2016, and we have migrations built into django you no longer need south. You need only two migrations instead of three.
Step 1: change the model.
class Location(models.Model):
#other fields here
lat = models.CharField(blank=True, max_length=100)
lng = models.CharField(blank=True, max_length=100)
point = models.PointField(null=True)
step 2: create the migration
./manage.py makemigrations
Data Migration
This is the third step and involves editing the migration file created above, look at the operations section and add
migrations.RunSQL('''UPDATE myapp_location SET `point` = Point(lat,lng)''')
This query is for mysql. If you are using PostGis your query would be
migrations.RunSQL('''UPDATE myapp_location SET "point" = ST_MakePoint(lng,lat)''')
Note that the format for postgis is lng,lat
Note that this is a single query and should execute reasonably quickly. To iterate through all the rows with python and update them one by one would mean a million queries if you have a million records!
Step 4: ./manage.py migrate
Step 5: drop the lat, lng columns from the model.
step 6: ./manage.py makemigrations
step 7: ./manage.py migrate
来源:https://stackoverflow.com/questions/21156630/how-to-filter-geodjango-based-on-longitude-and-latitude