Usando los widgets de fecha / hora de Django en forma personalizada

¿Cómo puedo usar los ingeniosos widgets de fecha y hora de JavaScript que usa el administrador predeterminado con mi vista personalizada?

He revisado la documentación de los formularios de Django , y menciona brevemente django.contrib.admin.widgets, pero no sé cómo usarlo.

Aquí está mi plantilla que quiero que se aplique.

  {% for f in form %}  {% endfor %} 
{{ f.name }} {{ f }}

Además, creo que se debe tener en cuenta que no he escrito una vista para este formulario, estoy usando una vista genérica. Aquí está la entrada de la url.py:

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

Y soy muy nuevo en todo lo relacionado con Django / MVC / MTV, así que por favor, sea fácil …

La complejidad creciente de esta respuesta a lo largo del tiempo, y los muchos trucos requeridos, probablemente deberían advertirle de no hacer esto en absoluto. Confiar en los detalles de la implementación interna no documentada del administrador, es probable que vuelva a fallar en futuras versiones de Django, y no es más fácil de implementar que solo encontrar otro widget de calendario JS y usarlo.

Dicho esto, esto es lo que tienes que hacer si estás decidido a hacer que esto funcione:

  1. Defina su propia subclase ModelForm para su modelo (lo mejor es ponerlo en forms.py en su aplicación) y dígale que use AdminDateWidget / AdminTimeWidget / AdminSplitDateTime (reemplace ‘mydate’, etc. con los nombres de campo adecuados de su modelo):

     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. Cambie su URLconf para pasar ‘form_class’: ProductForm en lugar de ‘model’: Producto a la vista genérica create_object (que significará “from my_app.forms import ProductForm” en lugar de “from my_app.models import Product”, por supuesto).

  3. En el encabezado de su plantilla, incluya {{form.media}} para generar los enlaces a los archivos de Javascript.

  4. Y la parte intrincada: los widgets de fecha / hora de administración suponen que se ha cargado el i18n JS, y también requieren core.js, pero no proporcionan ninguno de ellos automáticamente. Así que en la plantilla de arriba {{form.media}} necesitarás:

       

    También puede usar el siguiente CSS de administración (gracias Alex por mencionarlo):

         

Esto implica que los medios de administración de Django (ADMIN_MEDIA_PREFIX) están en / media / admin / – puedes cambiar eso para tu configuración. Lo ideal sería utilizar un procesador de contexto para pasar estos valores a su plantilla en lugar de codificarla, pero eso está fuera del scope de esta pregunta.

Esto también requiere que la URL / my_admin / jsi18n / esté conectada manualmente a la vista django.views.i18n.javascript_catalog (o null_javascript_catalog si no está usando I18N). Tienes que hacer esto tú mismo en lugar de ir a través de la aplicación de administración para que sea accesible independientemente de si has iniciado sesión en el administrador (gracias Jeremy por señalarlo). Código de muestra para su URLconf:

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

Por último, si está utilizando Django 1.2 o posterior, necesita un código adicional en su plantilla para ayudar a los widgets a encontrar sus medios:

 {% load adminmedia %} /* At the top of the template. */ /* In the head section of the template. */  

Gracias lupefiasco por esta adición.

Como la solución es pirata, creo que es más factible usar tu propio widget de fecha / hora con algo de JavaScript.

Sí, terminé reemplazando el / admin / jsi18n / url.

Esto es lo que agregué en mi urls.py. Asegúrate de que esté por encima de / admin / url

  (r'^admin/jsi18n', i18n_javascript), 

Y aquí está la función i18n_javascript que creé.

 from django.contrib import admin def i18n_javascript(request): return admin.site.i18n_javascript(request) 

Me estoy refiriendo mucho a esta publicación, y descubrí que la documentación define una forma un poco menos compleja de anular los widgets predeterminados.

( No es necesario anular el método __init__ de ModelForm )

Sin embargo, aún necesita conectar su JS y CSS de manera adecuada, como menciona Carl.

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 

Tipos de campos de referencia para encontrar los campos de formulario predeterminados.

A partir de Django 1.2 RC1, si está utilizando el truco de selección de la fecha de administración de Django, debe agregar lo siguiente a su plantilla, o verá que se hace referencia a la url del icono del calendario mediante “/ missing-admin-media-prefix / “.

 {% load adminmedia %} /* At the top of the template. */ /* In the head section of the template. */  

Mi código principal para la versión 1.4 (algunos nuevos y otros eliminados)

 {% block extrahead %}             {% endblock %} 

Complementando la respuesta de Carl Meyer, me gustaría comentar que debe colocar ese encabezado en un bloque válido (dentro del encabezado) dentro de su plantilla.

 {% block extra_head %}        {{ form.media }} {% endblock %} 

Lo siguiente también funcionará como último recurso si falla lo anterior.

 class PaymentsForm(forms.ModelForm): class Meta: model = Payments def __init__(self, *args, **kwargs): super(PaymentsForm, self).__init__(*args, **kwargs) self.fields['date'].widget = SelectDateWidget() 

Igual que

 class PaymentsForm(forms.ModelForm): date = forms.DateField(widget=SelectDateWidget()) class Meta: model = Payments 

ponga esto en su formulario.py from django.forms.extras.widgets import SelectDateWidget

¿Qué hay de simplemente asignar una clase a su widget y luego vincular esa clase con el datepicker JQuery?

Django forms.py:

 class MyForm(forms.ModelForm): class Meta: model = MyModel def __init__(self, *args, **kwargs): super(MyForm, self).__init__(*args, **kwargs) self.fields['my_date_field'].widget.attrs['class'] = 'datepicker' 

Y algunos JavaScript para la plantilla:

  $(".datepicker").datepicker(); 

Para Django> = 2.0

Nota: el uso de widgets de administrador para los campos de fecha y hora no es una buena idea, ya que las hojas de estilo de administrador pueden entrar en conflicto con las hojas de estilo de su sitio en caso de que esté utilizando bootstrap o cualquier otro marco CSS. Si está construyendo su sitio en bootstrap use mi widget bootstrap-datepicker django-bootstrap-datepicker-plus .

Paso 1: agregue la URL del javascript-catalog al archivo urls.py su proyecto (no de la aplicación).

 from django.views.i18n import JavaScriptCatalog urlpatterns = [ path('jsi18n', JavaScriptCatalog.as_view(), name='javascript-catalog'), ] 

Paso 2: Agregue los recursos de JavaScript / CSS necesarios a su plantilla.

     {{ form.media }} {# Form required JS and CSS #} 

Paso 3: Use los widgets de administrador para los campos de entrada de fecha y hora en su forms.py .

 from django.contrib.admin import widgets from .models import Product class ProductCreateForm(forms.ModelForm): class Meta: model = Product fields = ['name', 'publish_date', 'publish_time', 'publish_datetime'] widgets = { 'publish_date': widgets.AdminDateWidget, 'publish_time': widgets.AdminTimeWidget, 'publish_datetime': widgets.AdminSplitDateTime, } 

Solución actualizada y solución para SplitDateTime con required = False :

forms.py

 from django import forms class SplitDateTimeJSField(forms.SplitDateTimeField): def __init__(self, *args, **kwargs): super(SplitDateTimeJSField, self).__init__(*args, **kwargs) self.widget.widgets[0].attrs = {'class': 'vDateField'} self.widget.widgets[1].attrs = {'class': 'vTimeField'} class AnyFormOrModelForm(forms.Form): date = forms.DateField(widget=forms.TextInput(attrs={'class':'vDateField'})) time = forms.TimeField(widget=forms.TextInput(attrs={'class':'vTimeField'})) timestamp = SplitDateTimeJSField(required=False,) 

form.html

     

urls.py

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

Uso esto, es genial, pero tengo 2 problemas con la plantilla:

  1. Veo los icons de calendario dos veces para cada archivo en la plantilla.
  2. Y para TimeField tengo ‘ Ingresar una fecha válida.

    Aquí hay una captura de pantalla del formulario.

modelos.py

 from django.db import models name=models.CharField(max_length=100) create_date=models.DateField(blank=True) start_time=models.TimeField(blank=False) end_time=models.TimeField(blank=False) 

forms.py

 from django import forms from .models import Guide from django.contrib.admin import widgets class GuideForm(forms.ModelForm): start_time = forms.DateField(widget=widgets.AdminTimeWidget) end_time = forms.DateField(widget=widgets.AdminTimeWidget) create_date = forms.DateField(widget=widgets.AdminDateWidget) class Meta: model=Guide fields=['name','categorie','thumb'] 

En Django 10. myproject / urls.py: al principio de urlpatterns

  from django.views.i18n import JavaScriptCatalog urlpatterns = [ url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'), . . .] 

En mi template.html:

 {% load staticfiles %}   {# Loading internazionalization for js #} {% load i18n admin_modify %}             

Mi configuración de Django: 1.11 Bootstrap: 3.3.7

Dado que ninguna de las respuestas es completamente clara, estoy compartiendo el código de mi plantilla que no presenta ningún error.

La mitad superior de la plantilla:

 {% extends 'base.html' %} {% load static %} {% load i18n %} {% block head %} Add Interview {% endblock %} {% block content %}       

Mitad inferior:

     {% endblock %}