Django es el entorno de desarrollo web para perfeccionistas con límites de tiempo

Modificar la QuerySet de un ModelChoiceField dinámicamente

El campo de formulario ModelChoiceField sirve para permitir la selección de un elemento entre los objetos resultantes de una QuerySet. La QuerySet inicial puede definirse en el propio campo del formulario. Un ejemplo de uso en nuestro forms.py sería:

from django import forms
from django.contrib.auth.models import User

class MiFormulario(forms.Form):
    usuario = forms.ModelChoiceField(queryset=User.objects.all())

En este ejemplo nuestro formulario tiene un campo usuario que permite seleccionar un usuario entre todos los registrados. Sin embargo a veces necesitamos que la QuerySet sea distinta de la predefinida en el formulario. Vamos a ver cómo definir la QuerySet dinámicamente al crear un objeto de formulario.

Para definirla dinámicamente primero cambiamos la QuerySet inicial por una QuerySet vacía. El campo ModelChoiceField espera que le pasemos una QuerySet pero aún no sabemos qué QuerySet queremos ejecutar (la generaremos dinámicamente al crear cada objeto de formulario) por lo que primero usamos el método none() que devuelve una lista vacía:

usuario = forms.ModelChoiceField(queryset=User.objects.none())

A continuación sobreescribimos el método __init__ del formulario para poder definir la QuerySet dinámicamente cuando se crea un formulario. Imaginemos que queremos crear una vista que contenga el formulario y que permita seleccionar cualquier usuario que no sea staff. Sin embargo si un superusuario accede a la vista queremos permitirle seleccionar cualquier usuario (incluyendo los que forman parte del staff). Éste es un ejemplo donde necesitamos definir la QuerySet en el momento de crear el formulario:

class MiFormulario(forms.Form):
    usuario = forms.ModelChoiceField(queryset=User.objects.none())

    def __init__(self, user, *args, **kwargs):
        super(MiFormulario, self).__init__(*args, **kwargs)

        if user.is_superuser:
            self.fields['usuario'].queryset = User.objects.all()
        else:
            self.fields['usuario'].queryset = User.objects.filter(is_staff=False)

Como podemos ver, el método __init__ recibe un parámetro user. Si el usuario que recibe es superusuario el campo ModelChoiceField del formulario se construirá con la lista de todos los usuarios. De lo contrario se construirá con la lista de usuarios que no forman parte del staff del sitio web. Al crear el formulario en nuestra vista pasaremos request.user al formulario para que se construya con la QuerySet adecuada en función del usuario activo:

from forms import MiFormulario

def mi_vista(request):
    mi_form = MiFormulario(user=request.user)
    # ...

¡Listo! La QuerySet del campo usuario será una u otra en función de si el usuario que accede a la vista es superusuario o no. Hemos construído la QuerySet de un ModelChoiceField dinámicamente :)

Publicado por Antonio Melé el Friday 7 de August de 2009 | Categorías: fields, forms, trucos

Entradas similares

Error común en upload_to de ImageFields y FileFields

Hoy me he topado con un artículo sobre un error muy común al utilizar ImageField ó FileField. Al usar cualquiera de estos dos tipos de campo podemos especificar la ruta relativa a nuestro setting MEDIA_ROOT en la que queremos que ...


Utilizar un formulario para modificar dos modelos

Actualización/Update: Zack translated this post into english and it is available here.

A veces tenemos que modificar información relativa a dos o más modelos distintos desde un solo formulario HTML. Es algo común por ejemplo cuando definimos un perfil ...


Métodos para crear perfiles de usuario

En múltiples ocasiones nos gustaría extender el modelo User para que incluyera otros campos y funciones. La manera "oficial" de hacer esto (la mostrada en la documentación de Django) es creando un modelo para el perfil de usuario que incluya ...


Templatetags globales en nuestros proyectos Django

Los templatetags de Django son a nivel de aplicación. Sin embargo a veces nos gustaría que distintas aplicaciones compartieran templatetags ó evitarnos tener que cargarlos en todas las plantillas mediante {% load ... %}.

Este sencillo snippet muestra cómo registrar templatetags de modo ...


Settings accesibles desde las plantillas

Muchas veces deseamos acceder a los settings de nuestro proyecto desde alguna de nuestras plantillas. Lo ideal es crear un context processor que nos permita acceder a ellos desde cualquier plantilla de cualquier aplicación de nuestro proyecto.

Para ello en ...


Crear una imagen de nuestros modelos con django-command-extensions

Algo interesante que nos aporta django-command-extensions es poder crear una representación gráfica de nuestros modelos (o por decirlo de otro modo nuestro esquema de base de datos) con tan sólo un comando. Esto es posible gracias a GraphViz y el ...


9 comentarios:

El Saturday 8 de August de 2009 anonymous dijo:

class MiFormulario(forms.Form):
usuario = forms.ModelChoiceField(queryset=User.objects.all())

def __init__(self, user, *args, **kwargs):
super(MiFormulario, self).__init__(*args, **kwargs)

if not user.is_superuser:
self.fields['usuario'].queryset = self.fields['usuario'].queryset.filter(is_staff=False)

El Friday 14 de August de 2009 Andrea dijo:

Muy útil, gracias!!

El Tuesday 8 de September de 2009 panchicore dijo:

funciono en mi caso para una webapp saas para sacar los clientes de la empresa logeada:

class NuevoFacturaForm(ModelForm):
def __init__(self, empresa, *args, **kwargs):
super(NuevoFacturaForm, self).__init__(*args, **kwargs)
self.fields['cliente'].queryset = Cliente.objects.filter(empresa = empresa)

----------
PD: *HiddenField Magic? que pesadilla, tube que recargar la pagina como 10 veces para poner poner el comment :s

El Tuesday 29 de September de 2009 esteban dijo:

Hola. Esto anda en django 1.1?
me da este error cuando lo uso con sqlite:

Error binding parameter 0 - probably unsupported type.

Gracias.

El Monday 19 de October de 2009 Antonio Melé dijo:

No debería dar problemas con la 1.1. Yo la uso con la versión trunk.

El Tuesday 2 de February de 2010 Andrés dijo:

A mí también me ha servido, muchas gracias.

El Monday 8 de February de 2010 Joshua dijo:
Muchas gracias! Aunque lamentablemente no entiendo todavía los *args, **kwargs lo he modificado y hace lo que requiero. Saludos,
El Monday 8 de February de 2010 Joshua dijo:
Estimados, Pude hacer que en el form aparecieran opciones de acuerdo al usuario. Sin embargo al hacer el submit me da el siguiente error: TemplateSyntaxError at /transaction/add/transaction/ Caught an exception while rendering: Incorrect integer value: '
El Monday 8 de February de 2010 Joshua dijo:
... '

Escribe un comentario: