Login

Load File From URL Widget

Author:
bryanhelmig
Posted:
August 21, 2011
Language:
Python
Version:
1.3
Score:
2 (after 2 ratings)

Rather simple usage, modelforms/in the admin:

class CustomAdminForm(forms.ModelForm):
    class Meta:
        model = Something

        widgets = {
            'image': URLFileInput(default_exts=[".png", ".gif", ".jpg"]),
        }

class SomethingAdmin(admin.ModelAdmin):
    form = CustomAdminForm

admin.site.register(Something, SomethingAdmin)

Basically, this will pull the image from the URL instead of only pulling it from your harddrive for upload.

Also accepts optional default_exts argument which limits the file types. Defaults to images.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import os, urllib, urllib2, mimetypes
from urlparse import urlparse

from django.forms.widgets import FileInput, CheckboxInput, TextInput

from django.core.validators import URLValidator
from django.core.exceptions import ValidationError

from django.core.files import File
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.files.temp import NamedTemporaryFile

from django.utils.html import escape, conditional_escape
from django.utils.translation import ugettext, ugettext_lazy
from django.utils.encoding import StrAndUnicode, force_unicode
from django.utils.safestring import mark_safe

FILE_INPUT_CONTRADICTION = object()

class URLFileInput(FileInput):
    initial_text = ugettext_lazy('Currently')
    input_text = ugettext_lazy('Change')
    clear_checkbox_label = ugettext_lazy('Clear')
    url_text_label = ugettext_lazy('From URL')

    template_with_initial = u'<p class="file-upload">%(initial_text)s: %(initial)s %(clear_template)s<br />%(url_template)s<br />%(input_text)s: %(input)s</p>'
    template_only_url = u'<p class="file-upload">%(url_template)s<br />%(input_text)s: %(input)s</p>'
    
    template_with_clear = u'<span class="clearable-file-input">%(clear)s <label for="%(clear_checkbox_id)s">%(clear_checkbox_label)s</label></span>'
    template_with_url = u'%(url_text_label)s: %(url)s'

    def clear_checkbox_name(self, name):
        """
        Given the name of the file input, return the name of the clear checkbox
        input.
        """
        return name + '-clear'

    def clear_checkbox_id(self, name):
        """
        Given the name of the clear checkbox input, return the HTML id for it.
        """
        return name + '_id'

    def url_text_name(self, name):
        """
        Given the name of the file input, return the name of the url text
        input.
        """
        return name + '-url-clear'

    def url_text_id(self, name):
        """
        Given the name of the url text input, return the HTML id for it.
        """
        return name + '_url_id'

    def render(self, name, value, attrs=None):
        substitutions = {
            'initial_text': self.initial_text,
            'input_text': self.input_text,
            'clear_template': '',
            'clear_checkbox_label': self.clear_checkbox_label,
            'url_text_label': self.url_text_label,
        }
        
        template = self.template_only_url
        substitutions['input'] = super(URLFileInput, self).render(name, value, attrs)

        if value and hasattr(value, "url"):
            template = self.template_with_initial
            substitutions['initial'] = (u'<a href="%s">%s</a>'
                                        % (escape(value.url),
                                           escape(force_unicode(value))))
            if not self.is_required:
                checkbox_name = self.clear_checkbox_name(name)
                checkbox_id = self.clear_checkbox_id(checkbox_name)
                substitutions['clear_checkbox_name'] = conditional_escape(checkbox_name)
                substitutions['clear_checkbox_id'] = conditional_escape(checkbox_id)
                substitutions['clear'] = CheckboxInput().render(checkbox_name, False, attrs={'id': checkbox_id})
                substitutions['clear_template'] = self.template_with_clear % substitutions
        
        text_name = self.url_text_name(name)
        text_id = self.url_text_id(text_name)
        substitutions['url_text_name'] = conditional_escape(text_name)
        substitutions['url_text_id'] = conditional_escape(text_id)
        substitutions['url'] = TextInput().render(text_name, "", attrs={'id': text_id, "style":"width:400px"})
        substitutions['url_template'] = self.template_with_url % substitutions

        return mark_safe(template % substitutions)

    def value_from_datadict(self, data, files, name):
        upload = super(URLFileInput, self).value_from_datadict(data, files, name)
        if not self.is_required and CheckboxInput().value_from_datadict(
            data, files, self.clear_checkbox_name(name)):
            if upload:
                # If the user contradicts themselves (uploads a new file AND
                # checks the "clear" checkbox), we return a unique marker
                # object that FileField will turn into a ValidationError.
                return FILE_INPUT_CONTRADICTION
            # False signals to clear any existing value, as opposed to just None
            return False
        
        url = TextInput().value_from_datadict(data, files, 
                self.url_text_name(name))
        if url:
            validate = URLValidator(verify_exists=False)
            try:
                validate(url)
            except ValidationError, e:
                url = None
            
            parsed_url = urlparse(url)
            name = os.path.basename(parsed_url[2])
            ext = os.path.splitext(parsed_url[2])[1]
            
            if url and ext in [".png", ".gif", ".jpg"]:
                opener = urllib2.build_opener()
                opener.addheaders = [('User-agent', 'Mozilla/5.0')]
                ze_file = opener.open(url).read()
                upload = SimpleUploadedFile(name=name, content=ze_file, content_type=mimetypes.guess_type(name))
        
        return upload

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 3 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 3 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 10 months, 1 week ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 11 months ago
  5. Help text hyperlinks by sa2812 11 months, 3 weeks ago

Comments

ldd (on March 26, 2013):

Thanks. This is really useful, however the description mentions a default_exts argument which does not appear to be used anywhere. Conversely, the extensions accepted are hardcoded:

if url and ext in [".png", ".gif", ".jpg"]:

#

Please login first before commenting.