├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── compress_field ├── __init__.py ├── base.py ├── fieldfiles.py ├── models.py └── tasks.py ├── conftest.py ├── django_test_settings.py ├── example ├── __init__.py ├── apps.py ├── forms.py ├── models.py ├── urls.py └── views.py ├── pytest.ini ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── data └── text.txt └── test_models.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | media/ 22 | static/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | *.sqlite3 27 | 28 | # Installer logs 29 | pip-log.txt 30 | pip-delete-this-directory.txt 31 | 32 | # Unit test / coverage reports 33 | htmlcov/ 34 | .tox/ 35 | .coverage 36 | .cache 37 | .eggs 38 | nosetests.xml 39 | coverage.xml 40 | 41 | # Translations 42 | *.mo 43 | 44 | # Mr Developer 45 | .mr.developer.cfg 46 | .project 47 | .pydevproject 48 | 49 | # Rope 50 | .ropeproject 51 | 52 | # Django stuff: 53 | *.log 54 | *.pot 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | 59 | README.rst 60 | .DS_Store 61 | 62 | .venv 63 | .autoenv 64 | .vscode -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 3.7 4 | script: DJANGO_SETTINGS_MODULE=django_test_settings python setup.py pytest 5 | env: 6 | - DJANGO=1.11 7 | - DJANGO=2.1 8 | - DJANGO=3.0 9 | install: 10 | - pip install -q Django==$DJANGO -r requirements.txt 11 | branches: 12 | only: 13 | - master 14 | after_success: 15 | - coveralls 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 1994-2004 The FreeBSD Project. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. 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. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT "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 FREEBSD PROJECT 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. 10 | 11 | The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo "dev install development packages" 3 | @echo "setup install compress_fields" 4 | @echo "test run default test suit" 5 | @echo "upload upload packages to PyPi" 6 | 7 | 8 | dev: 9 | pip install -r requirements.txt 10 | 11 | upload: export DJANGO_SETTINGS_MODULE=django_test_settings 12 | upload: 13 | python setup.py build bdist_wheel 14 | python setup.py build sdist 15 | pip install twine 16 | twine upload dist/* 17 | 18 | setup: 19 | python setup.py install 20 | 21 | 22 | test: 23 | python setup.py test 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Django Compress Storage 2 | ======================= 3 | 4 | [![Build Status](https://travis-ci.org/valdergallo/django-compress-field.png?branch=master)](https://travis-ci.org/valdergallo/django-compress-field) 5 | [![Latest Version](http://img.shields.io/pypi/v/django-compress-field.svg)](https://pypi.python.org/pypi/django-compress-field) 6 | [![BSD License](http://img.shields.io/badge/license-BSD-yellow.svg)](http://opensource.org/licenses/BSD-3-Clause) 7 | [![Pypi Download](http://img.shields.io/pypi/dm/django-compress-field.svg)](https://www.djangopackages.com/packages/p/django-compress-field) 8 | 9 | Custom ZipFileField for Django that auto compact file uploaded 10 | 11 | ``` 12 | PROJECT RENAMED django-compress-storage to django-compress-field 13 | ``` 14 | 15 | Install 16 | ------- 17 | 18 | ```bash 19 | pip install django-compress-field 20 | ``` 21 | or by source code 22 | ```bash 23 | git clone https://github.com/valdergallo/django-compress-field/ 24 | python setup.py install 25 | ``` 26 | or 27 | ``` 28 | pip install git+https://github.com/valdergallo/django-compress-field.git 29 | ``` 30 | 31 | 32 | Features 33 | -------- 34 | 35 | - Compress FileUpload storage file with Zip 36 | - Delete old file that was compressed on zip 37 | - Support for South Migrations 38 | - Support Django 1.2+ 39 | - Celery 2.5+ support - async compress file with Celery 40 | - Windows Support 41 | - Linux support 42 | - OSx support 43 | - Support for Python3+ 44 | - Support for Python2.6+ 45 | 46 | 47 | Motivation 48 | ---------- 49 | 50 | On my job we need save all upload files for 5 year. Losing a lot space on server with this files, because this I created this application. 51 | 52 | 53 | Django Settings Configurations 54 | ------------------------------ 55 | 56 | ```python 57 | 58 | FILE_COMPRESS_DELETE_OLD_FILE = True # to delete old files after compressed 59 | FILE_COMPRESS_DELETE_OLD_FILE = False # to not delete old files after compressed 60 | 61 | # Feature only for version v9.0+ 62 | FILE_COMPRESS_QUEUE = 'Celery' # by default queue is Celery, but you can change this with this var on settings 63 | 64 | 65 | INSTALLED_APPS = ( 66 | ... 67 | ... 68 | 'compress_field', 69 | ) 70 | ``` 71 | 72 | 73 | Usage 74 | ----- 75 | 76 | ```python 77 | 78 | # example model.py 79 | 80 | from django.db import models 81 | from compress_field import ZipFileField 82 | 83 | class MyContent(models.Model): 84 | name = models.CharField(max_length=150) 85 | create_date = models.DateTimeField(auto_now=True) 86 | upload_file = ZipFileField(upload_to='mycontent/') 87 | 88 | def __unicode__(self): 89 | return self.name 90 | 91 | ``` 92 | 93 | 94 | Shell 95 | ----- 96 | 97 | ```python 98 | 99 | >>> from example.core import MyContent 100 | >>> m = MyContent.objects.get(id=2) 101 | >>> m.upload_file 102 | 103 | >>> m.upload_file.compress() 104 | >>> m.upload_file 105 | 106 | ``` 107 | 108 | 109 | Using with Celery 110 | ----------------- 111 | 112 | If Celery are installed on Site Packages. You just need create one post_save on 113 | your model to use async compress. 114 | 115 | 116 | ```python 117 | # listeners.py file 118 | 119 | from django.db.models.signals import post_save 120 | 121 | def auto_compress_file_on_post_save(sender, instance, **kargs): 122 | instance.upload_file.compress() 123 | 124 | post_save.connect(auto_compress_file_on_post_save, sender=MyContent) 125 | 126 | ``` 127 | 128 | If you don´t wanna use Celery async compress: 129 | 130 | 131 | ```python 132 | 133 | def auto_compress_file_on_post_save(sender, instance, **kargs): 134 | instance.upload_file.compress(async=False) 135 | 136 | post_save.connect(auto_compress_file_on_post_save, sender=MyContent) 137 | 138 | ``` 139 | 140 | 141 | Developer 142 | --------- 143 | 144 | ```bash 145 | # download code 146 | git clone https://github.com/valdergallo/django-compress-field 147 | 148 | # install developer packages 149 | setup.py develop 150 | 151 | # test project 152 | pytest . 153 | 154 | #clean extra content 155 | setup.py clean 156 | 157 | ``` 158 | 159 | 160 | -------------------------------------------------------------------------------- /compress_field/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = "0.10.2" 3 | 4 | from .models import ZipFileField 5 | from .fieldfiles import ZipCompressFieldFile 6 | -------------------------------------------------------------------------------- /compress_field/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | try: 5 | from celery.task import task 6 | except ImportError: 7 | task = None 8 | 9 | import os 10 | 11 | try: 12 | from django.conf import settings 13 | except ImportError: 14 | settings = {} 15 | 16 | from django.db.models.fields.files import FieldFile 17 | 18 | 19 | FILE_COMPRESS_DELETE_OLD_FILE = getattr(settings, "FILE_COMPRESS_DELETE_OLD_FILE", True) 20 | 21 | 22 | class CompressFieldFile(FieldFile): 23 | compress_ext = None 24 | 25 | def _is_compressed(self): 26 | basename, ext = os.path.splitext(self.name) 27 | compress_ext = "." + self.compress_ext 28 | return compress_ext == ext 29 | 30 | is_compressed = property(_is_compressed) 31 | 32 | def _base_name(self): 33 | return os.path.basename(self.file.name) 34 | 35 | base_name = property(_base_name) 36 | 37 | def _compress_name(self): 38 | if not hasattr(self, "_avaliable_compress_name"): 39 | basename, ext = os.path.splitext(self.name) 40 | compress_name = basename + ("." + self.compress_ext) 41 | self._avaliable_compress_name = self.storage.get_available_name( 42 | compress_name 43 | ) 44 | return self._avaliable_compress_name 45 | 46 | compress_name = property(_compress_name) 47 | 48 | def _compress_full_name(self): 49 | return os.path.join(self.storage.location, self.compress_name) 50 | 51 | compress_full_name = property(_compress_full_name) 52 | 53 | def get_available_name(self): 54 | return self.storage.get_available_name(self.compress_name) 55 | 56 | def compress_content(self): 57 | """ 58 | Method to change for new implementations 59 | """ 60 | raise NotImplementedError() 61 | 62 | def _update_filefield_name(self, delete_old_file=True): 63 | # update field value 64 | old_name = self.name 65 | 66 | setattr(self.instance, self.field.name, self.compress_name) 67 | self.instance.save() 68 | 69 | if delete_old_file: 70 | self.file.close() 71 | self.storage.delete(old_name) 72 | 73 | def compress_wrapper(self, delete_old_file): 74 | self.compress_content() 75 | self._update_filefield_name(delete_old_file=delete_old_file) 76 | return True 77 | 78 | def compress(self, async_task=True, delete_old_file=FILE_COMPRESS_DELETE_OLD_FILE): 79 | if self.is_compressed: 80 | return "This file is alredy compress" 81 | 82 | if async_task and task: 83 | from .tasks import task_compress_wrapper 84 | 85 | wrapper = task_compress_wrapper.delay( 86 | self.instance, self.field.name, delete_old_file 87 | ) 88 | else: 89 | wrapper = CompressFieldFile.compress_wrapper(self, delete_old_file) 90 | 91 | return wrapper 92 | -------------------------------------------------------------------------------- /compress_field/fieldfiles.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | import zipfile 4 | import sys 5 | from .base import CompressFieldFile 6 | 7 | try: 8 | compression = zipfile.ZIP_DEFLATED 9 | except ImportError: 10 | compression = zipfile.ZIP_STORED 11 | 12 | 13 | class ZipCompressFieldFile(CompressFieldFile): 14 | compress_ext = "zip" 15 | 16 | def compress_content(self): 17 | compress_file_fullname = self.compress_full_name 18 | 19 | # to work with py2.6 or lower 20 | if sys.version_info < (2, 7): 21 | compress_file_fullname = open(compress_file_fullname, "w+b") 22 | 23 | if not zipfile.is_zipfile(self.file.name): 24 | ziped = zipfile.ZipFile( 25 | compress_file_fullname, "w", compression=compression 26 | ) 27 | ziped.write(self.file.name, self.base_name) 28 | ziped.close() 29 | return ziped 30 | -------------------------------------------------------------------------------- /compress_field/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | from .fieldfiles import ZipCompressFieldFile 4 | from django.db import models 5 | 6 | 7 | class ZipFileField(models.FileField): 8 | """ 9 | ZipField - auto zip content after file was saved on server 10 | """ 11 | 12 | attr_class = ZipCompressFieldFile 13 | 14 | def south_field_triple(self): 15 | try: 16 | from south.modelsinspector import introspector 17 | 18 | cls_name = "{0}.{1}".format( 19 | self.__class__.__module__, self.__class__.__name__ 20 | ) 21 | args, kwargs = introspector(self) 22 | return cls_name, args, kwargs 23 | except ImportError: 24 | pass 25 | 26 | 27 | try: 28 | from south.modelsinspector import add_introspection_rules 29 | 30 | add_introspection_rules([], ["^compress_field\.models\.(ZipFileField)"]) 31 | except ImportError: 32 | pass 33 | -------------------------------------------------------------------------------- /compress_field/tasks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | try: 5 | from celery.task import task 6 | except ImportError: 7 | task = None 8 | 9 | from django.core.cache import cache 10 | 11 | LOCK_EXPIRE = 60 * 5 12 | 13 | try: 14 | from django.conf import settings 15 | except ImportError: 16 | settings = {} 17 | 18 | FILE_COMPRESS_QUEUE = getattr(settings, "FILE_COMPRESS_QUEUE", "Celery") 19 | 20 | 21 | # cache.add fails if if the key already exists 22 | def acquire_lock(lock_id): 23 | return cache.add(lock_id, "true", LOCK_EXPIRE) 24 | 25 | 26 | # memcache delete is very slow, but we have to use it to take 27 | # advantage of using add() for atomic locking 28 | def release_lock(lock_id): 29 | return cache.delete(lock_id) 30 | 31 | 32 | @task(serializer="pickle", queue=FILE_COMPRESS_QUEUE) 33 | def task_compress_wrapper(instance, field, delete_old_file): 34 | lock_id = "{0}-io-lock-{1}".format(instance.__class__.__name__, instance.id) 35 | 36 | if acquire_lock(lock_id): 37 | instance_field = getattr(instance, field) 38 | instance_field.compress(async=False, delete_old_file=delete_old_file) 39 | release_lock(lock_id) 40 | return True 41 | 42 | # task is locked by IO 43 | print("IO Lock Task") 44 | return False 45 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def pytest_configure(): 4 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_test_settings') 5 | 6 | -------------------------------------------------------------------------------- /django_test_settings.py: -------------------------------------------------------------------------------- 1 | SECRET_KEY = 1 2 | 3 | INSTALLED_APPS = ( 4 | 'compress_field', 5 | 'example', 6 | ) 7 | 8 | DATABASES = { 9 | 'default': { 10 | 'ENGINE': 'django.db.backends.sqlite3', 11 | 'NAME': 'test.sqlite' 12 | } 13 | } 14 | 15 | TEMPLATES = [ 16 | { 17 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 18 | 'DIRS': [], 19 | 'APP_DIRS': True, 20 | }, 21 | ] 22 | 23 | ROOT_URLCONF = 'example.urls' 24 | 25 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 26 | -------------------------------------------------------------------------------- /example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valdergallo/django-compress-field/311560fcffc07299d8641adbd1c16b130aa894dc/example/__init__.py -------------------------------------------------------------------------------- /example/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ExampleConfig(AppConfig): 5 | name = 'example' 6 | -------------------------------------------------------------------------------- /example/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from core.models import MyContent 3 | 4 | 5 | class MyContentForm(forms.ModelForm): 6 | class Meta: 7 | model = MyContent 8 | fields = ('name', 'create_date', 'upload_file') 9 | -------------------------------------------------------------------------------- /example/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | from django.db import models 3 | from compress_field.models import ZipFileField 4 | 5 | 6 | class MyContent(models.Model): 7 | name = models.CharField(max_length=150) 8 | create_date = models.DateTimeField(auto_now=True) 9 | upload_file = ZipFileField(upload_to='mycontent/2014/06/03/') 10 | 11 | def __str__(self): 12 | return self.name 13 | -------------------------------------------------------------------------------- /example/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import * 2 | 3 | # Uncomment the next two lines to enable the admin: 4 | # from django.contrib import admin 5 | # admin.autodiscover() 6 | 7 | urlpatterns = patterns('', 8 | url(r'^example/', 'example.views.home'), 9 | 10 | # Uncomment the admin/doc line below to enable admin documentation: 11 | # (r'^admin/doc/', include('django.contrib.admindocs.urls')), 12 | 13 | # Uncomment the next line to enable the admin: 14 | # (r'^admin/', include(admin.site.urls)), 15 | ) 16 | -------------------------------------------------------------------------------- /example/views.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valdergallo/django-compress-field/311560fcffc07299d8641adbd1c16b130aa894dc/example/views.py -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = --cov=compress_field -vrsx 3 | testpaths = 4 | compress_field 5 | tests 6 | example -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | coverage==5.2 2 | pytest==5.4.3 3 | pytest-cov==2.10.0 4 | pytest-django==3.9.0 5 | pytest-runner==5.2 -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [tool:pytest] 2 | DJANGO_SETTINGS_MODULE=django_test_settings 3 | 4 | [metadata] 5 | description-file=README.md 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import sys 5 | from setuptools import setup 6 | from setuptools.command.test import test as TestCommand 7 | 8 | import compress_field 9 | 10 | install_requires = [ 11 | 'django>=1.2', 12 | ] 13 | 14 | tests_requires = [ 15 | 'django>=1.2', 16 | 'pytest==5.4.3', 17 | 'pytest-runner==5.2', 18 | 'pytest-django==3.9.0', 19 | 'pytest-cov==2.10.0', 20 | ] 21 | 22 | 23 | setup(name='django-compress-field', 24 | url='https://github.com/valdergallo/django-compress-field', 25 | download_url='https://github.com/valdergallo/django-compress-field/tarball/v%s/' % compress_field.__version__, 26 | author="valdergallo", 27 | author_email='valdergallo@gmail.com', 28 | keywords=['django', 'compress', 'field', 'zip', 'tar', 'gzip'], 29 | description='Automantic compress files after upload', 30 | license='FREEBSD', 31 | classifiers=[ 32 | 'Framework :: Django', 33 | 'Operating System :: OS Independent', 34 | 'Topic :: Utilities', 35 | 'Programming Language :: Python', 36 | 'Programming Language :: Python :: 2.6', 37 | 'Programming Language :: Python :: 2.7', 38 | 'Programming Language :: Python :: 3.6', 39 | 'Programming Language :: Python :: 3.7', 40 | ], 41 | include_package_data=True, 42 | version=compress_field.__version__, 43 | install_requires=install_requires, 44 | tests_require=tests_requires, 45 | setup_requires=['pytest-runner'], 46 | platforms='any', 47 | packages=[ 48 | 'compress_field' 49 | ] 50 | ) 51 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valdergallo/django-compress-field/311560fcffc07299d8641adbd1c16b130aa894dc/tests/__init__.py -------------------------------------------------------------------------------- /tests/data/text.txt: -------------------------------------------------------------------------------- 1 | Lorem ipsum 2 | =========== 3 | 4 | 5 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eu tortor enim. Vivamus sed risus nunc. Nullam viverra nibh et arcu vestibulum, quis congue justo ullamcorper. Mauris facilisis sem non augue faucibus, a dignissim magna hendrerit. Curabitur lacinia adipiscing porta. Donec tempor volutpat nisi eleifend hendrerit. Donec venenatis, nisi id condimentum auctor, libero arcu tincidunt dolor, ut volutpat nunc orci ac libero. Donec quis feugiat leo. Suspendisse porttitor vestibulum velit, et dignissim mauris volutpat sed. Phasellus sed ante sem. In convallis vel lacus quis consequat. 6 | 7 | Curabitur semper mi ac nisl interdum laoreet. Praesent rutrum augue sed tellus vehicula interdum. Morbi eu semper magna. Nam ut consectetur metus. Fusce tincidunt dui ut dolor sodales convallis. Etiam at metus eu tellus facilisis sagittis. Vivamus nec laoreet arcu, id posuere dui. Praesent aliquet pharetra mauris sit amet elementum. Aliquam eu massa vitae ipsum ornare interdum in sed magna. Quisque sed felis nec turpis ornare semper vitae ac velit. 8 | 9 | Donec nec mi interdum, sollicitudin mauris vitae, viverra tortor. In nisl diam, pretium eu porta nec, lacinia sed velit. Nulla facilisi. Proin congue dui eget leo aliquam commodo. Ut sit amet pulvinar metus. Vivamus vitae varius mi, vitae laoreet ligula. Duis rhoncus euismod nunc iaculis iaculis. Aenean ac tellus erat. Sed tortor nunc, egestas nec tellus in, mattis pharetra libero. Etiam et egestas ipsum. Etiam lobortis dui metus. Curabitur neque neque, vestibulum vel ullamcorper vel, varius id tortor. Nunc mattis porttitor justo, et consectetur sem dapibus quis. 10 | 11 | Praesent ac vestibulum neque. Proin a eros id orci pretium viverra. Morbi et erat egestas, cursus neque in, tincidunt urna. Praesent faucibus non elit in lobortis. Fusce arcu tortor, consequat eget ligula eget, bibendum consectetur orci. In at felis sollicitudin, suscipit risus vitae, cursus risus. Vestibulum mauris lorem, euismod non mi nec, egestas sodales nisi. Morbi purus leo, molestie eu vehicula ac, molestie non neque. Sed mattis, turpis in semper gravida, urna diam lacinia turpis, id molestie odio metus suscipit enim. Nam ac enim quis massa commodo consectetur. 12 | 13 | Suspendisse potenti. Fusce a justo aliquam, dictum mauris ac, pellentesque diam. Maecenas id odio mattis, malesuada elit in, dignissim sapien. Vestibulum in pretium enim. Duis pretium ligula sit amet purus rutrum posuere nec in leo. Maecenas posuere facilisis urna nec suscipit. Cras sed ante id neque rhoncus elementum eget id elit. Praesent in elit at libero vulputate iaculis. Phasellus molestie diam ut volutpat condimentum. Nam tristique orci ut tempor mollis. Curabitur nisl quam, porta vel tempor sed, pharetra eget augue. Duis placerat neque sit amet arcu dignissim auctor. Donec viverra tincidunt enim a rutrum. 14 | -------------------------------------------------------------------------------- /tests/test_models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, unicode_literals 3 | import os 4 | import shutil 5 | from django.test import TestCase 6 | from example.models import MyContent 7 | from django.core.files.base import File 8 | from django.conf import settings 9 | try: 10 | ioError = WindowsError 11 | except NameError: 12 | ioError = IOError 13 | 14 | BASEDIR = os.path.dirname(__file__) 15 | FIXTURE = os.path.join(BASEDIR, 'data', 'text.txt') 16 | 17 | UPLOADED_FILE_FULL_PATH = os.path.join( 18 | settings.MEDIA_ROOT, 'mycontent/2014/06/03/', 'test_fixture.txt') 19 | UPLOADED_ZIPFILE_FULL_PATH = os.path.join( 20 | settings.MEDIA_ROOT, 'mycontent/2014/06/03/', 'test_fixture.zip') 21 | 22 | UPLOADED_FILE_PATH = os.path.join('mycontent/2014/06/03/', 'test_fixture.txt') 23 | UPLOADED_ZIPFILE_PATH = os.path.join( 24 | 'mycontent/2014/06/03/', 'test_fixture.zip') 25 | UPLOADED_ZIPFILE2_PATH = os.path.join( 26 | 'mycontent/2014/06/03/', 'test_fixture_1.zip') 27 | 28 | 29 | class TestCompressTestCase(TestCase): 30 | 31 | def setUp(self): 32 | self.dummyfile = File(open(FIXTURE, 'r'), name='test_fixture.txt') 33 | 34 | def tearDown(self): 35 | shutil.rmtree(os.path.join(settings.MEDIA_ROOT, 36 | 'mycontent'), ignore_errors=True) 37 | 38 | def create_my_content(self): 39 | my_content = MyContent() 40 | my_content.name = 'test' 41 | my_content.upload_file = self.dummyfile 42 | my_content.save() 43 | return my_content 44 | 45 | def test_save_file_on_model(self): 46 | my_content = self.create_my_content() 47 | self.assertEqual(my_content.upload_file.name, UPLOADED_FILE_PATH) 48 | 49 | def test_save_zipfile_on_model(self): 50 | my_content = self.create_my_content() 51 | my_content.upload_file.compress() 52 | 53 | self.assertEqual(my_content.upload_file.name, UPLOADED_ZIPFILE_PATH) 54 | 55 | def test_is_compress_has_updated_register(self): 56 | my_content = self.create_my_content() 57 | my_content.upload_file.compress() 58 | 59 | my_content = MyContent.objects.get(id=my_content.id) 60 | self.assertEqual(my_content.upload_file.name, UPLOADED_ZIPFILE_PATH) 61 | 62 | def test_if_is_compressed_must_return_true(self): 63 | my_content = self.create_my_content() 64 | my_content.upload_file.compress() 65 | self.assertTrue(my_content.upload_file.is_compressed) 66 | 67 | def test_if_is_compressed_must_return_false(self): 68 | my_content = self.create_my_content() 69 | self.assertFalse(my_content.upload_file.is_compressed) 70 | 71 | def test_compress_name(self): 72 | my_content = self.create_my_content() 73 | self.assertEqual(my_content.upload_file.compress_name, 74 | UPLOADED_ZIPFILE_PATH) 75 | 76 | def test_if_compress_delete_file_uncompress(self): 77 | my_content = self.create_my_content() 78 | my_content.upload_file.compress() 79 | 80 | self.assertFalse(os.path.exists(UPLOADED_FILE_PATH)) 81 | 82 | def test_if_zip_is_not_override_names(self): 83 | my_content = self.create_my_content() 84 | my_content.upload_file.compress() 85 | self.assertEqual(my_content.upload_file.name, UPLOADED_ZIPFILE_PATH) 86 | 87 | my_content2 = self.create_my_content() 88 | my_content2.upload_file.compress() 89 | self.assertNotEqual(my_content2.upload_file.name.replace( 90 | '\\', '/'), UPLOADED_ZIPFILE_PATH) 91 | --------------------------------------------------------------------------------