I have a labs with samples. For each lab, I want to display the related samples as inlines. I want to cache queries for each specified field in a model in a generic way. For that I use the OptimizedField
class:
class LabAdmin(admin.ModelAdmin):
model = Lab
inlines = (SampleInline,)
class SampleInline(MyBaseModelAdmin, admin.TabularInline):
model = Sample
list_optimized_fields = (
OptimizedField('condition', SampleCondition),
)
def __init__(self, *args, **kwargs):
super(SampleInline, self).__init__(*args, **kwargs)
self.extra = 2
class OptimizedField(object):
def __init__(self, name, cls, cache=True):
super(OptimizedField, self).__init__(name, cls)
self.name = name
self.cls = cls
self.cache = cache
def choices(self):
return [ (o.pk, str(o)) for o in self.cls.objects.all() ]
Here for each Lab I display inlines for the Sample model, with two extra
inlines as templates (`self.extra = 2`). The Sample model looks like this:
class Sample(models.Model):
name = models.SlugField()
lab = models.ForeignKey(Lab)
condition = models.ForeignKey(
SampleCondition,
blank=True,
null=True)
condition
is a foreign key. A Lab page displays an inline for all the samples related to the lab, Each SampleInline has formfield with choices for all existing SampleCondition. This works, but produces duplicated queries. To cache the relevant queries, I overwrite the admin.options.BaseModelAdmin
class as follow:
class MyBaseModelAdmin(admin.options.BaseModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
formfield = super(MyBaseModelAdmin, self) \
.formfield_for_foreignkey(db_field, request, **kwargs)
return self.cache_formfield_field(formfield, db_field)
def cache_formfield_field(self, formfield, db_field):
cached_field = "cached_{}".format(db_field.name)
if hasattr(self, cached_field):
setattr(formfield, 'choices', getattr(self, cached_field))
return formfield
def get_formsets_with_inlines(self, request, obj=None, *args, **kwargs):
for inline in self.get_inline_instances(request, obj):
if hasattr(inline, 'list_optimized_fields'):
for field_to_cache in [ f for f in inline.list_optimized_fields if f.cache ]:
field_to_cache_attr = "cached_{}".format(field_to_cache.name)
if not hasattr(inline, field_to_cache_attr):
setattr(
inline,
field_to_cache_attr,
field_to_cache.choices,
)
yield inline.get_formset(request, obj), inline
First, in LabAdmin, for each inline, I set a field to cache with its associated choices (get_formsets_with_inlines
). Then for each foreign key used in each formfield (formfield_for_foreignkey
), I overwrite the formfield choices with the custom cache_formfield_field
method. This works and removes all my duplicated queries.
However, the Sample's condition field can be blank (blank=True
). As such in each Sampleinline, I want my formfield for the condition to display not only all possible instances of SampleCondition, but also a first choice that would represent the default blank choice ('', '-----'). So in the method OptimizedField()
, I have to find a way to add that blank choice only if the class model's field (here 'condition') is blank. Following that logic I have thought of a solution of the kind:
class OptimizedField(object):
...
def choices(self):
choices = [ (o.pk, str(o)) for o in self.cls.objects.all() ]
if getattr(self.cls, self.name).__dict__['blank']:
choices.extend([('', '-----'),]
return choices
But it appears the SampleCondition's condition attribute doesn't have the 'blank' key, plus it is probably farfetched. How can I do that? Is there even an easier way?