jQuery sugar sweetens URLFields on Django admin forms

You know you want it. You know you need it. A way for someone to preview a URL entered into a URLField on a Django admin form. Oh, and you gotta deal with that nasty leading and trailing white space that inevitably creeps in from copying/pasting.  It appears that whitespace trimming will be added to the Django code in some form — see ticket #6362 — but currently there’s no easy way to add this to URLFields because URL verification (with the verify_exists option) happens before any custom validation.

Prerequisites

The JavaScript functions

Add this code to a new or existing JavaScript file (customized as necessary):

function addURLFormFeatures() {
    // add features to a form that has URLFields.
    addURLPreviewLinks();
    trimURLsOnSubmit();
}

function trimURLsOnSubmit() {
    // trims leading and trailing whitespace from URL form input values
    // on form submission.
    $('#content-main form').submit(trimURLs);
}

function trimURLs() {
    // trims leading and trailing whitespace from URL input values.
    $('input.vURLField').each(function() {
	    var trimmed = jQuery.trim($(this).val());
	    $(this).val(trimmed);
	});
}

function addURLPreviewLinks() {
    // adds "preview" links next to URL form inputs.
    var event = 'return URLPreview(this);';
    var title = 'Preview this URL';
    var style = 'margin-left: 0.5em';
    var image = new Image(16, 16);
    image.src = '/django/local/media/images/icons/link_go.png';
    image.alt = title;
    var link = '<a href="#" onclick="' + event + '" 
	                        style="' + style + '" 
                            title="' + title + '"><img 
                        src="' + image.src + '" 
                        alt="' + image.alt + '" 
	                    height="' + image.height + '" 
                        width="' + image.width + '" 
	                    border="0"/></a>';
    $('input.vURLField').after(link);
}

function URLPreview(link) {
    // executes on clicking "preview" link added by addURLPreviewLinks().
    var windowName = 'previewLink';
    var windowOptions = 'width=640,height=480,location=no,menubar=no,scrollbars=yes,resizable=yes,toolbar=no,status=no,left=100,top=100';
    var field = $(link).prev();            // field = URL input form control
    var url = jQuery.trim($(field).val());
    if (url) {
    	var url_re = new RegExp(/^https?:///);
    	if (url_re.test(url)) {
    	    window.open(url, windowName, windowOptions);
    	} else {
            alert('URL must begin with "http://" or "https://".nnPlease correct and retry.');
            $(field).select();
    	}
    } else {
    	alert('Please enter a URL and retry.');
    	$(field).focus();
    }
    return false;
}

Tying it together

If you just want to apply this customization to the admin forms for a single model, you can add a media definition to the ModelAdmin class for that model (thanks to Uzi for pointing this out) :

class MyModelAdmin(admin.ModelAdmin):
    class Media:
        js = ('/path/to/jquery.js', '/path/to/custom/script.js')

You’d also need another script to do the initialization (I didn’t include that in the custom script above):

$(function() {
    addURLFormFeatures();
});

If, however, you want this functionality to be available across all model admin forms, you can override the admin/change_form.html template:

{% extends "admin/change_form.html" %}
{% block extrahead %}
    {{ block.super }}
    <script src="/path/to/jquery.js" type="text/javascript"></script>
    <script src="/path/to/custom/script.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function() {
            addURLFormFeatures();
        });
    </script>
{% endblock %}

You could also replace the admin/change_form.html template to make this functionality globally available in Django’s admin, but you’d want to update the template with the new template from the distribution whenever you upgraded Django.

To Do

Adding a “browse” function as an alternative to copy/paste for entering URLs is left as an exercise for the reader.  You could also do more to check that the URL is at least “well-formed” before attempting to get it with the browser. The reason I only look for the https?:// prefix is for sanity (sure, you can add ftp://, etc.) because browsers will prepend http:// by default if no protocol is entered in a location, whereas Django’s built-in URL validation (using standard Python modules) isn’t going to do that, so users get confused. Otherwise, doing a full regex on the URL is sort of redundant, since if it’s a bad URL you ain’t gettin’ nothin’ anyway.  If you really want to be cool, figure out a way to create a custom option on URLField to toggle this functionality on/off (it might not be too hard).

Advertisements

, , , ,

  1. #1 by Uzi on December 9, 2008 - 8:01 am

    For “Tying it together”, you can probably just a ModelAdmin Media class:

    http://docs.djangoproject.com/en/dev/ref/contrib/admin/#modeladmin-media-definitions

    Don’t even need to mess with the template…

  2. #2 by David Chandek-Stark on December 9, 2008 - 2:59 pm

    You’re absolutely right. I lost track of how this was changed in the refactoring of Django’s admin. OTOH, if you want this functionality to apply across all models, as opposed to per-model, customizing the template is probably the easiest way to do it.

  1. Subclassing Django ModelAdmin « Fragments of Code