Digital product support for the Django/Mezzanine/Cartridge stack, with support for large files with django_downloadview.
- Adds download fields to the
Product
admin. - Adds download fields to the
Form
admin. - Download files are available upon order/form completion and if there is a confirmation email a link is added to the files.
Often times a download is bundled with a physical product -- mp3's may come with a cd or an epub might come with a book. In this case it often makes sense to have one variation for just the download and another for the physical good and the download.
- When all variations of a product are "download only", it's quantity field is hidden.
- The quantity field of "download only" variations is forced to "1" and hidden.
When orders consist solely of "download only" products, they get:
- marked as processed automatically
- free shipping
To enable this feature:
- Add a "Downloads" type to the setting
SHOP_OPTION_TYPE_CHOICES
. - Add a
ProductOption
named "Download Only" to the "Download" type.
pip install cartridge-downloads
python manage.py migrate
# OPTIONALLY, if you're using the "download only" feature and want free shipping for transactions consisting of only those products.
SHOP_HANDLER_BILLING_SHIPPING = "cartridge_downloads.checkout.billship_handler"
# Alternately, you could call this handler method from your own handler.
SHOP_HANDLER_ORDER = 'cartridge_downloads.checkout.order_handler'
...
# OPTIONALLY, specify allowed file formats here. Defaults to allowing all.
FILEBROWSER_SELECT_FORMATS = {
...
'Download': ['Document'],
}
...
INSTALLED_APPS = (
...
'cartridge_downloads', # Before cartridge.shop
...
'cartridge.shop',
...
...
)
from cartridge_downloads.views import override_filebrowser
...
urlpatterns = [
...
# Cartridge-Downloads' URLs (before cartridge urls).
url("^downloads/", include('cartridge_downloads.urls')),
url("^shop/(?=(product|cart))",
include('cartridge_downloads.cartridge_override_urls')),
url(r'^delete/$', override_filebrowser.delete, name="fb_delete"),
...
]
In development, static assets are served by the development server and as such can be masked by django views. In production, your static assets are served more efficiently by circumventing the wsgi process. As such, you'll need to take action specific to your deployment setup to ensure your downloads are only available to authorized clients.
Here's an example nginx snippet that takes advantage of the fact that nginx directs to the location block with the longest matching "prefix string" to ensure downloads are only served via django views:
# The longest matching "prefix string" is selected.
location /static/media/uploads/downloads {
proxy_pass http://.example.com;
}
location /static {
alias /path/to/static;
}
Even though they aren't publicly available you probably still want to serve downloads with your webserver for performance. See the django-downloadview docs for details.
There's quite a bit more going on than this, but it's mostly UI hacks. Here's a summary of the mechanics:
- Monkey patch an inline to our
Download
model onto cartridge/mezzanine'sProductAdmin
/FormAdmin
. - When a user successfully submits an order/form, create a
Transaction
and anAcquisition
referencing theDownload
s, add transaction credentials to their session and as query params in a link in the confirmation email, and direct them to the/downloads/
view. - The
/downloads/
view links toDownload
s associated withAcquisition
s associated with theTransaction
.
cd cartridge-downloads
pip install -e . -c constraints.txt
# If python2:
pip install mock
python test
- To run browser tests, install python dependencies with
pip install -r dev-requirements.txt
. - To run browser tests headless, install phantomjs on your system. A
ghostdriver.log
file is created (and deleted after every TestCase) which may be useful for debugging these, though running them again with firefox is generally easier.
python test [--debug] [<webdriver>]
- --debug Write verbose output to ghostdriver.log.
- <webdriver> [phantomjs|firefox|chrome] If ommitted the browser tests will default to phantomjs and fall back to firefox if unavailable.