Content Index
- The Dependency Problem and the Global Environment
- What is a virtual environment in Python and what is it for
- In Summary
- Advantages of using virtual environments in Django, Flask, or FastAPI
- Difference between venv, virtualenv, and uv
- Classic Approach: Creating a virtual environment with pip and venv
- Prerequisites before creating your environment
- Install the virtualenv tool
- Create a virtual environment step by step
- Generated folder structure
- Activating and deactivating the virtual environment
- Practical exercise with venv
- Installing Django, Flask, or FastAPI inside the virtual environment
- Saving and restoring project dependencies
- ⚠️ The Limitations of requirements.txt and pip
- Architecture and Hardware Conflicts
- Circular Dependencies and Unresolvable Conflicts
- The New Standard: Environment and Dependency Management with uv
- Configuration Files: pyproject.toml and uv.lock
- Core Commands: pip vs uv
- Practical Migration to uv
- Optional: Creating the Virtual Environment via Visual Studio Code
- Optional: Selecting the Python Interpreter in Visual Studio Code
- Final tips to keep your environment clean and stable
- Frequently Asked Questions about Python Virtual Environments
- Conclusion
We are going to talk about virtual environments in Python; specifically our case of interest, which would be to create applications in Django 6, Flask, or FastAPI —or, in short, any project in Python—. Usually, before writing a single line of code, we create a virtual environment.
Virtual environments are simply a tool used to create a Python space isolated from the rest of the Operating System, and this way we can develop our apps with specific versions of a framework, Python, or its dependencies.
When developing in Python, there is a very common problem: each project requires different versions of the same packages, and it becomes chaotic.
The solution I discovered was to use virtual environments, a simple yet powerful tool that allows you to keep each project completely isolated.
In Python, virtual environments are development environments isolated from the operating system; therefore, the packages we install will not be installed in the operating system, but in the virtual environment. We can create as many virtual environments as we want —usually, we have one virtual environment per project— and with this, all the packages and dependencies needed for a Python project are managed independently.
Specifically, they are used at the time of software development with Python; it is very common to want to use multiple versions of the same package in different projects, and to avoid installing and uninstalling (since we cannot install two versions of a package in our OS or venv), we can create an environment with the specific goal of isolating these multiple versions of one or several packages. For this reason, in this space, you will only find the packages you are going to use to develop your Python applications, whether native or with a framework like Django 6 or Flask.
In short, with virtual environments, we can install a specific version of that package needed for a particular project or projects, as well as the rest of the dependencies a project may have.
The Dependency Problem and the Global Environment
When we install Django, you will see that we have a lot of dependencies. That is, the installation does not only include Django, but also other related packages. If you have already worked a bit with the Python ecosystem, you know what I mean.
For example, when installing Django 6, we will have three or four additional packages. This happens because Django, unlike micro-frameworks (such as Flask), already comes with many features developed (templates, authentication, etc.). In Flask, being a micro-framework that brings the minimum necessary, it did not even initially include the template manager (Jinja2), and it had to be installed as an additional dependency.
The main idea is this: we install Django and three or four more packages get installed.
The problem is that, in the real world, you are going to have many projects.
If we had to install Django globally (as happens with Python, a browser, or VS Code), you could only have a single version of Django installed on your operating system, unless you perform complex configurations.
What is a virtual environment in Python and what is it for
A virtual environment is basically an isolated space within your operating system where you can install Python packages without affecting the rest of the system.
This means you can have several projects with different versions of Django or any library without them interfering with each other.
In my case, I usually create a new environment for each project, especially when working with frameworks like Django or Flask, because that way I avoid dependency conflicts and can update or test versions without risk.
In Summary
Instead of installing packages globally to the operating system, a kind of "little box" (a virtual environment) is created, and we install everything right there.
- Global Operating System: If we only use the operating system, we can only have a single installation of a version.
- Virtual Environments: If we use virtual environments, we can have "n" virtual environments for all our projects.
This applies to everything you want to work on with Python (Django, FastAPI, Tkinter, Flask, etc.). All these frameworks have dependencies that can clash with each other, and that is why we cannot install them globally on the operating system. If we did, we would have problems (something we will see in a comparison when we start installing Django).
Advantages of using virtual environments in Django, Flask, or FastAPI
- They isolate your dependencies per project.
- They avoid conflicts between package versions.
- They allow you to easily share your requirements with other developers.
- They facilitate installing identical environments on different machines.
Difference between venv, virtualenv, and uv
venv: comes built into Python 3, so you don't need to install anything extra. It is the simplest option to get started.virtualenv: is an external package that offers more flexibility, especially if you use multiple Python versions. It was the standard tool for years.uv: is the new ecosystem standard. Written in Rust, it replacespip,pip-tools, andvirtualenvin a single tool. Its execution speed is between 10 and 100 times faster than traditional tools, and it natively solves the cross-platform issues thatpiphas carried for years. If you are starting a project today, I recommend going directly withuv.- I use
venvmost of the time in legacy projects, but for new projects, I have completely switched touv.
Classic Approach: Creating a virtual environment with pip and venv
This is the workflow that has worked for years and remains perfectly valid for simple projects or if your team hasn't migrated to uv yet.
Prerequisites before creating your environment
Before starting, make sure you have Python and pip correctly installed on your system.
Open your terminal (or CMD on Windows) and type:
$ python --version
$ pip --versionIf you get a version number, everything is in order. If not, you can download Python from python.org/downloads.
Install the virtualenv tool
If you prefer to use virtualenv instead of venv, install the package as follows:
$ pip install virtualenvThis command will download and install the tool needed to create virtual environments.
Create a virtual environment step by step
With this ready, we can create virtual environments. Using virtualenv:
$ virtualenv <myproject>Where <myproject> is the name of your virtual environment. The command above creates a folder with that name, and that folder is your virtual environment, ready to use.
You can also create the environment with the venv module that already comes built into Python 3 (without installing anything extra):
$ python3 -m venv <myproject>Generated folder structure
Once the environment is created, you will see something like this:
myproject/
│
├── Scripts/ (on Windows)
├── bin/ (on Linux/macOS)
├── Lib/
└── pyvenv.cfgActivating and deactivating the virtual environment
Inside the environment folder, you will find files named activate and deactivate that allow you to activate or deactivate the virtual environment.
The first thing we would want to do is activate our virtual environment:
On Windows:
$ myproject\Scripts\activateYou will notice that the environment name appears at the beginning of your command line prompt, for example:
(myproject) C:\Users\andre\Desktop>On Linux and macOS:
$ source myproject/bin/activateAnd if we want to deactivate the virtual environment, we simply run:
$ deactivateLet's recall what virtual environments are
Virtual environments are, so to speak, a little box isolated from the operating system.
Inside this box, we have all the packages and dependencies needed for a specific project.Regardless of the jargon we use —whether in Django or another framework—, each project or application (for example, an online store) is usually linked to a single virtual environment.
Why create a virtual environment per project
If you want to create another project in Django, the ideal approach is to create a new virtual environment.
This is because, most likely, those projects will work with different packages or versions.For example, in addition to Django, we usually install other add-ons taking advantage of the Python ecosystem.
Each project may require different libraries, so keeping environments separate guarantees that dependencies remain exclusive and prevents mixing configurations between projects.
Practical exercise with venv
For example, if we run the following command:
$ python3 -m venv vtasksThis will generate the virtual environment, which is nothing more than a folder with several files and subfolders. To activate it:
On macOS or Linux:
$ source vtasks/bin/activateOn Windows:
$ vtasks\Scripts\activateYour project folder (not the vtasks folder, which corresponds to the virtual environment) is the one I recommend you open in Visual Studio Code (or your preferred editor). From the VS Code terminal, remember to activate the virtual environment:
Installing Django, Flask, or FastAPI inside the virtual environment
Once the environment is activated, our terminal looks like this:

And with this, we can install the framework we need. For example:
Django:
$ pip install djangoFlask:
$ pip install flaskFastAPI:
$ pip install fastapi uvicorn
uvicornis an ASGI (Asynchronous Server Gateway Interface) HTTP web server that we can use across multiple asynchronous Python web frameworks, such as Django, Flask, and of course FastAPI. To install it alongside FastAPI, we can use a single command:$ pip install fastapi uvicorn
And that is all: we now have Django (or whichever framework you selected) installed in our virtual environment. For now, we only installed a package; we don't have a project yet, but with this package, we can create them.
If we run:
$ pip freezeYou will see all the packages that make up the installation; something like:
asgiref==3.11.0
Django==6.0
sqlparse==0.5.4
tzdata==2025.2As mentioned earlier, it is very common that when installing packages via pip, these packages also need other packages to function; these are installed automatically and are known as dependencies.
In the example above, the packages asgiref, sqlparse, and tzdata are dependencies of the Django package.
It is important to note that you will likely have slightly newer versions and not exactly the ones listed here (the version of each package is located after the ==).
The pip freeze command displays all packages installed at the virtual environment level (if active) or at the operating system level (if the virtual environment is inactive or simply not being used).
Saving and restoring project dependencies
One practice I always apply is saving the exact dependencies I use in each project.
To do so, I run:
$ pip freeze > requirements.txtThis generates a requirements.txt file with the full list of packages and their versions. To restore the environment on another machine or after recreating the venv, simply run:
$ pip install -r requirements.txtYou can also install specific versions directly using the == operator, for example:
$ pip install Django==6.0Therefore, you can easily create an environment for a specific project using virtual environments. As you can see, the huge advantage is that we can install specific versions of our packages.
At this point, we are ready and can start developing our applications in Flask or start developing our applications in Django.
⚠️ The Limitations of requirements.txt and pip
Although the classic pip + requirements.txt workflow works adequately for lightweight applications developed with Django, Flask, or FastAPI, it exhibits serious deficiencies in larger-scale projects or cross-platform developments.
To contextualize these limitations, we will use a project named Digital Brain as a practical example. This system acts as an AI agent orchestrator and integrates complex video processing tools requiring hardware-specific drivers, such as NVIDIA drivers.
Architecture and Hardware Conflicts
Running pip freeze generates an exact, rigid snapshot of the packages installed on the current machine. This includes dependencies compiled for a specific operating system or libraries tied to particular hardware (for instance, CUDA and NVIDIA packages on Linux).
If this file, generated in a Linux environment with an NVIDIA GPU, is replicated on another architecture (such as macOS with Apple Silicon chips), the installation process will fail. The package manager will attempt to resolve hardware dependencies that do not exist on the new platform.
Circular Dependencies and Unresolvable Conflicts
The requirements.txt file does not distinguish between a project's direct dependencies and its sub-dependencies. By freezing the entire environment with pinned versions, lockups and circular dependencies between packages can occur (such as with libraries like browser-use or FFmpeg).
To mitigate this in the old approach, the solution requires manually maintaining a requirements.in file with the primary dependencies and installing over it, or generating separate files like requirements-linux.txt and requirements-mac.txt. However, this manual maintenance is tedious and error-prone.
The New Standard: Environment and Dependency Management with uv
uv is a package and environment manager designed in Rust that replaces pip, pip-tools, and virtualenv in a single tool. Its execution speed is 10 to 100 times faster than traditional tools, and it offers a native cross-platform resolution system.
In short, uv does for Python what npm or composer do for JavaScript and PHP respectively: managing dependencies in a declarative, reproducible, and headache-free manner.
Configuration Files: pyproject.toml and uv.lock
uv replaces manual management of .in and .txt files with two auto-generated, structured files:
pyproject.toml: It is the equivalent ofrequirements.in(or files likepackage.jsonandcomposer.json). It contains only the direct dependencies of the project declared cleanly.uv.lock: It is the equivalent of the lock file (likepackage-lock.jsonorcomposer.lock). It records the exact resolution of all sub-dependencies, including security hashes and specific binary selections (wheels) for each platform (Linux, macOS, Windows).
Thanks to this structure, a single uv.lock file allows installing the correct versions on both a Linux environment with NVIDIA and a macOS environment, ignoring unsupported hardware dependencies in each case.
Core Commands: pip vs uv
The following table summarizes the operational differences between the traditional workflow and the modern workflow with uv:
| Action | Classic Workflow (pip / pip-tools) | Modern Workflow (uv) |
|---|---|---|
| Create virtual environment | python3 -m venv .venv | uv init (creates it automatically) |
| Add dependency | pip install <pkg> and manual editing | uv add <pkg> |
| Remove dependency | pip uninstall <pkg> | uv remove <pkg> |
| Synchronize environment | pip install -r requirements.txt | uv sync |
| Run commands | python script.py (with active environment) | uv run script.py |
| Export dependencies | pip freeze > requirements.txt | Automatic in uv.lock |
Practical Migration to uv
To migrate an existing project to the new scheme with uv, follow these steps:
- Remove the previous virtual environment (
.venv) and oldrequirements.txtfiles. Initialize the project with
uvto generate the base structure:uv initAdd the project's primary dependencies.
uvwill automatically updatepyproject.tomland resolve theuv.lockfile:uv add djangoOr if your project uses FastAPI:
uv add fastapi uvicornSynchronize the development environment on any machine. The command will detect the current operating system and download only compatible binaries:
uv sync
Once synchronization is complete, application commands are executed by prepending uv run, ensuring that the managed virtual environment is used transparently:
uv run python manage.py runserverOr for FastAPI:
uv run uvicorn main:app --reloadOptional: Creating the Virtual Environment via Visual Studio Code
This is the option I recommend if you prefer not to use the terminal. Create the virtual environment directly from a VS Code instance.
- Open Folder: Open a clean instance of VS Code (
Ctrl + Shift + N). Drag or open your project folder. - Create Environment: Press
Ctrl + Shift + P. - Search for:
Python: Create Environment. - Select the provider:
Venv(orVirtualenv). - Select your Python: The Python installed at your operating system level.
VS Code will do everything for you. The great advantage is that we don't have to deal with interpreter selection or recognition issues. Once created, open the terminal in VS Code: you will see the virtual environment name in parentheses, indicating it is active. If you run pip freeze, you will see absolutely nothing (a clean environment).
Optional: Selecting the Python Interpreter in Visual Studio Code
Most of the time VS Code will not automatically recognize the Python interpreter configured in the virtual environment, but will instead pick the one installed globally on the operating system. This brings the inconvenience of showing warnings in application imports:

And, therefore, it is not possible to inspect the details of imported modules. To solve this, we must tell VS Code that the interpreter it should use is the one from the virtual environment —where we have installed the full project ecosystem— and not the Operating System one.
Press key combination Command/Control + Shift + P and search for "Select Interpreter":

And select the one from your virtual environment from the "Enter interpreter path" option:

In case selecting the interpreter still shows the warning "Import X could not be resolved", you can try creating a project from scratch using VS Code: open a clean VS Code instance, press Command/Control + Shift + P, and search for "Create Environment":

Follow the editor prompts: select the environment, choose Venv:

Next, it might ask you to set the workspace:

Then return to step 1: Command/Control + Shift + P and search for "Create Environment". This time, it should ask for the interpreter you wish to use:

And with this, packages installed at the virtual environment level should be recognized.
Final tips to keep your environment clean and stable
- Create a virtual environment for each project.
- Avoid installing packages globally.
- Use
requirements.txt(withpip) orpyproject.toml(withuv) to manage versions. - If an environment gets corrupted or throws errors, delete it and recreate it: it's fast and safe!
- Keep Python updated to avoid incompatibilities.
- For new projects, consider adopting
uvright from the start: it will save you many headaches down the road.
In my experience, using virtual environments saves you from headaches and gives you full control over your projects in Django, Flask, FastAPI, or any other Python framework.
Frequently Asked Questions about Python Virtual Environments
- What is a Python virtual environment?
It is an isolated space where you can install dependencies without affecting the main system. - Why use a virtual environment with Django, Flask, or FastAPI?
Because these frameworks depend on many libraries whose versions can vary from project to project. - What is the difference between
venv,virtualenv, anduv?venvcomes with Python 3;virtualenvis an older external tool that is still useful;uvis the new standard, faster and with native cross-platform resolution. - How do I activate my virtual environment?
Withpip: on Windowsmyenv\Scripts\activate, on Linux/macOSsource myenv/bin/activate.
Withuv: you don't need to activate anything manually; useuv runand the environment is managed automatically. - How do I save my dependencies?
Withpip:pip freeze > requirements.txt.
Withuv: it is managed automatically inpyproject.tomlanduv.lock. - Should I migrate my project to
uv?
If your project has complex dependencies, works across multiple platforms, or you simply want to modernize your workflow, yes, it is definitely worth it. For simple projects,pipremains valid.
Conclusion
Creating a virtual environment in Python for Django, Flask, or FastAPI is not complicated, and once you understand its purpose, it becomes a natural part of your workflow.
The ecosystem has evolved: from virtualenv we moved to venv, and now uv positions itself as the new standard that unifies environment and dependency management into a single fast, modern, and cross-platform tool.
In my case, it was a turning point in how I organize my projects. I encourage you to try it yourself: in just a few minutes, you'll have your own environment ready to build any Python application.
The next step is to learn about what the Django framework is.