Skip to content

Commit

Permalink
Make the module independent of a User model
Browse files Browse the repository at this point in the history
* Some projects don't have a User model, and django-chunked-upload should work for them too.
* Created a base model (`BaseChunkedUpload`), which doesn't have a FK to the User model. This model will always be abstract. The old `ChunkedUpload` remains unchanged, with a FK to the User model. If it fits your needs, use it making it not abstract on your settings.
* If an md5 doesn't match, the request will still fail, but won't mark the chunked upload as FAILED (I removed the FAILED status completely).

Bumped up version to `1.1.0`.
  • Loading branch information
juliomalegria committed Sep 15, 2015
1 parent 1ce302a commit 5f4109b
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 24 deletions.
10 changes: 5 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ If you want to see a very simple Django demo project using this module, please t
Installation
------------

**NEW:** This module is now in PyPI! Install via pip:
Install via pip:

::

Expand Down Expand Up @@ -103,19 +103,19 @@ Add any of these variables into your project settings to override them.
``CHUNKED_UPLOAD_STORAGE_CLASS``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* Storage system (should be a class)
* Storage system (should be a class).
* Default: ``None`` (use default storage system)

``CHUNKED_UPLOAD_ABSTRACT_MODEL``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* Boolean that defines if the ``ChunkedUpload`` model will be abstract or not (`what does abstract model mean? <https://docs.djangoproject.com/en/1.4/ref/models/options/#abstract>`__)
* Boolean that defines if the ``ChunkedUpload`` model will be abstract or not (`what does abstract model mean? <https://docs.djangoproject.com/en/1.4/ref/models/options/#abstract>`__).
* Default: ``True``

``CHUNKED_UPLOAD_ENCODER``
~~~~~~~~~~~~~~~~~~~~~~~~~~

* Function used to encode response data. Receives a dict and returns a string
* Function used to encode response data. Receives a dict and returns a string.
* Default: ``DjangoJSONEncoder().encode``

``CHUNKED_UPLOAD_CONTENT_TYPE``
Expand All @@ -132,7 +132,7 @@ Add any of these variables into your project settings to override them.
``CHUNKED_UPLOAD_MAX_BYTES``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* Max amount of data (in bytes) that can be uploaded. ``None`` means no limit
* Max amount of data (in bytes) that can be uploaded. ``None`` means no limit.
* Default: ``None``

Support
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.5
1.1.0
2 changes: 0 additions & 2 deletions chunked_upload/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ class http_status:

UPLOADING = 1
COMPLETE = 2
FAILED = 3

CHUNKED_UPLOAD_CHOICES = (
(UPLOADING, _('Uploading')),
(COMPLETE, _('Complete')),
(FAILED, _('Failed')),
)
23 changes: 20 additions & 3 deletions chunked_upload/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@ def generate_filename(instance, filename):
return time.strftime(filename)


class ChunkedUpload(models.Model):
class BaseChunkedUpload(models.Model):
"""
Base chunked upload model. This model is abstract (doesn't create a table
in the database).
Inherit from this model to implement your own.
"""

upload_id = models.CharField(max_length=32, unique=True, editable=False,
default=generate_upload_id)
file = models.FileField(max_length=255, upload_to=generate_filename,
storage=STORAGE)
filename = models.CharField(max_length=255)
user = models.ForeignKey(AUTH_USER_MODEL, related_name='chunked_uploads')
offset = models.BigIntegerField(default=0)
created_on = models.DateTimeField(auto_now_add=True)
status = models.PositiveSmallIntegerField(choices=CHUNKED_UPLOAD_CHOICES,
Expand All @@ -55,7 +60,7 @@ def md5(self):

def delete(self, delete_file=True, *args, **kwargs):
storage, path = self.file.storage, self.file.path
super(ChunkedUpload, self).delete(*args, **kwargs)
super(BaseChunkedUpload, self).delete(*args, **kwargs)
if delete_file:
storage.delete(path)

Expand Down Expand Up @@ -96,5 +101,17 @@ def get_uploaded_file(self):
return UploadedFile(file=self.file, name=self.filename,
size=self.offset)

class Meta:
abstract = True


class ChunkedUpload(BaseChunkedUpload):
"""
Default chunked upload model.
To use it, set CHUNKED_UPLOAD_ABSTRACT_MODEL as True in your settings.
"""

user = models.ForeignKey(AUTH_USER_MODEL, related_name='chunked_uploads')

class Meta:
abstract = ABSTRACT_MODEL
22 changes: 9 additions & 13 deletions chunked_upload/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .settings import MAX_BYTES
from .models import ChunkedUpload
from .response import Response
from .constants import http_status, COMPLETE, FAILED
from .constants import http_status, COMPLETE
from .exceptions import ChunkedUploadError


Expand All @@ -23,17 +23,17 @@ class ChunkedUploadBaseView(View):
def get_queryset(self, request):
"""
Get (and filter) ChunkedUpload queryset.
By default, user can only continue uploading his own uploads.
By default, users can only continue uploading their own uploads.
"""
queryset = self.model.objects.all()
if request.user.is_authenticated():
if hasattr(request, 'user') and request.user.is_authenticated():
queryset = queryset.filter(user=request.user)
return queryset

def validate(self, request):
"""
Placeholder method to define extra validation. Must raise
ChunkedUploadError if validation fails.
Placeholder method to define extra validation.
Must raise ChunkedUploadError if validation fails.
"""

def get_response_data(self, chunked_upload, request):
Expand Down Expand Up @@ -75,7 +75,7 @@ def check_permissions(self, request):
"""
Grants permission to start/continue an upload based on the request.
"""
if not request.user.is_authenticated():
if hasattr(request, 'user') and not request.user.is_authenticated():
raise ChunkedUploadError(
status=http_status.HTTP_403_FORBIDDEN,
detail='Authentication credentials were not provided'
Expand Down Expand Up @@ -150,9 +150,6 @@ def is_valid_chunked_upload(self, chunked_upload):
if chunked_upload.status == COMPLETE:
raise ChunkedUploadError(status=http_status.HTTP_400_BAD_REQUEST,
detail=error_msg % 'complete')
if chunked_upload.status == FAILED:
raise ChunkedUploadError(status=http_status.HTTP_400_BAD_REQUEST,
detail=error_msg % 'failed')

def get_response_data(self, chunked_upload, request):
"""
Expand All @@ -177,8 +174,9 @@ def _post(self, request, *args, **kwargs):
upload_id=upload_id)
self.is_valid_chunked_upload(chunked_upload)
else:
user = request.user if request.user.is_authenticated() else None
attrs = {'user': user, 'filename': chunk.name}
attrs = {'filename': chunk.name}
if hasattr(request, 'user') and request.user.is_authenticated():
attrs['user'] = request.user
attrs.update(self.get_extra_attrs(request))
chunked_upload = self.create_chunked_upload(save=False, **attrs)

Expand Down Expand Up @@ -250,8 +248,6 @@ def md5_check(self, chunked_upload, md5):
Verify if md5 checksum sent by client matches generated md5.
"""
if chunked_upload.md5 != md5:
chunked_upload.status = FAILED
self._save(chunked_upload)
raise ChunkedUploadError(status=http_status.HTTP_400_BAD_REQUEST,
detail='md5 checksum does not match')

Expand Down

0 comments on commit 5f4109b

Please sign in to comment.