# in util/models.py or wherever import fields from django.contrib.admin import options, widgets from django.db.models.fields import CharField class RedirectedURLField(CharField): def __init__(self, verbose_name=None, name=None, **kwargs): kwargs['max_length'] = kwargs.get('max_length', 200) CharField.__init__(self, verbose_name, name, **kwargs) def formfield(self, **kwargs): defaults = {'form_class': fields.RedirectedURLField} defaults.update(kwargs) return super(RedirectedURLField, self).formfield(**defaults) options.FORMFIELD_FOR_DBFIELD_DEFAULTS[RedirectedURLField] = {'widget': widgets.AdminURLFieldWidget} # in util/fields.py or wherever import urllib2 import urlparse from django.forms.fields import url_re, RegexField, URL_VALIDATOR_USER_AGENT from django.utils.translation import ugettext_lazy as _ class RedirectedURLField(RegexField): default_error_messages = { 'invalid': _(u'Enter a valid URL.'), 'invalid_link': _(u'This URL appears to be a broken link.'), } # http://www.diveintopython.org/http_web_services/redirects.html class RedirectHandler(urllib2.HTTPRedirectHandler): def http_error_301(self, req, fp, code, msg, headers): result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers) result.status = code return result def __init__(self, max_length=None, min_length=None, validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs): super(RedirectedURLField, self).__init__(url_re, max_length, min_length, *args, **kwargs) self.user_agent = validator_user_agent def clean(self, value): # If no URL scheme given, assume http:// if value and '://' not in value: value = u'http://%s' % value # If no URL path given, assume / if value and not urlparse.urlsplit(value)[2]: value += '/' value = super(RedirectedURLField, self).clean(value) if value == u'': return value headers = { "Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5", "Accept-Language": "en-us,en;q=0.5", "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "Connection": "close", "User-Agent": self.user_agent, } opener = urllib2.build_opener(self.RedirectHandler()) try: request = urllib2.Request(value, None, headers) response = opener.open(request) if 301 == getattr(response, 'status', None): value = response.geturl() except ValueError: raise ValidationError(self.error_messages['invalid']) except: # urllib2.URLError, httplib.InvalidURL, etc. raise ValidationError(self.error_messages['invalid_link']) return value