Python Virtual Environments for Django 6, Flask, or FastAPI with pip and uv: A Complete Guide

- Andrés Cruz - ES En español

Video thumbnail

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 replaces pip, pip-tools, and virtualenv in a single tool. Its execution speed is between 10 and 100 times faster than traditional tools, and it natively solves the cross-platform issues that pip has carried for years. If you are starting a project today, I recommend going directly with uv.
  • I use venv most of the time in legacy projects, but for new projects, I have completely switched to uv.

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 --version

If 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 virtualenv

This 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.cfg

Activating 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\activate

You 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/activate

And if we want to deactivate the virtual environment, we simply run:

$ deactivate

Let'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 vtasks

This 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/activate

On Windows:

$ vtasks\Scripts\activate

Your 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:

VSC Terminal

Installing Django, Flask, or FastAPI inside the virtual environment

Once the environment is activated, our terminal looks like this:

venv activated in terminal or CMD

And with this, we can install the framework we need. For example:

Django:

$ pip install django

Flask:

$ pip install flask

FastAPI:

$ pip install fastapi uvicorn

uvicorn is 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 freeze

You 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.2

As 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.txt

This 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.txt

You can also install specific versions directly using the == operator, for example:

$ pip install Django==6.0

Therefore, 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

Video thumbnail

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 of requirements.in (or files like package.json and composer.json). It contains only the direct dependencies of the project declared cleanly.
  • uv.lock: It is the equivalent of the lock file (like package-lock.json or composer.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:

ActionClassic Workflow (pip / pip-tools)Modern Workflow (uv)
Create virtual environmentpython3 -m venv .venvuv init (creates it automatically)
Add dependencypip install <pkg> and manual editinguv add <pkg>
Remove dependencypip uninstall <pkg>uv remove <pkg>
Synchronize environmentpip install -r requirements.txtuv sync
Run commandspython script.py (with active environment)uv run script.py
Export dependenciespip freeze > requirements.txtAutomatic in uv.lock

Practical Migration to uv

To migrate an existing project to the new scheme with uv, follow these steps:

  1. Remove the previous virtual environment (.venv) and old requirements.txt files.
  2. Initialize the project with uv to generate the base structure:

    uv init
  3. Add the project's primary dependencies. uv will automatically update pyproject.toml and resolve the uv.lock file:

    uv add django

    Or if your project uses FastAPI:

    uv add fastapi uvicorn
  4. Synchronize 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 runserver

Or for FastAPI:

uv run uvicorn main:app --reload

Optional: 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 (or Virtualenv).
  • 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:

Select interpreter

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":

Window to select interpreter with Command/Control + Shift + P

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

Select Python for interpreter

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":

Create virtual environment

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

Select venv

Next, it might ask you to set the workspace:

Select 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:

Select Python interpreter

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 (with pip) or pyproject.toml (with uv) 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 uv right 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, and uv?
    venv comes with Python 3; virtualenv is an older external tool that is still useful; uv is the new standard, faster and with native cross-platform resolution.
  • How do I activate my virtual environment?
    With pip: on Windows myenv\Scripts\activate, on Linux/macOS source myenv/bin/activate.
    With uv: you don't need to activate anything manually; use uv run and the environment is managed automatically.
  • How do I save my dependencies?
    With pip: pip freeze > requirements.txt.
    With uv: it is managed automatically in pyproject.toml and uv.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, pip remains 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.

Learn how to create virtual environments in Python using venv, pip, and uv. A step-by-step guide to isolating dependencies in Django 6, Flask, and FastAPI projects.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

I agree to receive announcements of interest about this Blog.