Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GeoDjango: Can I use OSMGeoAdmin in an Inline in the User Admin?

Profile contains a PointField. I've used OSMGeoAdmin in the ProfileAdmin, here:

class ProfileAdmin(admin.OSMGeoAdmin):
    model = Profile

But can't figure out how to use it in an inline for display in the UserAdmin. I currently have this set up as below:

# User Admin, with Profile attached
class ProfileInline(admin.StackedInline):
    model = Profile
    can_delete = False
    verbose_name_plural = 'Profile'  # As only one is displayed in this view

class UserAdmin(UserAdmin):
    inlines = (
        ProfileInline,
    )

admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Is it possible to use class OSMGeoAdmin in this situation?

like image 857
StringsOnFire Avatar asked Aug 16 '15 16:08

StringsOnFire


People also ask

How do I add GeoDjango to my project?

GeoDjango is a framework that makes it as easy as possible to build GIS and location aware web applications. You can add it by simply including the gis contrib module in the list of installed apps.

Is GeoDjango good for creating location based web applications?

Congratulations on creating your location based web application using GeoDjango, which aims to become a world-class geographic framework for implementing GIS apps. You now have the basic skills that you can use to either add simple geolocation stuff to your applications or create GIS apps.

How do I create a shop model in GeoDjango?

You’ll create a Shop model that has the following fields: Open the shops/models.py file and add the following code: For the location, you are using the PointField, a GeoDjango-specific geometric field for storing a GEOS Point object that represents a pair of longitude and latitude coordinates.

How to migrate 4326 srid to PostGIS using Django?

The 4326 srid is the most popular system used with PostGIS. It’s also known as WGS84, where units are specified in degrees of longitude and latitude. You can refer to spatialreference.org for a Django-powered database of spatial reference systems. Next, add the migration class to execute the above function when you run the migrate command:


2 Answers

This would be a good feature to request I guess.

As a workaround, you can take advantage of the fact that an InlineModelAdmin is quite similar to a ModelAdmin. Both extend BaseModelAdmin.

Inheriting from both StackedInline and ModelAdmin should not clash too much.

The only issue is that both __init__() methods take 2 positional arguments and call super().__init__() without arguments. So whatever the inheritance order, it will fail with TypeError: __init__() missing 2 required positional arguments: 'parent_model' and 'admin_site'

Fortunately, the InlineModelAdmin.__init__() method, the one we are interested in, is not really verbose nor complex (not too much super().__init__() calls in cascade).

Here is what it looks like in Django 1.9:

def __init__(self, parent_model, admin_site):
    self.admin_site = admin_site
    self.parent_model = parent_model
    self.opts = self.model._meta
    self.has_registered_model = admin_site.is_registered(self.model)
    super(InlineModelAdmin, self).__init__()
    if self.verbose_name is None:
        self.verbose_name = self.model._meta.verbose_name
    if self.verbose_name_plural is None:
        self.verbose_name_plural = self.model._meta.verbose_name_plural

And here is what its parent (BaseModelAdmin) looks like in Django 1.9

def __init__(self):
    overrides = FORMFIELD_FOR_DBFIELD_DEFAULTS.copy()
    overrides.update(self.formfield_overrides)
    self.formfield_overrides = overrides

Now let's put it all together:

from django.contrib.admin.options import FORMFIELD_FOR_DBFIELD_DEFAULTS

# User Admin, with Profile attached
class ProfileInline(OSMGeoAdmin, admin.StackedInline):
    model = Profile
    can_delete = False
    verbose_name_plural = 'Profile'  # As only one is displayed in this view

    def __init__(self, parent_model, admin_site):
        self.admin_site = admin_site
        self.parent_model = parent_model
        self.opts = self.model._meta
        self.has_registered_model = admin_site.is_registered(self.model)
        overrides = FORMFIELD_FOR_DBFIELD_DEFAULTS.copy()
        overrides.update(self.formfield_overrides)
        self.formfield_overrides = overrides
        if self.verbose_name is None:
            self.verbose_name = self.model._meta.verbose_name
        if self.verbose_name_plural is None:
            self.verbose_name_plural = self.model._meta.verbose_name_plural

class UserAdmin(UserAdmin):
    inlines = (
        ProfileInline,
    )

admin.site.unregister(User)
admin.site.register(User, UserAdmin)

It's not really a satisfying solution as it requires to copy/paste some code from django, which may be different within the version of Django you use, and might be a pain to maintain when upgrading Django. However it should work until it is included in Django as a mix-in or as an InlineModelAdmin.

Note: the code snippets above are taken from Django 1.9, you should browse github tags to find the snippets corresponding to your version.

like image 199
Antoine Pinsard Avatar answered Oct 27 '22 15:10

Antoine Pinsard


Since Django admin fields use widgets, you can override the widget that's automatically set for a PointField using formfield_overrides. In this case, you can override all PointField instances to use the OSMWidget class like so:

from django.contrib.gis.forms.widgets import OSMWidget 

class ProfileInline(admin.StackedInline):
    model = Profile
    can_delete = False
    verbose_name_plural = 'Profile'  # As only one is displayed in this view
    formfield_overrides = {
        PointField: {"widget": OSMWidget},
    }
like image 43
kavdev Avatar answered Oct 27 '22 15:10

kavdev