Using pyenv (venv) with Django on Webfaction
Deprecated Advice
Setting up Python virtual environments has never been easier.
When I returned to Django and began to rebuild my site I didn't find any information that mentioned this option.
A number of the how-tos I read involved editing the wsgi.pyfile that is installed when you create a mod_wsgi app on WebFaction, so the wsgi.py file would activate the virtualenv. This really isn’t necessary. Interestingly, WebFaction’s own instructions are simple, current, and really all you need to get things rolling pretty easily. Go figure. This article adds a few specifics (particularly for the Apache config file), but is really about using Python3's built in venv command.
As of Python3.3, the ability to create virtual environments is built right in to the languagein the form of the pyvenv command (which will be deprecated in favor of simply venv in Python3.6.) This makes it even easier to setup Python virtualenvs on WebFaction.
One last bit before we get to it. Most of what I did to set up Django in a virtualenv is exactly what is described in the this excellent how-to by Michał Karzyński, the only real difference being the use of the built in venv instead of doing a separate install of virtualenv. I recommend you read Michał’s article as it goes a bit beyond setup and includes instructions on serving static media and separating your development and production environments.
Ok. Here’s what I did:
We’re going to skip the Django App installer script that WebFaction provides and just create a generic mod_wsgi application.
In the WebFaction control panel,
-
Create a new app of type mod_wsgi 4.5.9/Python 3.5 (if you read this in the future the version numbers may have increased.) I’m going to call this app, 'yourapp.'
-
Create a websiteusing one of your available domains and connect it to yourapp.
-
Make sure it’s working by visiting the site in your browser. You should see this:
Welcome to your mod_wsgi website! It uses:
Python 3.5.2 (default, Sep 4 2016, 00:18:27)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)]
WSGI version: (4, 5, 8)
Now, if you visit /home/username/webapps/yourapp via ssh or your FTP client, you will see the directories: apache2 and htdocs.
You aren’t going to need htdocs so you can delete that now.
Ok. Now we just have to create the virtual environment for our Django app and set up a virtual host for our site in the Apache config file.
Let’s create the virtual environment first.
I prefer to create the virtual environment directory in the same location as the app itself. This puts the directory next to the apache2 directory (and in a moment next to our Django project directory.)
Do an ssh login to your WebFaction account, and run these commands: (You can use any name for your virtual environment. I've used the app name with ‘env’ appended.)
cd /home/username/webapps/yourapp
python3 -m venv yourappenv
Or just:
python3 -m venv /home/username/webapps/yourapp/yourappenv
Now we have a Python virtual environment installed. Easy! (See the directory tree below.)
/yourapp
├── apache2
│ ├── bin
│ ├── conf <== #location of http.conf file
│ ├── lib
│ ├── logs
│ └── modules
└── yourappenv
├── bin <== #location of activate command
├── include
├── lib
└── lib64 -> lib
Activating your virtual environment
When you are managing your app from an ssh session you will need to activate your virtual environment. The command is located in yourappenv/bin. From the yourapp directory, run source yourappenv/bin/activate
(I'd suggest creating an alias or login hook that activates your virtual environment so you don't have to type in file paths every time.)
Pip!
Another nice thing about the built in venv command is that it installs pip automatically. Let's get pip updated and ready to load your requirements.txt file.
Run:
pip install --upgrade pip
(Optional).
I like to install yolk so I can easily update all of my installed packages.
pip install yolk3K
Running,
yolk --upgrade
will check all of your installed packages for available updates and install them if there are any.
Now you can install the latest version of Django with:
pip install Django
If you have a requirements.txt file ready you can install all of your packages with:
pip install -r requirements.txt
Create a Django project
At this point I recommend you go ahead and set up a Django project. You will need to know the name (the path) of this project when we configure Apache to serve your site.
With your virtual environment activated run:
django-admin.py startproject yourproject
You may need to make sure that your django-admin.py and manage.py files are executable.
cd /home/username/webapps/yourapp/yourappenv/bin/
chmod +x django-admin.py
Once you create your project, from the project directory, run:
chmod +x manage.py
This allows you to run:
./manage.py 'your command'`
whenever you are managing your project
Great! Only one thing left to do!
Set up a virtual host for your site.
This will ensure that Apache is serving your site using the correct environment.
You are going to make some changes to your app's httpd.conf file. The file is located here:
/home/username/webapps/yourapp/apache2/conf/httpd.conf
You can make a copy and alter the original if you like. Change the original to look like this:
ServerRoot "/home/username/webapps/yourapp/apache2"
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule dir_module modules/mod_dir.so
LoadModule env_module modules/mod_env.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule mime_module modules/mod_mime.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule wsgi_module modules/mod_wsgi.so
LoadModule unixd_module modules/mod_unixd.so
Listen 27140 #this should be whatever WebFaction set it to.
KeepAlive Off
SetEnvIf X-Forwarded-SSL on HTTPS=1
ServerLimit 1
StartServers 1
MaxRequestWorkers 5
MinSpareThreads 1 MaxSpareThreads 3
ThreadsPerChild 5
WSGIRestrictEmbedded On
WSGILazyInitialization On
<VirtualHost *>
ServerName yourdomain.net # Logging config
LogFormat "%{X-Forwarded-For}i %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"" combined CustomLog /home/username/logs/user/access_yourapp.log combined
ErrorLog /home/username/logs/user/error_yourapp.log
#Django WSGI settings
WSGIDaemonProcess yourapp processes=2 threads=12 python-home=/home/username/webapps/yourapp/yourappenv python-path=/home/username/webapps/yourapp/yourproject
WSGIProcessGroup yourapp
WSGIScriptAlias / /home/username/webapps/yourapp/yourproject/yourproject/wsgi.py
</VirtualHost>
-
Make the necessary substitutions in the path names using your own user, application, and project names.
-
Set the
Listenvalue to whatever WebFaction set it to in the original config file (meaning you shouldn't have to change it.) -
Change
ServerNameto the appropriate domain. -
Note that due to Django's somewhat frustrating naming conventions, the
WSGIScriptAliaspath will seem like it contains a typo. It doesn't. This path needs to go all the way to your project directory, which is where thewsgi.pyfile lives. (When you create a Django project you get a directory named as you asked, with a subdirectory of the same name.) -
Save the
httpd.conffile (to its original location) and restart Apache.
From the 'yourapp' directory run:
./apache2/bin/restart
And that should do it! Have fun and build something cool!