Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Django time/date widgets in custom form

Tags:

python

django

How can I use the nifty JavaScript date and time widgets that the default admin uses with my custom view?

I have looked through the Django forms documentation, and it briefly mentions django.contrib.admin.widgets, but I don't know how to use it?

Here is my template that I want it applied on.

<form action="." method="POST">     <table>         {% for f in form %}            <tr> <td> {{ f.name }}</td> <td>{{ f }}</td> </tr>         {% endfor %}     </table>     <input type="submit" name="submit" value="Add Product"> </form> 

Also, I think it should be noted that I haven't really written a view up myself for this form, I am using a generic view. Here is the entry from the url.py:

(r'^admin/products/add/$', create_object, {'model': Product, 'post_save_redirect': ''}), 

And I am relevantly new to the whole Django/MVC/MTV thing, so please go easy...

like image 792
Josh Hunt Avatar asked Sep 01 '08 23:09

Josh Hunt


People also ask

What a widget is Django how can you use it HTML input elements?

A widget is Django's representation of an HTML input element. The widget handles the rendering of the HTML, and the extraction of data from a GET/POST dictionary that corresponds to the widget. The HTML generated by the built-in widgets uses HTML5 syntax, targeting <! DOCTYPE html> .

What is the significance of introducing custom widgets in Django?

The widget in Django will handle the rendering of the HTML elements and extract data from a POST/GET dictionary that corresponds to the same widget. Whenever we specify a field on a Django form, Django uses some default widget appropriate to the data type to be displayed.

What is Attrs Django?

attrs . A dictionary containing HTML attributes to be set on the rendered DateInput and TimeInput widgets, respectively. If these attributes aren't set, Widget. attrs is used instead.


1 Answers

The growing complexity of this answer over time, and the many hacks required, probably ought to caution you against doing this at all. It's relying on undocumented internal implementation details of the admin, is likely to break again in future versions of Django, and is no easier to implement than just finding another JS calendar widget and using that.

That said, here's what you have to do if you're determined to make this work:

  1. Define your own ModelForm subclass for your model (best to put it in forms.py in your app), and tell it to use the AdminDateWidget / AdminTimeWidget / AdminSplitDateTime (replace 'mydate' etc with the proper field names from your model):

     from django import forms  from my_app.models import Product  from django.contrib.admin import widgets                                          class ProductForm(forms.ModelForm):      class Meta:          model = Product      def __init__(self, *args, **kwargs):          super(ProductForm, self).__init__(*args, **kwargs)          self.fields['mydate'].widget = widgets.AdminDateWidget()          self.fields['mytime'].widget = widgets.AdminTimeWidget()          self.fields['mydatetime'].widget = widgets.AdminSplitDateTime() 
  2. Change your URLconf to pass 'form_class': ProductForm instead of 'model': Product to the generic create_object view (that'll mean from my_app.forms import ProductForm instead of from my_app.models import Product, of course).

  3. In the head of your template, include {{ form.media }} to output the links to the Javascript files.

  4. And the hacky part: the admin date/time widgets presume that the i18n JS stuff has been loaded, and also require core.js, but don't provide either one automatically. So in your template above {{ form.media }} you'll need:

     <script type="text/javascript" src="/my_admin/jsi18n/"></script>  <script type="text/javascript" src="/media/admin/js/core.js"></script> 

You may also wish to use the following admin CSS (thanks Alex for mentioning this):

    <link rel="stylesheet" type="text/css" href="/media/admin/css/forms.css"/>     <link rel="stylesheet" type="text/css" href="/media/admin/css/base.css"/>     <link rel="stylesheet" type="text/css" href="/media/admin/css/global.css"/>     <link rel="stylesheet" type="text/css" href="/media/admin/css/widgets.css"/> 

This implies that Django's admin media (ADMIN_MEDIA_PREFIX) is at /media/admin/ - you can change that for your setup. Ideally you'd use a context processor to pass this values to your template instead of hardcoding it, but that's beyond the scope of this question.

This also requires that the URL /my_admin/jsi18n/ be manually wired up to the django.views.i18n.javascript_catalog view (or null_javascript_catalog if you aren't using I18N). You have to do this yourself instead of going through the admin application so it's accessible regardless of whether you're logged into the admin (thanks Jeremy for pointing this out). Sample code for your URLconf:

(r'^my_admin/jsi18n', 'django.views.i18n.javascript_catalog'), 

Lastly, if you are using Django 1.2 or later, you need some additional code in your template to help the widgets find their media:

{% load adminmedia %} /* At the top of the template. */  /* In the head section of the template. */ <script type="text/javascript"> window.__admin_media_prefix__ = "{% filter escapejs %}{% admin_media_prefix %}{% endfilter %}"; </script> 

Thanks lupefiasco for this addition.

like image 57
Carl Meyer Avatar answered Sep 20 '22 19:09

Carl Meyer