Using Django time/date widgets in custom form
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: Riding Sky Waves v001
--
Chapters
00:00 Using Django Time/Date Widgets In Custom Form
00:47 Accepted Answer Score 169
03:25 Answer 2 Score 68
03:34 Answer 3 Score 12
03:58 Answer 4 Score 14
04:30 Thank you
--
Full question
https://stackoverflow.com/questions/3860...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #django
#avk47
ACCEPTED ANSWER
Score 169
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:
Define your own
ModelFormsubclass for your model (best to put it in forms.py in your app), and tell it to use theAdminDateWidget/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()Change your URLconf to pass
'form_class': ProductForminstead of'model': Productto the genericcreate_objectview (that'll meanfrom my_app.forms import ProductForminstead offrom my_app.models import Product, of course).In the head of your template, include
{{ form.media }}to output the links to the Javascript files.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.
ANSWER 2
Score 68
As the solution is hackish, I think using your own date/time widget with some JavaScript is more feasible.
ANSWER 3
Score 14
I find myself referencing this post a lot, and found that the documentation defines a slightly less hacky way to override default widgets.
(No need to override the ModelForm's __init__ method)
However, you still need to wire your JS and CSS appropriately as Carl mentions.
forms.py
from django import forms
from my_app.models import Product
from django.contrib.admin import widgets                                       
class ProductForm(forms.ModelForm):
    mydate = forms.DateField(widget=widgets.AdminDateWidget)
    mytime = forms.TimeField(widget=widgets.AdminTimeWidget)
    mydatetime = forms.SplitDateTimeField(widget=widgets.AdminSplitDateTime)
    class Meta:
        model = Product
Reference Field Types to find the default form fields.
ANSWER 4
Score 12
Yep, I ended up overriding the /admin/jsi18n/ url.
Here's what I added in my urls.py. Make sure it's above the /admin/ url
    (r'^admin/jsi18n', i18n_javascript),
And here is the i18n_javascript function I created.
from django.contrib import admin
def i18n_javascript(request):
  return admin.site.i18n_javascript(request)