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

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


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


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 attachedclass 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_pluralclass 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.