Django no blank choices for inline formfield

时间:2018-06-04 16:47:51

标签: django django-models django-forms

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?

0 个答案:

没有答案