24 February, 2008

Quick Django Tip: Dynamic Application Object Retrieval

In my recent django adventures I needed to introduce site-wide search functionality and in the process of doing so, encountered a small roadblock towards doing so.  Apparently due to the nature of Django's API for db interaction (as of the last stable release version), there is a limitation as to the use of python variables in API calls.  I found this to be a hinderance, but only for so long.  

The follow code snippet was something I whipped together which by utilising Python's 'eval' built-in, overcame the aforementioned limitation regarding the API's ability to interpolate native Python (e.g. non-django explicit) varaibles.

Things to know to understand the following example:  

 search_input is a list of cleaned and pre-processed user-driven terms, split into separate expressions, (e.g. ["dynamic langauge", "agile", "programming", "paradigm"]). 

 search_schema is a dictionary in which the key is the django model/class through whose objects we are attempting to search, and the value is a list of specific model attributes to attempt said search.  (e.g.   User_Profile : ['firstname' , 'lastname', 'address_1', 'bio_info', 'favourite_books'])

container_xref is a simple alias mapping for the actual django application names to our internal references inside this search code base.  Obviously this whole bit could be written without said setup, but for readability given the scope of the actual application involved, and the fact that I was not searching simply a few static fields in one django application, but several dozen fields through about two dozen separate applications, this container_xref dict was appropriate.   It is through this mapping dict which we place any matched object results (so as to not waste any additional space via unnecessary list initialisations.) for eventual results generation.

Note: the key "total_results" in the container_xref was a simple means of keeping track of overall search matches, rather than relying upon the Django templating engine (view) from doing work responsible from the processing (controller) perspective.  In retrospect, there are better ways this could have been handled, and in future point revisions, this will be addressed.

------

for search_string in search_input:
  for application in search_schema.keys():
    for attribute in search_schema[application]:
code_to_eval = "%s.objects.filter( %s__icontains='%s' ).order_by('-id')" %       
                     (str(application), str(attribute), str(search_string))
        try:
          eval_results = eval(code_to_eval)
          for eval_result in eval_results:
            if eval_result not in container_xref[application]:
              container_xref[application].append(eval_result)
              container_xref['total_results'] += 1
        except Exception:
    ### Case specific exception handler types, assignments and resultant actions 
          ### specific to each application in which the above is implemented, go here.

-----

As can be seen from the above, simple inline substitution proceeded by evaluation of said string results in post-compilation dynamic search functionalities within django, addressing simple problems one might run into with the existing API which will most likely be addressed in future versions.  

Your results may vary.

Eric