Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FileStorage abstraction for handling remote file storage #193

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,11 @@ in the CSV export. By default these uploaded files are stored in an
obscured location under your project's ``MEDIA_ROOT`` directory but
ideally the should be stored somewhere inaccessible to the public. To
set the location where files are stored to be somewhere outside of your
project's ``MEDIA_ROOT`` directory you just need to define the
project's ``MEDIA_ROOT`` directory you just need to define the either the
``FORMS_BUILDER_UPLOAD_ROOT`` setting in your project's ``settings``
module. Its value should be an absolute path on the web server that
isn't accessible to the public.
module or the ``FORMS_BUILDER_FILE_STORAGE`` setting. The
``FORMS_BUILDER_UPLOAD_ROOT`` value should be an absolute path on the web
server that isn't accessible to the public.


Configuration
Expand All @@ -146,6 +147,9 @@ module.
will be added to the form field types. Defaults to ``()``
* ``FORMS_BUILDER_UPLOAD_ROOT`` - The absolute path where files will
be uploaded to. Defaults to ``None``
* ``FORMS_BUILDER_FILE_STORAGE`` - The class path of the Django storage class
to use for storing files. Defaults to ``None`` which uses the default file
storage.
* ``FORMS_BUILDER_USE_HTML5`` - Boolean controlling whether HTML5 form
fields are used. Defaults to ``True``
* ``FORMS_BUILDER_USE_SITES`` - Boolean controlling whether forms are
Expand Down
22 changes: 15 additions & 7 deletions forms_builder/forms/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from future.builtins import bytes, open
import os

from csv import writer
from mimetypes import guess_type
Expand All @@ -18,7 +20,7 @@

from forms_builder.forms.forms import EntriesForm
from forms_builder.forms.models import Form, Field, FormEntry, FieldEntry
from forms_builder.forms.settings import CSV_DELIMITER, UPLOAD_ROOT
from forms_builder.forms.settings import CSV_DELIMITER, UPLOAD_ROOT, FILE_STORAGE
from forms_builder.forms.settings import USE_SITES, EDITABLE_SLUGS
from forms_builder.forms.utils import now, slugify

Expand All @@ -29,15 +31,17 @@
except ImportError:
XLWT_INSTALLED = False


fs = FileSystemStorage(location=UPLOAD_ROOT)
if UPLOAD_ROOT is not None:
fs = FileSystemStorage(location=UPLOAD_ROOT)
else:
fs = FILE_STORAGE()
form_admin_filter_horizontal = ()
form_admin_fieldsets = [
(None, {"fields": ("title", ("status", "login_required",),
("publish_date", "expiry_date",),
"intro", "button_text", "response", "redirect_url")}),
(_("Email"), {"fields": ("send_email", "email_from", "email_copies",
"email_subject", "email_message")}),]
"email_subject", "email_message")}), ]

if EDITABLE_SLUGS:
form_admin_fieldsets.append(
Expand Down Expand Up @@ -190,10 +194,14 @@ def file_view(self, request, field_entry_id):
"""
model = self.fieldentry_model
field_entry = get_object_or_404(model, id=field_entry_id)
path = join(fs.location, field_entry.value)
if hasattr(fs, 'location'):
path = join(fs.location, field_entry.value)
else:
path = field_entry.value
response = HttpResponse(content_type=guess_type(path)[0])
f = open(path, "r+b")
response["Content-Disposition"] = "attachment; filename=%s" % f.name
f = fs.open(path, "r+b")
filename = os.path.basename(field_entry.value)
response["Content-Disposition"] = "attachment; filename=%s" % filename
response.write(f.read())
f.close()
return response
Expand Down
7 changes: 4 additions & 3 deletions forms_builder/forms/forms.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from future.builtins import int, range, str

Expand All @@ -8,7 +9,6 @@
import django
from django import forms
from django.forms.extras import SelectDateWidget
from django.core.files.storage import default_storage
from django.core.urlresolvers import reverse
from django.template import Template
from django.utils.safestring import mark_safe
Expand All @@ -20,9 +20,10 @@
from forms_builder.forms.utils import now, split_choices


fs = default_storage
if settings.UPLOAD_ROOT is not None:
fs = default_storage.__class__(location=settings.UPLOAD_ROOT)
fs = settings.FILE_STORAGE(location=settings.UPLOAD_ROOT)
else:
fs = settings.FILE_STORAGE()


##############################
Expand Down
6 changes: 5 additions & 1 deletion forms_builder/forms/settings.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured

from django.core.files.storage import get_storage_class

if not ("django.contrib.sites" in settings.INSTALLED_APPS):
raise ImproperlyConfigured("django.contrib.sites is required")
Expand All @@ -23,6 +24,9 @@
# The absolute path where files will be uploaded to.
UPLOAD_ROOT = getattr(settings, "FORMS_BUILDER_UPLOAD_ROOT", None)

# The File Storage class to use, uses default file storage if None
FILE_STORAGE = get_storage_class(getattr(settings, "FORMS_BUILDER_FILE_STORAGE", None))

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following on from our discussion - perhaps this could default to a storage class that exists in this app, that merely uses UPLOAD_ROOT inside its init, and raises an error there if the setting isn't defined. That way all the access to the storage class can happen directly via FORMS_BUILDER_FILE_STORAGE.

# Boolean controlling whether HTML5 form fields are used.
USE_HTML5 = getattr(settings, "FORMS_BUILDER_USE_HTML5", True)

Expand Down