BOMBOLOM.COM

(django) Admin Widgets vrs User Forms (AdminDateWidget)

Posted by José Lopes.

The Django administration has a couple of widgets that can be used in an user's form.

This is mentioned on the Django's documentation ( Media and Django Admin) but no practical example is given. Searching the web we find that a lot of people have tried to use them without success, specially the AdminDateWidget that provides a nice popup calendar for choosing the date.

This post show how one can use this widget, and explains why there have been so many missing attemps.

The information here presented is also valid for the AdminTimeWidget. Other widgets may be subject of future posts.

Lets start by the cause of the unsuccess in making the widget work, that at the same time is the solution.

If we check the admin page source we find the following line on the header:

<script type="text/javascript" src="../../../jsi18n/"></script>

This line executes the jsi18n script that, in a simple way to say it, calls a javascript code responsible for the substitution of specific divs (on the case of the AdminDateWidget is the div declared on the input as class="vDateField") by nice visual outputs like the button for the popup calendar. This code changes depending if our project uses or not internationalization.

The problem is that nothing happens even when we are including this line in our forms. This is due to fact that this script needs the admin authentication to work. More frustrating is that we get no error message neither any authentication request.

The solution is to force the running of the script content in our forms.

Following Django's philosophie (the famous DRY) we can create a file in our project media, to be more accurate at media/js/, with for instance the name adminwidgets.js. Inside this file we'll write the output of http://127.0.0.1:8000/admin/jsi18n/ that will be:

function gettext(msgid) { return msgid; }
function ngettext(singular, plural, count) { return (count == 1) ? singular : plural; }
function gettext_noop(msgid) { return msgid; }

function interpolate(fmt, obj, named) {
  if (named) {
    return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
  } else {
    return fmt.replace(/%s/g, function(match){return String(obj.shift())});
  }
}

If your project has no internationalization.

var catalog = new Array();

function pluralidx(count) { return (count == 1) ? 0 : 1; }


function gettext(msgid) {
  var value = catalog[msgid];
  if (typeof(value) == 'undefined') {
    return msgid;
  } else {
    return (typeof(value) == 'string') ? value : value[0];
  }
}

function ngettext(singular, plural, count) {
  value = catalog[singular];
  if (typeof(value) == 'undefined') {
    return (count == 1) ? singular : plural;
  } else {
    return value[pluralidx(count)];
  }
}

function gettext_noop(msgid) { return msgid; }

function interpolate(fmt, obj, named) {
  if (named) {
    return fmt.replace(/%\(\w+\)s/g, function(match){return 
String(obj[match.slice(2,-2)])});
  } else {
    return fmt.replace(/%s/g, function(match){return String(obj.shift())});
  }
}

If your project has internationalization. Despite the difference I would suggest to use the second so that you may include the internationalization in the future without more problems.

Saying this, in order to have a popup calendar working on a form one just have to follow the next two points:

1. Create the Form

Lets assume that we have a form from a model. The model has a field (mydatefield) of the date type. The form code should be something like:

    from django import forms
(1) from my_project.myuserapp.models import MyUserModel
(2) from django.contrib.admin.widgets import AdminDateWidget

    class MyUserForm(forms.ModelForm):
(3)     mydatefield = forms.DateField(widget=AdminDateWidget)
        class Meta:
            model = MyUserModel

Explaining the code:
  (1)  importing our model.
  (2)  importing the AdminDateWidget widget.
  (3)  we overwrite the mydatefield model definitions to be a date field with the AdminDateWidget widget.

2. Create the form template

On the form template, that we can call myformtemplate.html, we must place the following lines on the header:

(1) <link rel="stylesheet" type="text/css" href="/media/css/widgets.css"/></link>
(2) <script type="text/javascript" src="/admin_media/js/core.js"></script>
(3) <script type="text/javascript" src="/media/js/adminwidgets.js"></script>
(4) {{ form.media }}

Explaining the code:
  (1)  points to a copy of the file with the same name, originally at django/contrib/admin/media/css, that defines the calendar layout (with a copy we can always change at will);
  (2)  admin javascript;
  (3)  our javascript file that we created to solve the jsi18n question;
  (4)  includes the AdminDateWidget javascripts pre-defined, more precicely the calendar.js and the DateTimeShortcuts.js

We're done and the calendar is working!
Until now I had found nothing documenting this so I hope it may be useful.

2008.09.07 | There's more... | Comments 1 | Tags , ,
Comment by Chrystiano Araujo on Tue Jan 13 17:11:34 2009
OI..
Parabens pelo post. Procurei e nao encontrei nada parecido.

Sendo que infelizmente, nao funcionou comigo. Sabe o que posso fazer?
ou me enviar o seu codigo de exemplo.

Deixe a sua mensagem:

Nome:


E-mail:


URL:


Comment:

Secret number

To send you comment you must insert the "secret number" on the box


Made with PyBlosxom | Add to Google