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 ofpost.php
, there is no main query – the$post
global is populated manually, usingget_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.