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

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.


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},
    }