Building GIS Web Apps with Python & Django

Building GIS Web Apps with Python & Django

This was a *very brief* (20minutes) look at a simple GeoDjango app (a blog with a PointField), given at the MAGIC 2014 conference (Memphis Area Geographic Council).

D57aec10399cbb252bd890c2bb3fe1c9?s=128

Brad Montgomery

November 06, 2014
Tweet

Transcript

  1. Building GIS Web Apps with Python & Django ! Brad

    Montgomery MEMpy: Memphis Python User Group (mempy.org) Tennessee Data Commons (tndata.org)
  2. The Web framework for perfectionists with deadlines. ! http://djangoproject.com

  3. MVC-inspired

  4. MTV (model, template, view)

  5. Batteries Included • An object-relational mapper (ORM) • Form Validation

    • Testing Tools • Migrations (recently added to Django) • Built-in apps (admin, auth, caching, sessions, sitemaps, syndication, gis, … more)
  6. Community • 100’s of 3rd-party, open source apps • Many

    are MIT or BSD Licensed. • djangopackages.com
  7. Let’s Build a Blog!

  8. Directory Structure sampleproject/! !"" blog! # !"" __init__.py! # !""

    admin.py! # !"" migrations! # # $"" __init__.py! # !"" models.py! # !"" tests.py! # $"" views.py! # !"" templates/
  9. Projects & Apps sampleproject/! !"" blog! # !"" __init__.py! #

    !"" admin.py! # !"" migrations! # # $"" __init__.py! # !"" models.py! # !"" tests.py! # $"" views.py! # !"" templates/ Most of your work is here.
  10. models.py from django.contrib.auth.models import User! from django.contrib.gis.db import models! !

    ! class Post(models.Model):! author = models.ForeignKey(User)! title = models.CharField(max_length=128)! content = models.TextField()! slug = models.SlugField(max_length=128, unique=True)! published_on = models.DateTimeField(auto_now_add=True)! published_at = models.PointField(srid=4326)! ! objects = models.GeoManager()
  11. models.py from django.contrib.auth.models import User! from django.contrib.gis.db import models! !

    ! class Post(models.Model):! author = models.ForeignKey(User)! title = models.CharField(max_length=128)! content = models.TextField()! slug = models.SlugField(max_length=128, unique=True)! published_on = models.DateTimeField(auto_now_add=True)! published_at = models.PointField(srid=4326)! ! objects = models.GeoManager() corresponds to a database table
  12. models.py from django.contrib.auth.models import User! from django.contrib.gis.db import models! !

    ! class Post(models.Model):! author = models.ForeignKey(User)! title = models.CharField(max_length=128)! content = models.TextField()! slug = models.SlugField(max_length=128, unique=True)! published_on = models.DateTimeField(auto_now_add=True)! published_at = models.PointField(srid=4326)! ! objects = models.GeoManager() columns in the table
  13. models.py from django.contrib.auth.models import User! from django.contrib.gis.db import models! !

    ! class Post(models.Model):! author = models.ForeignKey(User)! title = models.CharField(max_length=128)! content = models.TextField()! slug = models.SlugField(max_length=128, unique=True)! published_on = models.DateTimeField(auto_now_add=True)! published_at = models.PointField(srid=4326)! ! objects = models.GeoManager()
  14. models.py from django.contrib.auth.models import User! from django.contrib.gis.db import models! !

    ! class Post(models.Model):! author = models.ForeignKey(User)! title = models.CharField(max_length=128)! content = models.TextField()! slug = models.SlugField(max_length=128, unique=True)! published_on = models.DateTimeField(auto_now_add=True)! published_at = models.PointField(srid=4326)! ! objects = models.GeoManager()
  15. gis fields from django.contrib.gis.db import models! ! class SomeModel(models.Model):! line

    = models.LineStringField()! poly = models.PolygonField()! geometry = models.GeometryField()! ! lines = models.MultiLineStringField()! points = models.MultiPointField()! polygons = models.MultiPolygonField()! geometries = models.GeometryCollectionField()! ! objects = models.GeoManager()
  16. ORM/gis lookups # Get a list of all Post objects!

    Post.objects.all()
  17. ORM/gis lookups # Get Posts mentioning “Foo”! Post.objects.filter(content__contains=“Foo”)

  18. ORM/gis lookups # Get Posts mentioning “Foo”! Post.objects.filter(content__contains=“Foo”) A field

    on our model
  19. ORM/gis lookups # Get Posts mentioning “Foo”! Post.objects.filter(content__contains=“Foo”) 2 underscores!

    “dunder”
  20. ORM/gis lookups # Get Posts mentioning “Foo”! Post.objects.filter(content__contains=“Foo”) A lookup

    operation
  21. ORM/gis lookups # Get Posts mentioning “Foo”! Post.objects.filter(content__contains=“Foo”) The value

    we want to find.
  22. ORM/gis lookups # Get a single Post object! Post.objects.get(id=7)

  23. ORM/gis lookups ! # Get the city using a WKT!

    City.objects.get(! ! mpoly__contains=‘POINT(-95.3385 29.7245)’! )!
  24. ORM/gis lookups ! # Get the city where a Post

    was published! City.objects.get(! ! mpoly__contains=post.published_at! )!
  25. ORM/gis lookups from django.contrib.gis.measure import D! ! # Find other

    posts published within 5 Miles.! radius = (post.published_at, D(mi=5))! Post.objects.filter(! published_at__distance__lte=radius! )
  26. ORM/gis lookups Distance Lookups ! •distance_lt •distance_lte •distance_gt •distance_gte •dwithin

    Spatial Lookups ‣bbcontains ‣bboverlaps ‣covers ‣crosses ‣overlaps ‣intersects ‣touches ‣within ‣…more!
  27. views.py from django.shortcuts import render_to_response! from django.template import RequestContext! from

    models import Post! ! def display_post(request, post_slug): ! post = Post.objects.get(slug=post_slug)! template_data = {'post': post}! template = "blog/post.html"! ! return render_to_response(! template,! template_data,! context_instance=RequestContext(request)! )
  28. views.py from django.shortcuts import render_to_response! from django.template import RequestContext! from

    models import Post! ! def display_post(request, post_slug): ! post = Post.objects.get(slug=post_slug)! template_data = {'post': post}! template = "blog/post.html"! ! return render_to_response(! template,! template_data,! context_instance=RequestContext(request)! )
  29. views.py from django.shortcuts import render_to_response! from django.template import RequestContext! from

    models import Post! ! def display_post(request, post_slug): ! post = Post.objects.get(slug=post_slug)! template_data = {'post': post}! template = "blog/post.html"! ! return render_to_response(! template,! template_data,! context_instance=RequestContext(request)! )
  30. views.py from django.shortcuts import render_to_response! from django.template import RequestContext! from

    models import Post! ! def display_post(request, post_slug): ! post = Post.objects.get(slug=post_slug)! template_data = {'post': post}! template = "blog/post.html"! ! return render_to_response(! template,! template_data,! context_instance=RequestContext(request)! )
  31. views.py from django.shortcuts import render_to_response! from django.template import RequestContext! from

    models import Post! ! def display_post(request, post_slug): ! post = Post.objects.get(slug=post_slug)! template_data = {'post': post}! template = "blog/post.html"! ! return render_to_response(! template,! template_data,! context_instance=RequestContext(request)! )
  32. views.py from django.shortcuts import render_to_response! from django.template import RequestContext! from

    models import Post! ! def display_post(request, post_slug): ! post = Post.objects.get(slug=post_slug)! template_data = {'post': post}! template = "blog/post.html"! ! return render_to_response(! template,! template_data,! context_instance=RequestContext(request)! )
  33. <!DOCTYPE html>! <html>! <head>! <title>! {% block title %}{% endblock

    %}! </title>! </head>! ! <body>! {% block content %}{% endblock %}! </body>! </html> A base.html template
  34. A base.html template <!DOCTYPE html>! <html>! <head>! <title>! {% block

    title %}{% endblock %}! </title>! </head>! ! <body>! {% block content %}{% endblock %}! </body>! </html>
  35. post.html {% extends "base.html" %}! ! {% block title %}!

    {{ post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %}
  36. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html
  37. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html
  38. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html
  39. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html
  40. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html
  41. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html
  42. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html
  43. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html template filters
  44. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html
  45. {% extends "base.html" %}! ! {% block title %}! {{

    post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} post.html template filter
  46. URLConf’s • Tie it all together! • Route HTTP requests

    to views • May also capture values
  47. Root URLConf: urls.py from django.conf.urls import patterns, include, url! !

    from django.contrib import admin! admin.autodiscover()! ! urlpatterns = patterns('',! url(r'^blog/(?P<post_slug>.+)/$',! 'blog.views.display_post',! name='display_post'),! url(r'^admin/', include(admin.site.urls)),! )
  48. from django.conf.urls import patterns, include, url! ! from django.contrib import

    admin! admin.autodiscover()! ! urlpatterns = patterns('',! url(r'^blog/(?P<post_slug>.+)/$',! 'blog.views.display_post',! name='display_post'),! url(r'^admin/', include(admin.site.urls)),! ) Root URLConf: urls.py
  49. from django.conf.urls import patterns, include, url! ! from django.contrib import

    admin! admin.autodiscover()! ! urlpatterns = patterns('',! url(r'^blog/(?P<post_slug>.+)/$',! 'blog.views.display_post',! name='display_post'),! url(r'^admin/', include(admin.site.urls)),! ) Root URLConf: urls.py
  50. from django.conf.urls import patterns, include, url! ! from django.contrib import

    admin! admin.autodiscover()! ! urlpatterns = patterns('',! url(r'^blog/(?P<post_slug>.+)/$',! 'blog.views.display_post',! name='display_post'),! url(r'^admin/', include(admin.site.urls)),! ) Root URLConf: urls.py
  51. from django.conf.urls import patterns, include, url! ! from django.contrib import

    admin! admin.autodiscover()! ! urlpatterns = patterns('',! url(r'^blog/(?P<post_slug>.+)/$',! 'blog.views.display_post',! name='display_post'),! url(r'^admin/', include(admin.site.urls)),! ) Root URLConf: urls.py
  52. from django.conf.urls import patterns, include, url! ! from django.contrib import

    admin! admin.autodiscover()! ! urlpatterns = patterns('',! url(r'^blog/(?P<post_slug>.+)/$',! 'blog.views.display_post',! name='display_post'),! url(r'^admin/', include(admin.site.urls)),! ) Root URLConf: urls.py
  53. An HTTP Request

  54. An HTTP Request

  55. An HTTP Request from django.conf.urls import patterns, include, url! !

    from django.contrib import admin! admin.autodiscover()! ! urlpatterns = patterns('',! url(r'^blog/(?P<post_slug>.+)/$',! 'blog.views.display_post',! name='display_post'),! url(r'^admin/', include(admin.site.urls)),! ) blog/sample-title/
  56. An HTTP Request from django.conf.urls import patterns, include, url! !

    from django.contrib import admin! admin.autodiscover()! ! urlpatterns = patterns('',! url(r'^blog/(?P<post_slug>.+)/$',! 'blog.views.display_post',! name='display_post'),! url(r'^admin/', include(admin.site.urls)),! ) blog/sample-title/
  57. An HTTP Request def display_post(request, post_slug): ! post = Post.objects.get(slug=post_slug)!

    template_data = {'post': post}! template = "blog/post.html"! ! return render_to_response(! template,! template_data,! context_instance=RequestContext(request)! ) sample-title
  58. An HTTP Request {% extends "base.html" %}! ! {% block

    title %}! {{ post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} sampleproject/blog/templates/blog/post.html Sample Title
  59. An HTTP Request {% extends "base.html" %}! ! {% block

    title %}! {{ post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} sampleproject/blog/templates/blog/post.html (35, 89)
  60. An HTTP Request {% extends "base.html" %}! ! {% block

    title %}! {{ post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} sampleproject/blog/templates/blog/post.html Lorem Ipsum...
  61. An HTTP Request {% extends "base.html" %}! ! {% block

    title %}! {{ post.title }}! {% endblock %}! ! {% block content %}! <h1>{{ post.title }}</h1>! <p>Published at {{ post.published_at }}</p>! ! {{ post.content|urlize|linebreaks }}! ! <p>Published on:! {{ post.published_on|date:"M d, Y" }}</p>! {% endblock %} sampleproject/blog/templates/blog/post.html Nov 3, 2012
  62. An HTTP Request

  63. GeoDjango is built on PostgreSQL PostGIS GEOS, GDAL, PROJ

  64. Want to Learn More? The Official Django Docs are great!

    https://docs.djangoproject.com
  65. Thanks! Questions? Brad Montgomery brad@bradmontgomery.net @bkmontgomery MEMpy 3rd Monday 6:30

    pm-8:30pm @MemphisPython http://mempy.org