Большинство жалоб вида «установил, а импорт не работает» сводятся к тому, что pip принадлежит другому Python, не тому, что запускает код. pip install берёт тот pip, который первым найдётся в PATH. python -m pip install использует интерпретатор, который вы только что назвали. Одна эта привычка убирает целое семейство путаницы, а python -m pip --version покажет, к какому интерпретатору он привязан.
Вторая привычка это одно виртуальное окружение на проект, созданное через python -m venv .venv. Активация это удобство, которое в основном правит PATH. Её можно пропустить и вызывать .venv/bin/python напрямую в Linux и macOS, и скриптам с редакторами так и следует делать.
Чего venv не даёт, так это воспроизводимой установки. Он отделяет пакеты от системы, но файл requirements с нестрогими версиями через месяц разрешится иначе. Для этого нужны закреплённые версии с хешами или что-то вроде Nix, которое закрепляет и сам интерпретатор. Стоит ли такая машинерия того в небольших проектах, вопрос для меня открытый. Чаще всего просто закрепляю версии.