Commit de557851 authored by Ben Firshman's avatar Ben Firshman
Browse files

Initial commit

parents
*.pyc
/build
Copyright (c) 2009, Ben Firshman
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The names of its contributors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
django-database-files
=====================
django-database-files is a storage system for Django that stores uploaded files
in the database.
BIG SCARY WARNING: It is generally a bad idea to serve static files from Django,
but there are some valid use cases. If your Django app is behind a caching
reverse proxy and you need to scale your application servers, it may be
simpler to store files in the database instead of using a distributed
filesystem.
Requires:
* Django 1.1
Installation
------------
$ python setup.py install
Usage
-----
In ``settings.py``, add ``'database_files'`` to your INSTALLED_APPS and add this line::
DEFAULT_FILE_STORAGE = 'database_files.storage.DatabaseStorage'
Although the ``upload_to`` argument on ``FileField`` is required, it is not
used by ``database_files``. Just set it to a dummy value::
upload = models.FileField(upload_to='not required')
All your ``FileField`` and ``ImageField`` files will now be stored in the
database.
from django.db import models
import os
class FileManager(models.Manager):
def get_from_name(self, name):
return self.get(pk=os.path.splitext(os.path.split(name)[1])[0])
from south.db import db
from django.db import models
from database_files.models import *
class Migration:
def forwards(self, orm):
# Adding model 'File'
db.create_table('database_files_file', (
('id', orm['database_files.File:id']),
('content', orm['database_files.File:content']),
('size', orm['database_files.File:size']),
))
db.send_create_signal('database_files', ['File'])
def backwards(self, orm):
# Deleting model 'File'
db.delete_table('database_files_file')
models = {
'database_files.file': {
'content': ('django.db.models.fields.TextField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'size': ('django.db.models.fields.IntegerField', [], {})
}
}
complete_apps = ['database_files']
from django.db import models
from database_files.manager import FileManager
class File(models.Model):
content = models.TextField()
size = models.IntegerField()
objects = FileManager()
import base64
from database_files import models
from django.core import files
from django.core.files.storage import Storage
from django.core.urlresolvers import reverse
import os
import StringIO
class DatabaseStorage(Storage):
def _generate_name(self, name, pk):
"""
Replaces the filename with the specified pk and removes any dir
"""
dir_name, file_name = os.path.split(name)
file_root, file_ext = os.path.splitext(file_name)
return '%s%s' % (pk, file_ext)
def _open(self, name, mode='rb'):
try:
f = models.File.objects.get_from_name(name)
except models.File.DoesNotExist:
return None
fh = StringIO.StringIO(base64.b64decode(f.content))
fh.name = name
fh.mode = mode
fh.size = f.size
return files.File(fh)
def _save(self, name, content):
f = models.File.objects.create(
content=base64.b64encode(content.read()),
size=content.size,
)
return self._generate_name(name, f.pk)
def exists(self, name):
"""
We generate a new filename for each file, so it will never already
exist.
"""
return False
def delete(self, name):
try:
models.File.objects.get_from_name(name).delete()
except models.File.DoesNotExist:
pass
def url(self, name):
return reverse('database_file', kwargs={'name': name})
def size(self, name):
try:
return models.File.objects.get_from_name(name).size
except models.File.DoesNotExist:
return 0
[
{
"pk": 1,
"model": "database_files.file",
"fields": {
"content": "MTIzNDU2Nzg5MA==",
"size": 10
}
}
]
from django.db import models
class Thing(models.Model):
upload = models.FileField(upload_to='not required')
DATABASE_ENGINE = 'sqlite3'
ROOT_URLCONF = 'database_files.urls'
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'database_files',
'database_files.tests',
]
DEFAULT_FILE_STORAGE = 'database_files.storage.DatabaseStorage'
from django.core import files
from django.test import TestCase
from database_files.models import File
from database_files.tests.models import Thing
import StringIO
class DatabaseFilesTestCase(TestCase):
def test_adding_file(self):
test_file = files.temp.NamedTemporaryFile(
suffix='.txt',
dir=files.temp.gettempdir()
)
test_file.write('1234567890')
test_file.seek(0)
t = Thing.objects.create(
upload=files.File(test_file),
)
self.assertEqual(File.objects.count(), 1)
t = Thing.objects.get(pk=t.pk)
self.assertEqual(t.upload.file.size, 10)
self.assertEqual(t.upload.file.name[-4:], '.txt')
self.assertEqual(t.upload.file.read(), '1234567890')
t.upload.delete()
self.assertEqual(File.objects.count(), 0)
class DatabaseFilesViewTestCase(TestCase):
fixtures = ['test_data.json']
def test_reading_file(self):
response = self.client.get('/1.txt')
self.assertEqual(response.content, '1234567890')
self.assertEqual(response['content-type'], 'text/plain')
self.assertEqual(unicode(response['content-length']), '10')
from django.conf.urls.defaults import *
urlpatterns = patterns('',
url(r'^(?P<name>.+)$', 'database_files.views.serve', name='database_file'),
)
import base64
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.cache import cache_control
import mimetypes
from database_files.models import File
import os
@cache_control(max_age=86400)
def serve(request, name):
pk, file_ext = os.path.splitext(name)
try:
pk = int(pk)
except ValueError:
raise Http404('Filename is not an integer')
f = get_object_or_404(File, pk=pk)
mimetype = mimetypes.guess_type(name)[0] or 'application/octet-stream'
response = HttpResponse(base64.b64decode(f.content), mimetype=mimetype)
response['Content-Length'] = f.size
return response
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from distutils.core import setup
setup(
name='django-database-files',
version='0.1',
description='A storage system for Django that stores uploaded files in the database.',
author='Ben Firshman',
author_email='ben@firshman.co.uk',
url='http://github.com/bfirsh/django-database-files/',
packages=[
'database_files',
],
classifiers=['Development Status :: 4 - Beta',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
],
)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment