Drupal in the Trenches: Fighting with Views

The other developers have bought into the idea that all behavior-related changes should be in the source code. It’s the only thing keeping us sane with four developers and three environments: local, testing, and production. It has its own challenges, though, like this one:

Problem: Blocks based on views with dependencies on custom tables sometimes don’t show up when we refreshed from the database dump, although things work if we update the database one revision at a time.

After far too much pain and suffering, I figured out that _views_get_default_views was caching the results. When it checked our newly-enabled modules for default views, it found some views whose dependencies hadn’t gotten enabled yet, so it didn’t cache those. The next time _views_get_default_views was called, it used the values it had stored in a static variable available only in that function.

_views_get_default_views did not provide a way to reset that static variable, so once it was called, the data was practically written in stone.

_views_get_tables is similarly evil. (ARGH!)

This might’ve been fixed in a Views update (and we’re still using Drupal 5, in any case), but I’m not going to suggest updating the module this close to external acceptance testing.

So our options were:

  • Make sure _block_rehash and similar functions never get called before the update function that enables the module. Problems: Not only do we need to untangle the order of update functions that get called and be hyper-aware of what other modules do, but we’ll also need to change old update functions if we ever need to do this for another view in the future.
  • Hack _views_get_default_views and _views_get_tables to accept an optional parameter that resets the static variable.

When there are no clean alternatives, you just gotta get your fingers dirty and hack code.

Was that really only one hour of my life? It felt so much longer.

  • http://angrydonuts.com merlinofchaos

    views_invalidate_cache() at the end of your update. Everything will be reread properly on the next page update.

  • http://rtfverterra.mathalino.com RTFVerterra

    Wow! the lord and creator of views himself is here. My site won’t live without views. :)

  • http://sachachua.com Sacha Chua

    merlinofchaos: Right, but not in time for other update functions that try to mess around with the blocks table to set visibility and things like that. What’s the canonical Drupal 5 way to set block visibility for views that provide blocks, in code? I think the developer tried inserting into the blocks table, but he reported problems with it.

  • http://angrydonuts.com merlinofchaos

    Ugh, block visibility. Since the delta of the view won’t change, being based on the view name, you could just write the block record directly instead of trying to modify it, I suppose. There’s nothing dynamic going on in there that I can think of, so just writing it the way you know it should be should work for that update.

    Also, possibly moving the module enables earlier? I realize that might not be feasible, sometimes order of things is important. It is *very* rare that the available default views is expected to change during a single pageload (first time in 3 years I’ve heard of anybody needing to, for example) so I guess I’m not surprised I never thought to put in a way to reset that static cache.

  • David Stosik

    Here is how I update my blocks table in my hook_update_N, 4 days ago:

    views_invalidate_cache(); // to be sure our last default views (defined in files) are loaded

    // change theme_key just during _block_rehash, so that blocks for the good theme are rehashed
    global $theme_key;
    $old_theme_key = $theme_key;
    $theme_key = ‘blog_rfi';

    // rebuild the blocks entries from blocks table for theme ‘blog_rfi’
    _block_rehash();

    // restore old theme_key (which is probably something like administration_theme, as we’re in hook_update_N)
    $theme_key = $old_theme_key;

    After this, I can do some UPDATE on blocks visibility, with WHERE clauses looking like “theme=’blogs_rfi AND module=’my_module’ AND delta=1″.

    Hope this will help. :)

    Regards,
    David

  • http://sachachua.com Sacha Chua

    That generally worked for us, until we had a case where we had two update functions that called _block_rehash, which causes _views_get_default_views to store the results in a static variable. Or maybe it’s just the version of Views we’re still using… (Ah, the Drupal 5 world).

  • http://sachachua.com Sacha Chua

    merlinofchaos: Yeah, I suggested using DELETE and INSERT instead of UPDATE, but the developer mentioned he had been having weird problems with that, too. I haven’t confirmed for myself how deep the rabbit hole goes, though.

    Three years, eh? <laugh> Maybe I really am weird for insisting that all behavior-related changes (blocks, views, anything that moves) need to be checked into the source code some way or another, which usually means writing install functions. This has been fantastic for smoothly deploying to production sites, but occasionally runs into assumptions in code.

    For example, I live in dreadful fear of running into an update function that uses #finished = 0, because I don’t know how to simulate a browser refresh and new instance of PHP (while still keeping session variables) in my Drush command-line tool for updating all the modules…

    By the way, thank you so much for your time and insight, and for the awesomeness that is Views!

  • David Stosik

    Sacha Chua: you just made me understand where really was your problem, and that the code I pasted won’t help… That was a tough one! ^^

    By the way, you’re not so weird: I have been thinking the same thing for long and started doing it for real some days ago. :D

    David

  • http://angrydonuts.com merlinofchaos

    It’s not weird to want to do this, it’s just that most of Drupal does not currently provide the tools necessary to do this. Views actually for the most part does; the problem you’re running into is that blocks don’t, and because they don’t, they run into something where *I* made an assumption that default views would never actually change during a page load.

    We still live in a world where sometimes you have to use drupal_execute(). This is unfortunate.

    As an aside, the captcha words for this comment disturb me.

  • http://sachachua.com Sacha Chua

    drupal_execute wouldn’t solve this particular views problem, I think, but it does come in handy for other things. =) (I think every module developer should write APIs first.)

    I wonder how to control the words for my reCAPTCHA…