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

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