Beginner 5 min · March 05, 2026

Python Virtual Environments — Django 1.11 & 4.0 Conflict

One server running Django 1.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
Quick Answer
  • A virtual environment is an isolated folder with its own Python binary and packages — nothing affects the global Python installation.
  • Create with python -m venv .venv; activate with source .venv/bin/activate (Mac/Linux) or .venv\Scripts\activate (Windows).
  • When active, your terminal prompt shows (.venv) — that's your visual confirmation isolation is working.
  • pip freeze > requirements.txt captures exact package versions; pip install -r requirements.txt recreates the environment anywhere.
  • Performance insight: Virtual environments add zero runtime overhead — they just change which Python binary your terminal uses.
  • Production failure: committing the .venv folder to Git causes repository bloat (100+ MB) and broken setups across operating systems.
  • Biggest mistake: forgetting to activate before running pip install — packages go into the global environment and your project fails on another machine.

Every Python developer hits the same wall eventually: you install a package for one project, and suddenly another project breaks. Or your colleague sends you code that works on their machine but explodes on yours with a cryptic version error. These aren't rare edge cases — they're the everyday reality of working with Python, and they happen because Python installs packages globally by default, meaning every project on your computer shares the same pool of libraries.

Virtual environments solve this by giving each project its own isolated bubble. Inside that bubble, you can install exactly the packages — and exactly the versions — that project needs, without touching anything else. Delete the bubble when the project is done and your computer is as clean as when you started.

By the end of this article you'll know how to create a virtual environment from scratch, activate and deactivate it, install packages inside it, save a record of those packages so teammates can replicate your setup exactly, and spot the traps that catch beginners off guard. No prior experience needed — we'll build everything step by step.

Why Global Package Installation Is a Ticking Time Bomb

When you first install Python and run pip install requests, that library lands in a single global location on your computer. Every Python script you ever write shares that same copy. Sounds convenient — until it isn't.

Picture this: your first project uses requests version 2.20. Six months later you start a new project that needs requests version 2.31 because it uses a feature that didn't exist before. You upgrade globally. Your old project breaks. You downgrade to fix the old project. The new project breaks. You're stuck in a loop with no clean way out.

This exact scenario has a name in software: dependency conflict. It's not hypothetical — it's the single most common source of pain for Python beginners and professionals alike.

Virtual environments break this cycle permanently. Each environment is a self-contained folder that holds its own Python interpreter and its own set of packages. Project A's requests 2.20 and Project B's requests 2.31 can coexist peacefully on the same machine because they live in completely different folders and never meet.

Here's a subtle but important detail: virtual environments don't copy the entire Python installation. They create symlinks (Mac/Linux) or shortcuts (Windows) to the Python binary and then add their own site-packages folder. This keeps environments lightweight — typically 10-20 MB plus whatever packages you install.

Creating and Activating Your First Virtual Environment

Python ships with a built-in module called venv (short for virtual environment). You don't need to install anything — it's already there, waiting. You create a virtual environment with a single terminal command, and from that moment on, that folder is your project's private kitchen.

The general pattern is: python -m venv <name-of-environment>. The name is just a folder name — most developers use venv or .venv (with a dot prefix so the folder is hidden by default on Mac/Linux).

Once created, you need to activate it. Activating tells your terminal 'for every command I run from now on, use the Python and pip inside this folder, not the global ones'. The activation command is slightly different depending on your operating system, but the effect is identical.

After activation your terminal prompt usually changes to show the environment name in parentheses — that's your visual confirmation that you're inside the bubble. When you're done working, deactivate drops you back to the global environment.

What's actually happening when you activate? The activation script modifies your shell's PATH environment variable, adding the .venv/bin directory to the front. Since shells search PATH in order, python now resolves to .venv/bin/python first. The VIRTUAL_ENV environment variable is also set so tools can detect which environment is active. The deactivate command restores the original PATH.

Freezing Dependencies So Your Team Gets Identical Setups

Creating a virtual environment is half the job. The other half is making sure anyone else — a teammate, your future self on a new laptop, a deployment server — can recreate that exact same environment instantly.

That's what requirements.txt is for. It's a plain text file that lists every package your project depends on, along with the exact version numbers. Think of it as a recipe card: instead of 'bring the ingredients', you hand someone the full recipe and they produce the identical dish.

You generate it with one command: pip freeze > requirements.txt. The pip freeze part lists all installed packages with their versions; the > requirements.txt part saves that list into a file. To recreate the environment from that file, anyone runs pip install -r requirements.txt.

This is not optional polish — it's professional practice. Every serious Python project has a requirements.txt (or its more advanced cousin pyproject.toml). Without it, your project only works on your machine, which is the definition of a broken workflow.

A subtle but important detail: pip freeze includes transitive dependencies — packages that your direct dependencies depend on. That's intentional — you want the entire dependency graph locked, not just what you explicitly installed. If you want only your top-level dependencies, consider using pip-tools or poetry, but for most projects, pip freeze is sufficient.

Another best practice: maintain two requirements files — requirements.txt for production dependencies and requirements-dev.txt for development tools (pytest, black, mypy, pre-commit). Your production server doesn't need testing frameworks.

A Real-World Mini Project Tying It All Together

Theory is fine, but let's build something real inside a virtual environment so the whole workflow clicks. We'll write a small script that fetches the current weather for a city using the requests library — a package we install inside a virtual environment, not globally.

This example shows the complete developer workflow you'll repeat on every Python project you ever build: create environment → activate → install dependencies → write code → freeze requirements → deactivate. Once this muscle memory kicks in, you'll do it automatically.

Pay attention to the Python script itself too. It runs perfectly inside the virtual environment because requests is installed there. If you deactivate the environment and try to run the same script with the global Python, it would fail with ModuleNotFoundError: No module named 'requests' — unless you also happen to have it globally. That's virtual environment isolation working exactly as intended.

The script uses a free weather API (Open-Meteo) that requires no API key — perfect for learning. It demonstrates error handling for network issues and API errors, showing real production considerations even in a demo.

Global vs Virtual Environment: What Changes
AspectNo Virtual Environment (Global)With Virtual Environment
Package storage locationOne shared system folder for all projectsIsolated folder per project — no sharing
Risk of version conflictsHigh — upgrading for one project breaks anotherZero — each project has its own versions
Replicating setup on another machineManual, error-prone, undocumentedOne command: pip install -r requirements.txt
Cleaning up after project endsHunt down and uninstall packages manually, risk breaking other projectsDelete the .venv folder — done completely
Working on two projects simultaneouslyOnly one version of any package at a timeDifferent versions side-by-side, no conflict
CI/CD and deployment pipelinesFragile — depends on server's global stateReliable — environment is fully specified
Disk space usageShared — packages installed once for all projectsHigher — each project has its own copy of dependencies

Key Takeaways

  • Virtual environments isolate project dependencies completely — no more version conflicts between projects.
  • Create with python -m venv .venv. Activate with source .venv/bin/activate (Mac/Linux) or .venv\Scripts\activate (Windows).
  • Your terminal prompt shows (.venv) when active — never ignore this visual cue.
  • pip freeze > requirements.txt captures exact versions. pip install -r requirements.txt recreates the environment anywhere.
  • Never commit .venv to Git — add it to .gitignore. Commit requirements.txt instead.
  • Two requirements files: requirements.txt for production, requirements-dev.txt for development tools.
  • Always activate before running pip install — otherwise packages go into global Python and your project breaks on other machines.
  • Production servers need virtual environments too — one per application, even on the same machine.
  • Use which python to verify your environment is active — it should point inside .venv/bin/python.
  • Virtual environments add zero runtime overhead — they only change which binary your shell finds first.

Common Mistakes to Avoid

  • Forgetting to activate the environment before installing packages
    Symptom: You run `pip install requests` and it installs globally into `/usr/local/lib/python3.x/site-packages` instead of into your project. A teammate clones your repo, runs `pip install -r requirements.txt`, and gets `ModuleNotFoundError` because the requirements file is empty (since you never froze it from the right environment).
    Fix: Always check your terminal prompt for (.venv) before running any pip command. If it's missing, run source .venv/bin/activate (Mac/Linux) or .venv\Scripts\activate (Windows) first. After activation, run which python to confirm it points to your project folder.
  • Committing the .venv folder to Git
    Symptom: Your repository balloons to hundreds of megabytes. Cloning takes ages. Teammates on different operating systems (Windows vs Mac vs Linux) get broken environments because the compiled binaries inside `.venv` are platform-specific and incompatible.
    Fix: Create a .gitignore file in your project root and add .venv/ to it immediately after creating the environment. Only ever commit requirements.txt, never the folder itself. If you've already committed it, remove it with git rm -r --cached .venv and add it to .gitignore.
  • Running `pip freeze > requirements.txt` with test packages installed
    Symptom: Your `requirements.txt` includes development tools like `pytest`, `black`, `mypy`, and `pre-commit` that your production server doesn't need. This causes slower deployments, larger Docker images (800 MB instead of 200 MB), and potential security bloat.
    Fix: Maintain two files — requirements.txt for production dependencies and requirements-dev.txt for development tools. Generate them separately. In production, only install requirements.txt. On your local machine, install both: pip install -r requirements.txt -r requirements-dev.txt. Use pip freeze | grep -E "pytest|black|mypy" > requirements-dev.txt to extract dev dependencies.
  • Using different Python versions in environment than production
    Symptom: You create a virtual environment with `python -m venv .venv` where `python` points to Python 3.12, but your production server runs Python 3.10. Your code works locally but crashes in production with syntax errors or missing standard library features.
    Fix: Always create your virtual environment with the same Python version you'll use in production. Use python3.10 -m venv .venv to specify the exact version. Consider using pyenv to manage multiple Python versions locally. In CI, test against the same Python version as production.
  • Running activation script without `source` (Mac/Linux)
    Symptom: You run `.venv/bin/activate` (without `source`) and see no error, but `which python` still shows the global Python path. The prompt doesn't show `(.venv)`. No error message appears, making the mistake hard to detect.
    Fix: Always use source .venv/bin/activate (Mac/Linux) or . .venv/bin/activate (sh). The source command runs the script in the current shell, not a subshell, so its environment changes persist. Without source, the script runs in a child process that exits immediately, leaving your shell unchanged.

Interview Questions on This Topic

  • QWhy would you use a virtual environment instead of just installing packages globally? Can you describe a real scenario where not using one caused a problem?JuniorReveal
    Virtual environments isolate dependencies per project, preventing version conflicts. Real scenario: Project A uses Django 1.11, Project B uses Django 4.0. If both share the global Python, upgrading to Django 4.0 for Project B breaks Project A because Django 4.0 removed APIs that Project A relied on. Virtual environments let each project have its own Django version — no conflict. Another scenario: deploying two apps to the same server without venvs — one app's package upgrade breaks the other. Virtual environments per app, plus setting the service's ExecStart to the venv's Python binary, solve this.
  • QIf a colleague clones your Python project and gets a ModuleNotFoundError when they run it, what's the most likely cause and what's the correct fix?JuniorReveal
    Most likely cause: you forgot to freeze your dependencies into requirements.txt and commit it, or your colleague hasn't installed them. The fix: 1) You generate pip freeze > requirements.txt and commit it. 2) Your colleague activates their virtual environment with source .venv/bin/activate. 3) They run pip install -r requirements.txt to install all dependencies at the exact versions you used. Less likely causes: different Python versions (3.8 vs 3.12) or platform-specific binary incompatibilities (solved by using the same base image in production and development).
  • QWhat's the difference between pip freeze and pip list, and when would you use each one?Mid-levelReveal
    pip freeze outputs package names and versions in name==version format, designed to be saved directly to requirements.txt. pip list outputs a human-readable table. Use pip freeze when generating requirements.txt for reproducibility. Use pip list for ad-hoc inspection of what's installed in your environment. pip list also shows package installation location (--path) and outdated versions (--outdated), which pip freeze doesn't do. However, pip freeze includes transitive dependencies while pip list shows the same set. The key difference is output format, not content.
  • QExplain what happens under the hood when you activate a virtual environment. What environment variables change?SeniorReveal
    Activation modifies three key environment variables: (1) PATH: adds .venv/bin (or .venv\Scripts on Windows) to the front, so python and pip resolve to the venv's binaries first. (2) VIRTUAL_ENV: set to the path of the venv directory, allowing tools to detect which environment is active. (3) On some systems, PYTHONHOME is unset to ensure Python uses the venv's isolated paths. The activation script also defines a deactivate function that restores PATH and unsets VIRTUAL_ENV. The script doesn't copy the entire Python installation — it creates symlinks (Unix) or shortcuts (Windows) to the base Python binary and adds a site-packages directory for isolated package installations.
  • QHow would you manage Python dependencies for a production Docker image? Should you use a virtual environment inside the container?SeniorReveal
    Yes — use a virtual environment inside the Docker image for isolation, even though the container itself already provides some isolation. Reasons: (1) Multi-stage builds can copy only the virtual environment (COPY --from=builder /app/.venv /app/.venv), excluding build tools and source code from the final image. (2) Default Docker images often have Python installed globally; installing packages directly into the system Python can conflict with OS tools. (3) Virtual environments make it explicit which Python and packages are used, improving reproducibility. The standard pattern: RUN python -m venv /opt/venv && /opt/venv/bin/pip install -r requirements.txt and then ENV PATH="/opt/venv/bin:$PATH". This results in a lean image (no build dependencies) and clean dependency management.

Frequently Asked Questions

Do I need to create a new virtual environment for every Python project?

Yes, and that's the whole point. One environment per project guarantees that each project's dependencies stay isolated. It takes about 10 seconds to create one, and it saves hours of debugging mysterious version conflicts down the road. Think of it as a professional habit, not extra work. Even two projects that use identical dependencies today still benefit from separate environments — one will inevitably drift as you add new packages or upgrade existing ones.

What's the difference between venv, virtualenv, and conda?

venv is built into Python 3.3+ and covers 90% of use cases with zero installation required — use this unless you have a specific reason not to. virtualenv is a third-party package that predates venv and offers a few extra features like support for older Python versions (2.7) and faster creation. conda is a completely different tool from Anaconda that manages both Python packages AND non-Python system dependencies (like C libraries, CUDA, R), making it popular in data science but overkill for most web or scripting projects. Use venv for standard Python development; use conda only if you need binary dependencies that pip can't handle (e.g., numpy compiled with MKL).

If I delete my virtual environment folder by accident, do I lose my code?

No — your code lives in your project folder, not inside .venv. The virtual environment folder only contains Python itself and installed libraries. As long as you have your requirements.txt committed to Git, you can recreate the entire environment in seconds with python -m venv .venv followed by pip install -r requirements.txt. This is exactly why committing requirements.txt to Git is non-negotiable. Your code is safe; your environment is reproducible.

Can I move a virtual environment to a different folder or share it with another user?

No — virtual environments are not portable. The activation scripts contain hardcoded absolute paths to the Python binary and the environment location. If you move the folder, source .venv/bin/activate will fail with bad interpreter: No such file or directory. The correct approach is to delete the environment and recreate it at the new location using requirements.txt. Sharing an environment between users is similarly problematic due to file permissions. Don't try to hack around this — just regenerate the environment from requirements.txt.

Why does `pip freeze` show packages I didn't explicitly install?

pip freeze shows ALL installed packages, including transitive dependencies (packages that your direct dependencies depend on). For example, if you install requests, you'll also see urllib3, certifi, idna, and charset-normalizer in the freeze output because requests depends on them. This is intentional — you want to freeze the entire dependency graph, not just top-level packages. However, it also means that if you have unnecessary packages installed in the environment (e.g., from previous experiments), they'll also appear. Always start from a fresh environment for project-specific freezes: python -m venv fresh_env, activate, then install only what your project needs, then freeze.

What's the best practice for virtual environments in CI/CD pipelines?

In CI, always create a fresh virtual environment per run to ensure reproducibility and isolation from previous runs. Use a cache for pip packages (not the environment itself) to speed up installs. Example GitHub Actions snippet: ``yaml - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Cache pip packages uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - name: Install dependencies run: | python -m venv .venv source .venv/bin/activate pip install -r requirements.txt ` Always run test commands with the environment active: source .venv/bin/activate && pytest`. For Docker builds, use multi-stage builds with virtual environments as described in the interview questions section.

🔥

That's Advanced Python. Mark it forged?

5 min read · try the examples if you haven't

Previous
Unit Testing with pytest
8 / 17 · Advanced Python
Next
Python Packaging and pip