Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Add "shop" Table to Django Admin

In this video we will add our shop database table to our admin view and load some initial records.

At this point you should have most of your app set up along with a superuser account for Django authentication.

What you will do now is register your shop model in the Admin interface. To do so you will subclass a GeoDjango class called OSMGeoAdmin and specifying the list of columns to display for the view.

00:00 Welcome to video number nine in our series on creating a location-based web app with Django and GeoDjango. In this video, we will add our shop database table to our admin view and load some initial records. At this point, you should have most of your app set up, along with a superuser account for Django authentication.

00:21 What you will do now is register your shop model in the admin interface. To do so, you will subclass a GeoDjango class by the name of OSMGeoAdmin, and specify the list of columns to display for the view.

00:37 Back in our editor, you’re going to modify a different file this time—admin.py, located in the app part of your project. This file provides a way for you to customize the built-in admin pages.

00:49 Add the following code to this file.

00:56 If your site is still running, stop it by pressing Control + C at the terminal. Then relaunch your site with the manage.py runserver command.

01:06 Go back into your site and then go to the admin view by typing /admin after the URL in your address bar. You see a new link called Shops.

01:15 Django even capitalized the table name and made it plural for consistency. Let’s look inside. Not surprisingly, we have no shop data yet, but as you can see, the interface has provided a way for us to manually add records if we wanted.

01:29 Instead of doing that by hand, though, you’re going to load your database with some initial data courtesy of the overpass-turbo.eu website. Open this address in your browser.

01:43 Use the Wizard option on this site and type the following query: shop in Baltimore. Build and run the query.

01:59 Once the query completes, choose the Export option at the top of the page, and then click the download link next to raw OSM data.

02:08 This downloads a file to your downloads area called export.json. We want to place export.json in the root of your project—at the same level as the manage.py utility.

02:19 You can take a look inside the file if you’re curious about its structure. But what we’re going to do next is create a Django migration to move this data into our database. To do that, we’re going to execute a manage.py command to initialize an empty migration for our nearbyshops app.

02:37 First, we’ll stop our site by pressing Control + C, and then clean up our view a little. Notice the migrations/ folder falls within the app part of our project, within the nearby shops/ folder. If you expand this, you’ll see artifacts from our previous migration.

02:56 Open your terminal window now and proceed with the --empty migration command.

03:07 If you refresh your migrations/ folder now, you’ll see an empty migration file has been created. You are going to make significant changes to this file.

03:16 You’ll need to add three import statements for some functionality you’ll need. Next, you’ll create a variable to hold the name of your JSON file.

03:28 Then—and this is the lengthy part—you’ll need to complete the code for the following load_data() function. The important parts to note here in the function is the name of your app, 'nearbyshops', and the model name that will be needed, 'Shop'.

03:47 We have one more change to make in the Migration class. Add the load_data function to the operations list.

03:58 Our migration is ready to go. We just have the final step now of running the manage.py migrate command.

04:07 Everything looks good! Let’s relaunch our site and see if we see any shops.

04:20 We’ll go back into the admin view and click the Shops link.

04:26 Great job! You should now see your initial location data. Before we leave this admin view, let’s take a look at what the ADD SHOP button does for us.

04:39 Neat! The OSMGeoAdmin class includes a map view—even though the location is a little off. You should be very pleased with how much you’ve accomplished so far.

04:49 We’re on the home stretch! To wrap up this project, we’re finally going to show our user which shops are nearby—in our next video.

Avatar image for abairy

abairy on June 4, 2019

Hello When I try to migrate the data exported from overpass its giving me this error

File “C:\Users\13154\Anaconda3\lib\encodings\cp1252.py”, line 23, in decode return codecs.charmap_decode(input,self.errors,decoding_table)[0] UnicodeDecodeError: ‘charmap’ codec can’t decode byte 0x9d in position 26427: character maps to <undefined>

Avatar image for snandw

snandw on June 22, 2019

please help I am getting the same error on a windows machine ,a quick reply would be appreciated i need to submit this as a project

Avatar image for Jackie Wilson

Jackie Wilson RP Team on June 24, 2019

I am traveling with limited access. Do you mean to say your map data (location data in JSON) is causing the error?

Avatar image for drewewhite1

drewewhite1 on Feb. 9, 2020

@abairy and @snandw, you may have solved your problem already. If not or for anyone who runs into this issue in the future, I was able to resolve the issue by specifying the encoding upon opening the file. So it looks something like this.

...

with open(str(jsonfile), encoding = "utf8") as datafile:
        objects = json.load(datafile)
        for obj....

Hope this helps!

Avatar image for Amr

Amr on Feb. 17, 2020

how to to use file.xlsx instead of file.json? and also how to add any type of file.

Avatar image for Mydhe

Mydhe on April 7, 2020

Just to add, the edit is done in your empty migration file for example mine is named 0002_auto_20200408_0157.py The edit is in this block;

with open(str**(jsonfile), encoding = "utf8") as **datafile:
    objects = json.load(datafile)
    for obj in objects['elements']:
        try:
            objType = obj['type']
            if objType == 'node':
                tags = obj['tags']
                name = tags.get('name','no-name')
                longitude = obj.get('lon', 0)
                latitude = obj.get('lat', 0)
                location = fromstr(f'POINT({longitude} {latitude})', srid=4326)
                Shop(name=name, location = location).save()
        except KeyError:
            pass

Thanks drewewhite1 for the pointer!

Avatar image for jacob6

jacob6 on May 4, 2020

My Shops table is still empty…no error messages. This is my code:

# Generated by Django 3.0.5 on 2020-05-04 08:55

from django.db import migrations
import json
from django.contrib.gis.geos import fromstr
from pathlib import Path

DATA_FILENAME = 'export.json'

def load_data(apps, schema_editor):
    Shop = apps.get_model('nearbyshops', 'Shop')
    jsonfile = Path(__file__).parents[2] / DATA_FILENAME

    with open(str(jsonfile)) as datafile:
        objects = json.load(datafile)
        for obj in objects['elements']:
            try:
                objType = obj['type']
                if objType == 'node':
                    tags = obj['tags']
                    name = tags.get('name', 'no-name')
                    longitude = obj.get('lon', 0)
                    latitude = obj.get('lat', 0)
                    location = fromstr(
                        f'POINT({longitude} {latitude})', srid=4326
                    )
                    Shop(name=name, location=location).save()
            except KeyError:
                pass

class Migration(migrations.Migration):

    dependencies = [
        ('nearbyshops', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(load_data)
    ]
Avatar image for jacob6

jacob6 on May 5, 2020

I’ve been over my code a few times now. I’m now getting the following error message:

TypeError: Shop() got an unexpected keyword argument 'location'

My code is as follows:

# Generated by Django 3.0.5 on 2020-05-04 10:00

from django.db import migrations
import json
from django.contrib.gis.geos import fromstr
from pathlib import Path

DATA_FILENAME = 'export.json'

def load_data(apps, schema_editor):
    Shop = apps.get_model('nearbyshops', 'Shop')
    jsonfile = Path(__file__).parents[2] / DATA_FILENAME

    with open(str(jsonfile)) as datafile:
        objects = json.load(datafile)
        for obj in objects['elements']:
            try:
                objType = obj['type']
                if objType == 'node':
                    tags = obj['tags']
                    name = tags.get('name', 'no-name')
                    longitude = obj.get('lon', 0)
                    latitude = obj.get('lat', 0)
                    location = fromstr(
                        f'POINT({longitude} {latitude})', srid=4326
                    )
                    Shop(name=name, location=location).save()
            except KeyError:
                pass

class Migration(migrations.Migration):

    dependencies = [
        ('nearbyshops', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(load_data)
    ]

As per the instructions in the video the export.json is on the same level as manage.py. I’ve been over this code a lot of times now, and I’ve tried googling, with no success…can anyone help out?

Avatar image for pmannion95

pmannion95 on July 23, 2020

Hi, I’m running into an odd error that I believe is related the map data but I’ve follow the instructions in the video for querying shops in Baltimore and downloading as raw OSM data.

django.contrib.gis.geos.error.GEOSException: Error encountered checking Geometry returned from GEOS C function "GEOSWKTReader_read_r".

My migration code is below, but otherwise I’m unsure of what may be the issue. I’ve retraced it, and redownloaded the export.json data to no avail.

My code is below: ``` # Generated by Django 3.0.8 on 2020-07-21 05:08

from django.db import migrations import json from django.contrib.gis.geos import fromstr from pathlib import Path

DATA_FILENAME = ‘export.json’

def load_data(apps, schema_editor): Shop = apps.get_model(‘nearbyshops’, ‘Shop’) jsonfile = Path(file).parents[2] / DATA_FILENAME

with open(str(jsonfile)) as datafile:
    objects = json.load(datafile)

    for obj in objects['elements']:
        try:
            objType = obj['type']
            if objType == 'node':
                tags = obj['tags']
                name = tags.get('name', 'no-name')
                longitude = obj.get('lon', 0)
                latitude = obj.get('lat', 0)
                location = fromstr(f'POINT({longitude} {latitude}', srid=4326)

                Shop(name=name, location=location).save()
        except KeyError:
            pass

class Migration(migrations.Migration):

dependencies = [
    ('nearbyshops', '0001_initial'),
]

operations = [
    migrations.RunPython(load_data)
]

```

Avatar image for karakus

karakus on Sept. 19, 2020

FYI - After spending some ‘extended happy time’ googling and checking my installs and code, I discovered that attempting to view the location in /Admin using OSMGeoAdmin doesn’t display the map correctly if using Safari, but works using Chrome. Hope this helps others!

See FAQ here for the source of my new found wisdom.

Enjoying the style and pace of the Tutorial all the same!

Avatar image for mkpannah

mkpannah on March 1, 2021

from django.db import migrations
import json
from django.contrib.gis.geos import fromstr
from pathlib import Path

DATA_FILENAME = 'export.json'

def load_data(apps, schema_editor):
    Shop = apps.get_model('nearbyshops', 'Shop')
    jsonfile = Path(__file__).parents[2] / DATA_FILENAME

    with open(str(jsonfile)) as datafile:
        objects = json.load(datafile)
        for obj in objects['elements']:
            try:
                objType = obj['type']
                if objType == 'node':
                    tags = obj['tags']
                    name = tags.get('name', 'no-name')
                    longitude = obj.get('lon', 0)
                    latitude = obj.get('lat', 0)
                    location = fromstr(
                        f'POINT({longitude} {latitude})', srid=4326
                    )
                    Shop(name=name, location=location).save()
            except KeyError:
                pass

class Migration(migrations.Migration):

    dependencies = [
        ('nearbyshops', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(load_data)
    ]

Please help me out here. My shops are not loading on my django site. Don’t know what happening.

Avatar image for Joe Madaus

Joe Madaus on Nov. 2, 2021

Not sure this was anyone’s issue, but be sure your syntax is correct. I found an error in my f-string, which was:

location = fromstr(
    f'POINT({longitude}, {latitude}', srid=4326

I was shocked this didn’t pick up. Explanation why I was getting a GEOSException versus a syntax error?

The correct syntax is:

location = fromstr(
    f'POINT({longitude} {latitude})', srid=4326
)

One little closing parenthesis :)

Avatar image for Martin Breuss

Martin Breuss RP Team on Nov. 2, 2021

Oh the joys (and pains of missing parentheses! (we’ve (probably all been there…

) 😜

Thanks for the heads-up to check your syntax @ Joe Madaus! And to reduce the amount of times you run into annoying stuff like that, you can use a Python IDE or code editor that helps you by highlighting issues with your syntax right when you’re writing your code.

Avatar image for Joe Madaus

Joe Madaus on Nov. 3, 2021

Hi Martin

I do use PyCharm which is why I was perplexed. It was late though so it’s possible PyCharm was teling me but I didn’t see it. Really good tutorial. We should expand this to be more user interactive.

Avatar image for Martin Breuss

Martin Breuss RP Team on Nov. 3, 2021

Ah yes I also miss things like this every so often, despite all the IDE support! I’ll more quickly spot the mistake after it’s gone wrong though with the text editor highlighting :)

What do you mean about expanding it to be more user-interactive @Joe Madaus?

Avatar image for Joe Madaus

Joe Madaus on Nov. 4, 2021

Hello again Martin. First, thanks for listening to feedback.

What I mean is a web form with users entering points on a map, those points autofilling a field. Then give the user the ability to input information about them. Kind of like a blog or something. Like a “places I’ve been” sort of thing with user “memories” they can share or not. Then rank those against “other user places” should the user choose to do so. Something like that.

Avatar image for Martin Breuss

Martin Breuss RP Team on Nov. 4, 2021

That’s a great addition to this project @Joe Madaus! 🙌 🗺

For implementing this, you’ll need a way for your users to submit content to your Django back end. Here are a couple of links that might help you piece that functionality together:

You’ll have to pick out the relevant content, but if you go through this process and keep working on it, you’ll definitely learn a lot about Django and you’ll have a cool project to show at the end! :D Let me know if you work it out, I’ll come and record some places there!

Avatar image for Joe Madaus

Joe Madaus on Nov. 4, 2021

Martin

Sounds awesome and thanks for the tips!!! I will definitely let you know when i finish it.

Avatar image for kimstar619

kimstar619 on Aug. 17, 2023

Hello there, When I run makemigrations it says ‘No changes detected’, any ideas? export.json is in the same place as shown in video. I’m on a Mac M1 running Ventura 13.5

thanks, Kim

Avatar image for kimstar619

kimstar619 on Aug. 17, 2023

Nevermind, figured it out, just a stupid mistake....

Avatar image for Rafael Tenorio Gama

Rafael Tenorio Gama on April 24, 2024

I try everything to solve the problem with django.contrib.gis.admin import OSMGeoAdmin but doesn’t work at all!! I have follow all the steps and instructions on gdal.org/download.html but nothing solves too.

We need a update from that lesson on Real Python.

My error is: when run python manage.py runserver

ImportError: cannot import name ‘OSMGeoAdmin’ from ‘django.contrib.gis.admin’.

Avatar image for gabriel

gabriel on May 13, 2024

@Rafael Tenorio Gama I got stuck on the same part but got it to work using the following in my admin.py file. However, i haven’t figured out how to make the list_display option work yet.

from django.contrib import admin
from django.contrib.gis.admin import GISModelAdmin
from .models import Shop

admin.site.register(Shop, GISModelAdmin)

I found this at the very bottom of the following django doc. GeoDjango Tutorial Hope this helps.

Avatar image for jwil

jwil on Sept. 8, 2024

@gabriel and @Rafael Tenorio Gama I was able to get it working with the following in admin.py

from django.contrib.gis import admin
from django.contrib.gis.admin import GISModelAdmin
from .models import Shop

@admin.register(Shop)
class ShopAdmin(GISModelAdmin):
    list_display = ('name', 'location')

Also, I found GISModelAdmin by discovering this: django.readthedocs.io/en/4.2.x/ref/contrib/gis/admin.html#osmgeoadmin

Avatar image for Thierry Gagné

Thierry Gagné on Oct. 2, 2024

Just want to say thanks to gabriel and jwil above me: I had the same problem as them and using GISModelAdmin instead of OSMGeoAdmin solved the issue. It is a shame RealPython does not want to update the Contents tab with these solutions.

Become a Member to join the conversation.