docs: def cache(self, checkvalidity=True, staleonly=False, allowraise=True):
          """ Re-caches the Symbol's datatable by querying each Feed. 
          
          Parameters
          ----------
          checkvalidity : bool, optional
              Optionally, check validity post-cache.  Improve speed by
              turning to False.
          staleonly : bool, default False
              Set to True, for speed up, by looking at staleness
          allowraise : bool, default True
              AND with the Symbol.handle and Feed.handle's 'raise',
              set to False, to do a list of symbols.  Note, this
              won't silence bugs in Trump, eg. unhandled edge cases.
              So, those still need to be handled by the application.
          
          Returns
          -------
          SymbolReport
          """
          note = "staleonly = {}".format(staleonly)
          self._log_an_event('CACHE','START',note)
  
          docache = True
          if staleonly:
              lc = self.last_cache()
              if lc:
                  freshthresh = self.freshthresh 
                  
                  nw = dt.datetime.now()
                  freshness = (nw - lc).total_seconds() / 60.0
  
                  if freshness <= freshthresh:
                      docache = False
          
          smrp = SymbolReport(self.name)
          if docache:
              data = []
              cols = ['final', 'override_feed000', 'failsafe_feed999']
      
              if len(self.feeds) == 0:
                  err_msg = "Symbol has no Feeds. Can't cache a feed-less Symbol."
                  raise Exception(err_msg)
      
              try:
                  datt = datadefs[self.dtype.datadef]
  
                  indtt = indexingtypes[self.index.indimp]
                  indkwargs = self.index.getkwargs()
                  indt = indtt(self.index.case, **indkwargs)
                  
                  rp = ReportPoint('datadef', 'class', datt)
                  smrp.add_reportpoint(rp)
                  
                  for afeed in self.feeds:
                      fdrp = afeed.cache(allowraise)
                      smrp.add_feedreport(fdrp)
                      tmp = datt(afeed.data).converted
                      tmp = indt.process_post_feed_cache(tmp)
                      data.append(tmp)
                      cols.append(afeed.data.name)
              except:
                  point = "caching"
                  smrp = self._generic_exception(point, smrp, allowraise)
                      
              try:
                  data = pd.concat(data, axis=1)
              except:
                  point = "concatenation"
                  smrp = self._generic_exception(point, smrp, allowraise)
              
              # We shouldn't need to do anything here, as the concatenation
              # should be smooth...
              
  #            preindlen = len(data)
  #
  #                     
  #            if preindlen > 0 : 
  #                #indt = indtt(data, self.index.case, indkwargs)
  #                #data = indt.final_dataframe()
  #                data = indt.process_post_concat(data)
  #
  #                postindlen = len(data)   
  #                if postindlen == 0 and preindlen > 0:
  #                    raise Exception("Indexing Implementer likely poorly designed")
  #            else:
  #                postindlen = 0
              
              
              def build_hi_df(which, colname):
                  
                  objs = object_session(self)
      
                  qry = objs.query(which.ind,
                                   func.max(which.dt_log).label('max_dt_log'))
                  
                  qry = qry.filter_by(symname = self.name)
                  
                  grb = qry.group_by(which.ind).subquery()
          
                  qry = objs.query(which)
                  ords = qry.join((grb, and_(which.ind == grb.c.ind,
                                             which.dt_log == grb.c.max_dt_log))).all()
              
                  if len(ords):
                      orind = [row.ind for row in ords]
                      orval = [row.val for row in ords]
                      ordf = indt.build_ordf(orind, orval, colname)
                  else:
                      ordf = pd.DataFrame(columns=[colname])
                  return ordf
              
              ordf = build_hi_df(Override, 'override_feed000')
              fsdf = build_hi_df(FailSafe, 'failsafe_feed999')
              
              orfsdf = pd.merge(ordf, fsdf, how='outer', left_index=True, right_index=True)
              data = pd.merge(orfsdf, data, how='outer', left_index=True, right_index=True)
              
              data = indt.process_post_orfs(data)
  
              try:
                  data = data.fillna(value=pd.np.nan)
                  data = data[sorted_feed_cols(data)]
                  data['final'] = FeedAggregator(self.agg_method).aggregate(data)
              except:
                  point = "aggregation"
                  smrp = self._generic_exception(point, smrp, allowraise)
      
      
              # SQLAQ There are several states to deal with at this point
              # A) the datatable exists but a feed has been added
              # B) the datatable doesn't exist and needs to be created
              # C) the datatable needs to be updated for more or less feeds
              # D) the datatable_exists flag is incorrect because all edge cases
              #    haven't been handled yet.
              #
              # My logic is that once Trump is more functional, I'll be able to
              # eliminate this hacky solution.  But, SQLAlchemy might have
              # a more elegant answer.  A check, of somekind prior to deletion?
      
              # if not self.datatable_exists:
              #     self._init_datatable() #older version of _init_datatable
              # delete(self.datatable).execute()
              # self._init_datatable() #older version of _init_datatable
      
              # Is this the best way to check?
              # if engine.dialect.has_table(session.connection(), self.name):
              #    delete(self.datatable).execute()
              self._refresh_datatable_schema()
              
              if len(data) > 0:
                  data.index.name = 'indx'
                  data = data.reset_index()
                  datarecords = data.to_dict(orient='records')
                  
                  objs = object_session(self)
                  objs.execute(self.datatable.insert(), datarecords)
                  objs.commit()
              
              
              if checkvalidity:
                  try:
                      isvalid, reports = self.check_validity(report=True)
                      for rep in reports:
                          smrp.add_reportpoint(rep)
                      if not isvalid:
                          raise Exception('{} is not valid'.format(self.name))
                  except:
                      point = "validity_check"
                      smrp = self._generic_exception(point, smrp, allowraise)
              self._log_an_event('CACHE','COMPLETE', "Fresh!")
          else:
              self._log_an_event('CACHE','FRESH', "Was still fresh")
          return smrp