pip-tools is the right tool. I had the same problem so I solved it in this easy way.
From the site:
If you have different environments that you need to install different but compatible packages for, then you can create layered requirements files and use one layer to constrain the other.
For example, if you have a Django project where you want the newest 2.1 release in production and when developing you want to use the Django debug toolbar, then you can create two *.in files, one for each layer:
# requirements.in
django<2.2
At the top of the development requirements dev-requirements.in you use -c requirements.txt to constrain the dev requirements to packages already selected for production in requirements.txt.
# dev-requirements.in
-c requirements.txt
django-debug-toolbar
First, compile requirements.txt as usual:
$ pip-compile
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile
#
django==2.1.15
# via -r requirements.in
pytz==2019.3
# via django
Now compile the dev requirements and the requirements.txt file is used as a constraint:
$ pip-compile dev-requirements.in
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile dev-requirements.in
#
django-debug-toolbar==2.2
# via -r dev-requirements.in
django==2.1.15
# via
# -c requirements.txt
# django-debug-toolbar
pytz==2019.3
# via
# -c requirements.txt
# django
sqlparse==0.3.0
# via django-debug-toolbar
As you can see above, even though a 2.2 release of Django is available, the dev requirements only include a 2.1 version of Django because they were constrained. Now both compiled requirements files can be installed safely in the dev environment.
To install requirements in production stage use:
$ pip-sync
You can install requirements in development stage by:
$ pip-sync requirements.txt dev-requirements.txt