python - example - selectdatewidget




Usar widgets de fecha y hora de Django en forma personalizada (9)

¿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 ...


¿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();

(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.


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.


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:

  1. 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()
    
  2. 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).

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

  4. 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.


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 %}

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'),






django