Trouble with wp_reset_postdata() in Admin Panel

I believe the official stance is to not use WP_Query in the admin panel. There’a a trac ticket #18408 on this exact same issue. Here’s what Boone says:

The root issue is that wp_reset_postdata() is designed to reset globals to match the main query. But in the case of post.php, there is no main query – the $post global is populated manually, using get_post(), rather than through the normal $wp_query loop.

Because the post is populated manually in the admin panel you will not be able to reset it in a traditional fashion. If you print out the global $wp_query at this point you’ll also find that it’s empty.

The preferred method is to instead use get_posts() which doesn’t override the global $post object.


You could manually preserve the global $post object in a custom variable and manually reset it once you’re done with your loop. That being said, I do not know the overarching consequences with this method. It’s safer to use get_posts() when possible.

global $post;

$tmp_post = $post;
$posts_query = new WP_Query( $args );

if( $posts_query->have_posts() ) {
    
    while( $posts_query->have_posts() ) {
        $posts_query->the_post();
        the_title();
    }
    
    $post = $tmp_post;
}

Another solution may be, specifically in the admin panel, store the global $post in the global $wp_query object during loop_start. For example:

/**
 * Store the global $post object so we may reset it
 * 
 * @return void
 */
function wpse377565_admin_post_storage() {
    
    global $wp_query,
        $post;
    
    if( ! is_admin() ) {
        return;
    }
    
    $wp_query->post = $post;
    
}
add_action( 'loop_start', 'wpse377565_admin_post_storage' );

The wp_reset_postdata() function looks for $this->post in the global $wp_query object which is both empty and available in admin requests. We could use the above action hook to allow us to call wp_reset_postdata(). Again, I also do not know the overarching consequences of doing this, use with caution.