python - example - Usar widgets de fecha y hora de Django en forma personalizada
select widget django example (13)
¿Qué tal si le asignamos una clase a su widget y luego vinculamos esa clase con el selector de fecha de 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 algo de JavaScript para la plantilla:
$(".datepicker").datepicker();
¿Cómo puedo usar los ingeniosos widgets de fecha y hora de JavaScript que el administrador predeterminado usa con mi vista personalizada?
He revisado la documentación de 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.
<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>
Además, creo que debe tenerse en cuenta que realmente no he escrito una vista de este formulario, estoy usando una vista genérica. Aquí está la entrada de url.py:
(r''^admin/products/add/$'', create_object, {''model'': Product, ''post_save_redirect'': ''''}),
Y soy completamente nuevo en todo lo relacionado con Django / MVC / MTV, así que por favor vaya fácil ...
(Estoy tratando de comentar sobre las personas que sugieren rodar su propio widget de calendario, pero o no veo el botón de comentario, o no tengo suficiente representante).
¿Qué pasó con DRY ? Creo que sería mejor volver a usar el widget de administración, pero quizás debería estar separado del administrador y más fácil de usar. Gracias por esta información de todos modos.
Comenzando en Django 1.2 RC1, si está utilizando el truco de widgets del selector de fechas de administrador de Django, se debe agregar lo siguiente a su plantilla, o verá que se hace referencia a la URL del icono de calendario a través de "/ missing-admin-media-prefix" / ".
{% 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>
Como complemento de 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 %}
<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"/>
<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="/media/admin/js/core.js"></script>
<script type="text/javascript" src="/media/admin/js/admin/RelatedObjectLookups.js"></script>
{{ form.media }}
{% endblock %}
Como la solución es hackish, creo que usar tu propio widget de fecha / hora con JavaScript es más factible.
En Django 10. myproject / urls.py: al comienzo de los patrones url
from django.views.i18n import JavaScriptCatalog
urlpatterns = [
url(r''^jsi18n/$'', JavaScriptCatalog.as_view(), name=''javascript-catalog''),
.
.
.]
En mi template.html:
{% load staticfiles %}
<script src="{% static "js/jquery-2.2.3.min.js" %}"></script>
<script src="{% static "js/bootstrap.min.js" %}"></script>
{# Loading internazionalization for js #}
{% load i18n admin_modify %}
<script type="text/javascript" src="{% url ''javascript-catalog'' %}"></script>
<script type="text/javascript" src="{% static "/admin/js/jquery.init.js" %}"></script>
<link rel="stylesheet" type="text/css" href="{% static "/admin/css/base.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "/admin/css/forms.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "/admin/css/login.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "/admin/css/widgets.css" %}">
<script type="text/javascript" src="{% static "/admin/js/core.js" %}"></script>
<script type="text/javascript" src="{% static "/admin/js/SelectFilter2.js" %}"></script>
<script type="text/javascript" src="{% static "/admin/js/admin/RelatedObjectLookups.js" %}"></script>
<script type="text/javascript" src="{% static "/admin/js/actions.js" %}"></script>
<script type="text/javascript" src="{% static "/admin/js/calendar.js" %}"></script>
<script type="text/javascript" src="{% static "/admin/js/admin/DateTimeShortcuts.js" %}"></script>
Finalmente logré que este widget funcionara en el servidor de desarrollo, solo para interrumpir la implementación. Finalmente decidí que no valía la pena meterme en mi sitio, y escribí mi propio widget. No es tan flexible, pero probablemente funcionará bien para muchos: http://www.copiesofcopies.org/webl/?p=81
La creciente complejidad de esta respuesta a lo largo del tiempo, y los muchos hackeos requeridos, probablemente deberían advertirle que no debe hacer esto. Se basa en detalles de la implementación interna no documentada del administrador, es probable que se rompa de nuevo en versiones futuras de Django, y no es más fácil de implementar que simplemente encontrar otro widget de calendario JS y usarlo.
Dicho eso, esto es lo que debes hacer si estás decidido a hacer que esto funcione:
Defina su propia subclase ModelForm para su modelo (mejor ponerla 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()
Cambie su URLconf para pasar ''form_class'': ProductForm en lugar de ''model'': Product a la vista genérica create_object (eso significará "from my_app.forms import ProductForm" en lugar de "from my_app.models import Product", por supuesto).
En el encabezado de su plantilla, incluya {{form.media}} para mostrar los enlaces a los archivos Javascript.
Y la parte hacky: los widgets de fecha / hora de administración presuponen que el material de i18n JS se ha cargado, y también requieren core.js, pero no proporcionan ninguno automáticamente. Por lo tanto, en su plantilla de arriba {{form.media}} necesitará:
<script type="text/javascript" src="/my_admin/jsi18n/"></script> <script type="text/javascript" src="/media/admin/js/core.js"></script>
También puede usar el siguiente CSS de administrador (gracias Alex por mencionar esto):
<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"/>
Esto implica que el medio de administración de Django (ADMIN_MEDIA_PREFIX) está 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á más allá del alcance de esta pregunta.
Esto también requiere que la URL / my_admin / jsi18n / se conecte 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 pasar por la aplicación de administración para que sea accesible independientemente de si has iniciado sesión en el administrador (gracias Jeremy por señalar esto). 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. */
<script type="text/javascript">
window.__admin_media_prefix__ = "{% filter escapejs %}{% admin_media_prefix %}{% endfilter %}";
</script>
Gracias lupefiasco por esta adición.
Lo siguiente también funcionará como último recurso si lo anterior falló
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 forms.py from django.forms.extras.widgets import SelectDateWidget
Me encuentro haciendo referencia a esta publicación mucho, y encontré que la documentation define una forma un poco menos hacky de anular los widgets predeterminados.
( No es necesario anular el método __init__ de ModelForm )
Sin embargo, aún necesita cablear su JS y CSS apropiadamente como Carl menciona.
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 campo de referencia para encontrar los campos de formulario predeterminados.
Mi código de cabeza para la versión 1.4 (algunos nuevos y otros eliminados)
{% block extrahead %}
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/forms.css"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/base.css"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/global.css"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/widgets.css"/>
<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/core.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/jquery.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/jquery.init.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/actions.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/calendar.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/admin/DateTimeShortcuts.js"></script>
{% endblock %}
Sí, terminé anulando el / admin / jsi18n / url.
Esto es lo que agregué en mi urls.py. Asegúrate de que esté 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)
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
<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="/admin_media/js/core.js"></script>
<script type="text/javascript" src="/admin_media/js/calendar.js"></script>
<script type="text/javascript" src="/admin_media/js/admin/DateTimeShortcuts.js"></script>
urls.py
(r''^admin/jsi18n/'', ''django.views.i18n.javascript_catalog''),