I'm trying to show one of three different child model inlines for a parent model, based on a selected field value within the parent model. I have tried every solution that pops up from various google searches but nothing seems to work.
First a little background, I'm new to python and django but so far I feel that I have been a quick study. I'm attempting to build a web application to house information linked to various spatial locations. The geometry type (geom_type) for each location may be different (i.e., points, linestring, and polygons are possible). To capture this information I plan to create a parent model (Location) to house the name and geom_type (and possibly other metadata). The spatial data related to each Location would then be housed in three separate child models; one for each geom_type. When entering data I would like to create a new location and select the geom_type, which would then pull up the correct inline.
Now for the details:
Models
from django.contrib.gis.db import models
class Geometry(models.Model):
TYPE = (
('Point', 'Point'),
('Linestring', 'Linestring'),
('Polygon', 'Polygon'),
)
geom_type = models.CharField('Geometry Type', choices = TYPE, max_length = 30)
class Meta:
verbose_name = 'Geometry'
verbose_name_plural = 'Geometries'
def __str__(self):
return self.geom_type
class Location(models.Model):
name = models.CharField('Location Name', max_length = 50)
geom_type = models.ForeignKey(Geometry, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Point(models.Model):
name = models.OneToOneField(Location, on_delete=models.CASCADE)
geometry = models.PointField()
def __str__(self):
return self.name.name
class Linestring(models.Model):
name = models.OneToOneField(Location, on_delete=models.CASCADE)
geometry = models.LineStringField()
def __str__(self):
return self.name.name
class Polygon(models.Model):
name = models.OneToOneField(Location, on_delete=models.CASCADE)
geometry = models.PolygonField()
def __str__(self):
return self.name.name
Admin
from django.contrib.gis import admin
from leaflet.admin import LeafletGeoAdmin, LeafletGeoAdminMixin
from .models import Geometry, Location, Point, Linestring, Polygon
class GeometryAdmin(admin.ModelAdmin):
list_display = ('id', 'geom_type')
admin.site.register(Geometry, GeometryAdmin)
class PointInline(LeafletGeoAdminMixin, admin.StackedInline):
model = Point
class LinestringInline(LeafletGeoAdminMixin, admin.StackedInline):
model = Linestring
class PolygonInline(LeafletGeoAdminMixin, admin.StackedInline):
model = Polygon
class LocationAdmin(admin.ModelAdmin):
model = Location
list_display = ('id', 'name', 'geom_type')
inlines = [
PointInline,
LinestringInline,
PolygonInline
]
admin.site.register(Location, LocationAdmin)
All three inlines show up correctly with the code above as expected. However, when I try to incorporate the conditional logic with different variations of get_inlines
or get_inline_instances
it always just ends up displaying the inline associated with the final "else" statement.
My failed attempt
def get_inlines(self, request, obj: Location):
if obj.geom_type == 'Point':
return [PointInline]
elif obj.geom_type == 'Location':
return [LinestringInline]
elif obj.geom_type == 'Polygon':
return [PolygonInline]
else:
return []
I believe the problem occurs because conditional statements are not referencing the model field correctly. But I can't seem to stumble upon the correct way to achieve my expected outcome.
Use related_name
in model like below:
next_question = models.ForeignKey(Question, on_delete=models.CASCADE, null = True, blank = True, related_name='next_question', limit_choices_to={'is_active': True})
and then fk_name
Like the example below: Then try. Hope you can find a solution by yourself.
class Labels(admin.TabularInline):
model = Label
extra = 0
fk_name = "next_question"
Use admin.StackedInline
for OneToOne and admin.TabularInline
for ForeignKey.
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
Create separate admin for 'Geometry' and 'Location' if you stacked.