We can change the default behaviour of including posts attached to child categories by mapping the category_name
query var (set from pretty permalinks) to category__in
(which ignores child categories):
function wpse_184127_ignore_category_children( $wp_query ) {
if ( $wp_query->is_main_query() && $wp_query->is_category() && $name = $wp_query->get( 'category_name' ) ) {
if ( $term = get_term_by( 'slug', sanitize_title_for_query( $name ), 'category' ) ) {
if ( $term->parent )
$depth = count( get_ancestors( $term->term_id, 'category', 'taxonomy' ) );
else
$depth = 0;
if ( $depth <= 1 ) {
$wp_query->set( 'category__in', array( $term->term_id ) );
unset( $wp_query->query_vars['category_name'] );
}
}
}
}
add_action( 'pre_get_posts', 'wpse_184127_ignore_category_children' );
Update: Added depth checking. Just change $depth <= 1
to whichever expression you need. Currently it will only ignore children for top-level and first-level categories (i.e. depth less than or equal to 1).