diff --git a/README.md b/README.md new file mode 100644 index 0000000..65101fe --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# Django-dbfilestorage + + +[![CircleCI](https://circleci.com/gh/tyrelsouza/django-dbfilestorage.svg?style=svg)](https://circleci.com/gh/tyrelsouza/django-dbfilestorage) + +Custom file storage for Django that stores file data and content type in the database. +Easy to use for testing when you don't care about a filename, and just want to test file data. + +Intended to be used in tests, never in production. + +## INSTALLATION + +In your project's `settings.py` file, add `'dbfilestorage'` to your `INSTALLED_APPS`: + +```python +INSTALLED_APPS = ( + ... + 'dbfilestorage' +) +``` + +Next, if you want to set this globally, set the setting: + +```python +DEFAULT_FILE_STORAGE='dbfilestorage.storage.DBFileStorage' +``` + +Or you can set it individually on a field: [Django Docs](https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.FileField.storage) + +```python +from dbfilestorage.storage import DBFileStorage + +class SomeClass(models.Model): + file = models.FileField(upload_to=u'anywhere', + storage=DBFileStorage()) +``` + + +## TODO + +- More Tests +- Test that this works on a fake model, not just the storage file. +- Different django and different python versions. +- Store original filename in a field, maybe? +- Use original filename instead, so it honors the "upload_to" parameter. + + +## CHANGELOG + +- 2016-12-08 [Tyrel Souza] Add more documentation. +- 2016-12-07 [Tyrel Souza] Update Readme, move to github, gitlab wasn't functioning properly. +- 2016-12-07 [Tyrel Souza] Initial commits and basic project setup diff --git a/README.rst b/README.rst deleted file mode 100644 index 3ffc24f..0000000 --- a/README.rst +++ /dev/null @@ -1,26 +0,0 @@ -Django-dbfilestorage --------------------- - -.. image:: https://circleci.com/gh/tyrelsouza/django-dbfilestorage/tree/master.svg?style=svg - :target: https://circleci.com/gh/tyrelsouza/django-dbfilestorage/tree/master - -Custom file storage for Django that stores file data and content type in the database. -Easy to use for testing when you don't care about a filename, and just want to test file data. - -Intended to be used in tests, never in production. - - -TODO -==== - -More Tests -Different django and different python versions. -Store original filename in a field, maybe? -Use original filename instead, so it honors the "upload_to" parameter. - - -CHANGELOG -========= - -- 2016-12-07 [Tyrel Souza] Update Readme, move to github, gitlab wasn't functioning properly. -- 2016-12-07 [Tyrel Souza] Initial commits and basic project setup diff --git a/dbfilestorage/migrations/0001_initial.py b/dbfilestorage/migrations/0001_initial.py index 794d98c..838d2c6 100644 --- a/dbfilestorage/migrations/0001_initial.py +++ b/dbfilestorage/migrations/0001_initial.py @@ -16,7 +16,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name='DBFile', fields=[ - ('name', models.CharField(max_length=100, + ('name', models.CharField(max_length=100, primary_key=True, serialize=False)), ('content_type', models.CharField(max_length=100)), diff --git a/dbfilestorage/storage.py b/dbfilestorage/storage.py index c716901..133d919 100644 --- a/dbfilestorage/storage.py +++ b/dbfilestorage/storage.py @@ -31,9 +31,12 @@ class DBStorage(Storage): """ The save method does most of the 'magic'. It stores the contents of the file as a base64 string. - It then takes the filename, and tries to get the mimetype from that (for rendering) - Then it takes the md5 of the read file and uses that as the "unique" key to access the file. - Then it checks if the file exists and if it doesn't, it will create the entry in the database. + It then takes the filename, and tries to get the mimetype from that + (for rendering) + Then it takes the md5 of the read file and uses that as the "unique" + key to access the file. + Then it checks if the file exists and if it doesn't, it will create + the entry in the database. :return str: the name(md5) to look up the file by. """ diff --git a/docs/conf.py b/docs/conf.py index 19ff111..f56ad0a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Django DBStorage documentation build configuration file, created by +# Django DBFileStorage documentation build configuration file, created by # sphinx-quickstart on Wed Dec 7 14:50:49 2016. # # This file is execfile()d with the current directory set to its @@ -45,7 +45,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'Django DBStorage' +project = u'Django DBFileStorage' copyright = u'2016, Tyrel Souza' author = u'Tyrel Souza' @@ -54,9 +54,9 @@ author = u'Tyrel Souza' # built documents. # # The short X.Y version. -version = u'0.0.1' +version = u'0.0.3' # The full version, including alpha/beta/rc tags. -release = u'0.0.1' +release = u'0.0.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -82,7 +82,7 @@ todo_include_todos = False # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -99,7 +99,7 @@ html_static_path = ['_static'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = 'DjangoDBStoragedoc' +htmlhelp_basename = 'DjangoDBFileStoragedoc' # -- Options for LaTeX output --------------------------------------------- @@ -126,7 +126,9 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'DjangoDBStorage.tex', u'Django DBStorage Documentation', + (master_doc, + 'DjangoDBFileStorage.tex', + u'Django DBFileStorage Documentation', u'Tyrel Souza', 'manual'), ] @@ -136,7 +138,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'djangodbstorage', u'Django DBStorage Documentation', + (master_doc, 'djangodbstorage', u'Django DBFileStorage Documentation', [author], 1) ] @@ -147,10 +149,7 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'DjangoDBStorage', u'Django DBStorage Documentation', - author, 'DjangoDBStorage', 'One line description of project.', + (master_doc, 'DjangoDBFileStorage', u'Django DBFileStorage Documentation', + author, 'DjangoDBFileStorage', 'One line description of project.', 'Miscellaneous'), ] - - - diff --git a/docs/dbfilestorage/installation.rst b/docs/dbfilestorage/installation.rst new file mode 100644 index 0000000..a048721 --- /dev/null +++ b/docs/dbfilestorage/installation.rst @@ -0,0 +1,29 @@ +Installation +============ + +In your project's `settings.py` file, add `'dbfilestorage'` to your `INSTALLED_APPS`: + +.. code:: python + + INSTALLED_APPS = ( + ... + 'dbfilestorage' + ) + + +Next, if you want to set this globally, set the setting: + +.. code:: python + + DEFAULT_FILE_STORAGE='dbfilestorage.storage.DBFileStorage' + + +Or you can set it individually on a field: `Django Docs `_ + +.. code:: python + + from dbfilestorage.storage import DBFileStorage + + class SomeClass(models.Model): + file = models.FileField(upload_to=u'anywhere', + storage=DBFileStorage()) diff --git a/docs/dbfilestorage/usage.rst b/docs/dbfilestorage/usage.rst new file mode 100644 index 0000000..4b69f95 --- /dev/null +++ b/docs/dbfilestorage/usage.rst @@ -0,0 +1,65 @@ +Usage +===== + +Backstory +--------- + +The use case for this may be very small. At my company for our project we have +a giant test fixture, and we test file upload and creation. At the inception of +this module, I was working on changing our backend system to support Amazon S3 +filestorage. I was getting frustrated with testing the assumption that a file +would exist on the hard drive, and not "somewhere else" so I started this module. + +At first, I was going to use the BLOB field, but the way that we dump and load +our test fixtures didn't dump BLOBs by default. + +Thus, the current implementation was born. + + +On to the Usage! +---------------- + +The normal usage for this is almost exactly the same for local file storage. +The user will upload a file the exact same way, and except for the url being an +md5 string, everything works exactly the same. + +Saving file +~~~~~~~~~~~ + +The save method does most of the 'magic'. It stores the contents of the file as +a base64 string. It then takes the filename, and tries to get the mimetype from +that (for rendering purposes). Then it takes the md5 of the read file and uses +that as the "unique" key to access the file. Then it checks if the file exists +and if it doesn't, it will create the entry in the database. + +Example from Tests: + +.. code:: python + + DBFileStorage.save("kris.jpg", file_object) + +There will then be an entry in the database `50f3bfa6b91668789acab5f0a733fb3a` +that has the content of the `kris.jpg` file in it, with the content_type of +`image/jpeg`. + +Opening file +~~~~~~~~~~~~ + +This file will then be accessible by the model field's `.open()` as normal. +The `.open()` returns a ContentFile of the base64 decoded data, so it should +work everywhere that a normal file would. + +Viewing file with browser ( .url() ) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +I've provided a `dbstorage_file` view that will render the file. + +It gets the file object from the database referenced by the md5 (the +filefield.url() will provide this) automatically. It then returns a +HttpResponse of the decoded file and content type. Very straightforward. + +Other operations +~~~~~~~~~~~~~~~~ + +Everything else, such as `.path()`, `.delete()`, `.size()`, `.exists()` are +exactly the same, in usage as normal. diff --git a/docs/index.rst b/docs/index.rst index 7d99198..08cde44 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,13 +3,15 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to Django DB File Storage's documentation! -================================================== +Django DB File Storage +====================== .. toctree:: :maxdepth: 2 :caption: Contents: + dbfilestorage/installation + dbfilestorage/usage Indices and tables diff --git a/tests/urls.py b/tests/urls.py index 87abfd0..897d9c8 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -13,9 +13,10 @@ Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ -from django.conf.urls import url +from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), + url(r'^dbfilestorage/', include('dbfilestorage.urls')), ]