¿Cómo definir dinámicamente un formulario usando Django?
En el desarrollo de proyectos recientes, se utiliza esta estructura de datos:
Principalmente para gestionar equipos.
Permítanme enumerar su estructura simplificada. No hace falta más, el resto es simplemente redundante o guinda del pastel:
La estructura de datos de la clasificación del dispositivo (equivalente a la. clasificación grande del dispositivo):
[python]Ver texto sin formato
Categoría Categoría(Model.Model):
Nombre = modelo. CharField(max_length=40)
Estructura de datos de un dispositivo específico:
[python] Ver texto sin formato
Categoría de dispositivo (modelo. modelo):
Nombre = modelo. CharField(max_length=100)
Categoría=Modelo. ForeignKey(category, related_name="cg_equip_list ")
Estructura de datos de los parámetros del dispositivo:
[python] Ver texto sin formato
Características de la categoría (model.model) :
Categoría=Modelo. ForeignKey(Categoría, related_name = "eq_characteristics")
nombre = modelo. CharField(max_length=40)
Estructura de datos del valor del parámetro del dispositivo:
[python] Ver texto sin formato
Valor de característica de categoría (model.model):
Dispositivo = modelo. ForeignKey(Equipo, related_name = "eq_characteristic_values")
Característica = modelo. ForeignKey(Característica, related_name="eq_key_values")
Valores = modelo. CharField(max_length=100)
Muchas personas pueden preguntar: ¿Por qué no definir directamente el valor del parámetro del dispositivo en la estructura de datos del dispositivo?
Mi respuesta es: Puedes definir más dispositivos tú mismo.
Los parámetros de cada dispositivo son diferentes. Por ejemplo, los parámetros de un ordenador son básicamente:
Frecuencia de la CPU, tamaño de la memoria, tamaño del disco duro, tipo de monitor, tamaño del monitor, etc.
Los parámetros de la impresora pueden ser los siguientes:
Formato máximo de papel, tipo de impresora, etc.
¿Cómo guardan datos en la base de datos dos dispositivos con diferentes parámetros?
Basándonos en la pregunta anterior, necesitaremos crear dos tablas, una tabla de computadora y una tabla de impresora, cuyas columnas contengan los parámetros correspondientes. En este caso, si hay un dispositivo más, como una cámara, tenemos que crear otra tabla de datos y cansarnos de mantener la base de datos.
Sin embargo, de acuerdo con la separación de parámetros y equipos, los parámetros de varios tipos de equipos se definen en forma de tablas de parámetros y se vinculan a la tabla de clasificación de equipos mediante claves externas.
Luego, coloque los valores de varios parámetros del dispositivo específico en la tabla de valores de parámetros y podrá guardar los parámetros de cada dispositivo.
En la tabla de datos de valores de parámetros, a qué dispositivo pertenece un parámetro se determina principalmente en función de la ID del dispositivo y la ID del parámetro.
Esta estructura de tabla es relativamente común.
Lo más problemático es que al agregar un dispositivo, se debe agregar su valor de parámetro correspondiente al mismo tiempo, pero sus parámetros no están en la tabla de dispositivos y deben consultarse a través de la tabla de parámetros:
En la tabla de parámetros, la fila en la que el ID de clasificación del dispositivo del parámetro = el ID de clasificación del dispositivo específico es el parámetro propiedad del dispositivo.
Por ejemplo:
Datos en la tabla de clasificación de dispositivos:
nombre de id
1 Computadora de escritorio
2 Impresora
Los parámetros guardan las siguientes líneas de datos en la tabla de parámetros:
id nombre categoría
1 frecuencia de CPU 1
2 Dirección MAC 1
3 Tamaño de memoria 1
4 Tamaño de pantalla 1
5 Tipo de pantalla 1
6 Tamaño de disco duro 1 p>
7 Formato de impresión máximo 2
Se puede ver de un vistazo que la computadora de escritorio tiene 6 parámetros y la impresora tiene 1 parámetro.
Sección 2 Selección de soluciones
La estructura de datos es muy clara. La pregunta ahora es ¿cómo ingresar los valores de estos parámetros?
La dificultad es que la cantidad de parámetros para cada dispositivo es incierta, por lo que al definir un formulario, no podemos fijar la cantidad de campos en el formulario, solo podemos aumentar o disminuir dinámicamente la cantidad de campos.
Entonces, ¿cómo generar dinámicamente formularios con diferentes números de campos?
Aquí, mi idea es que, dado que la cantidad de parámetros se almacena en la base de datos, se debe generar dinámicamente consultando la base de datos.
Entonces, ¿cuáles son los métodos específicos para usar Django?
Yo mismo probé muchos métodos y poco a poco encontré una solución a este problema durante el proceso de depuración.
El proceso es así:
La solución que comencé a considerar es:
1. Usar ajax para generar dinámicamente el formulario.
2. Al agregar un dispositivo, use los métodos en admin e inline para agregar varios parámetros.
3. Utilice formset para hacerlo.
4. Utilice formwizard en formtools para hacerlo.
El dilema del problema es que hay dos claves foráneas en la tabla de valores de parámetros y no es fácil determinar qué hacer.
Probé los primeros tres métodos y me di por vencido después de depurar durante uno o dos días. Las razones principales son:
1. Ajax todavía me resulta relativamente desconocido. Si desea utilizarlo bien, debe aprender algunas bibliotecas de JavaScript relacionadas y el efecto no será bueno por un tiempo.
2. Inline en admin es para un modelo de datos. Los valores de los parámetros agregados son los mismos. Por ejemplo, se pueden aumentar a 6 parámetros de CPU cuando se usan en Admin. Extráigalo y úselo usted mismo.
3. El formulario en FormSet también requiere el mismo modelo de datos, pero no puede usar FormSet en absoluto porque las dos claves externas en la tabla de valores de parámetros son muy altas. FormSet se utiliza para ingresar datos cuando la estructura de datos es simple.
Formset tiene una función llamada add_fields, que es muy útil, pero los formularios no tienen esta función. El principio es muy sencillo de entender, es decir, formulario ['campo_nombre'] = formularios. charfield() y así sucesivamente, y puede agregar un campo a la instancia del formulario.
Creo que formset puede resolver el problema, pero la premisa es que antes de construir el formset, necesita saber de antemano cuántos formularios con valores de parámetros hay en el formset (es decir, necesita para saber cuántos parámetros tiene un determinado dispositivo de clasificación, simplemente construya varios formularios con valores característicos en el conjunto de formularios, y cada formulario se inicializa con diferentes parámetros).
En una página, primero agregue el formulario del dispositivo. Debido a que el número de parámetros del dispositivo no es fijo, es necesario agregar dinámicamente un conjunto de formularios que contenga varios formularios de valores de parámetros para que los parámetros de. se puede seleccionar el dispositivo. formset es más adecuado para usar un número fijo de formularios, por lo que es más difícil usar formset. Si utilizara este enfoque, se utilizaría ajax para generar dinámicamente el conjunto de formularios según el tipo de dispositivo seleccionado por el usuario.
El conjunto de formularios debe inicializarse en la vista.
Así que no utilicé este método.
Yo uso el Asistente de formularios.
La contribución de Django proporciona herramientas de formulario, que son realmente fáciles de usar después de usarlas. Aprendí a usarlo poco a poco leyendo la documentación en inglés y lo encontré realmente conveniente.
Sin embargo, el proceso de implementación de las 4 soluciones anteriores es muy difícil.
Me tomó 3 días conseguir algunas pistas.
Sección 3 Implementación específica
El asistente de formularios en Django es muy sencillo de usar.
[python]Ver texto sin formato
Desde django.utils.translation importar ugettext_lazy como _
Importar tabla desde django
Desde django .forms.formsets import BaseFormSet
Importar campos de archivo desde django.forms.fields
Importar errores de validación desde django.forms.util
Importar acceso directo render_to_response
p>Asistente para importar formularios desde django. Contrib. Asistente para herramientas de formulario.
Importar equipos, propiedades y valores de propiedad desde DDT CMS. formulario de categoría (formulario. formulario modelo):
elemento de categoría:
modelo=equipo
formulario de valor de característica de clase (formulario. formulario):
def clean(self):
a =self campo
s=self.data
self.cleaned_data = {}
#El siguiente párrafo está copiado de full_clean en form.py de Django
Para nombres, campos en self.fields.items():
# value_from_datadict() Obtener datos de datos diccionario.
# Cada tipo de widget sabe cómo recuperar sus propios datos, ya que algunos
# widgets dividen los datos en varios campos HTML.
valor = field.widget.value_from_datadict(self.data,self.files,self.add_prefix(name))
Pruebe:
Si es una instancia (campo, FileField):
inicial = self.initial.get(nombre, campo.inicial)
valor = campo.clean(valor, valor inicial)
p >En caso contrario:
valor = campo.clean(valor)
self.cleaned_data[nombre] = valor
if hasattr(self,' clean_ %s' % nombre):
valor = getattr(self, 'clean_%s' % nombre)()
self.cleaned_data[nombre] = valor
Excepto por errores de validación, e:
self. _ errores[nombre] = self . error _ clase(e . mensajes)
Si el nombre está en self.cleaned_data:
del self.cleaned_data[nombre]
#cl=self.cleaned_data
# debug()<<<Al depurar, verifique el valor de cl, principalmente el valor de self.cleaned_data. Si se devuelve, se perderá.
Devolver self.cleaned_data
clase EquipmentCreateWizard (asistente de formularios):
Definición completada (self, request, form_list):
Devolver render_to_response(' equipo/done . html ',
{
form_data':[form.cleaned_data del formulario en form_list,
})
def get_form(self, step, data=None):
"Un método auxiliar que devuelve una instancia de formulario para un paso determinado."
p>form = self .form_list[paso](datos, prefijo = self. prefijo _ para _ paso(paso), inicial=self.initial.get(paso, Ninguno))
Si zancada == 1:
si datos:
CG = datos .get('0-category',1)
cs = Característica.objetos todos(). filter(category__id=cg)
Para c en cs:
form .fields[' caracter istic-'+str(c . id)] = formulario. CharField(label = c.name)
g =Form.Field
#Debug()
Formulario de devolución
#Desde el asistente Cópielo en .py para realizar cambios.
def render(self, form, request, step, context = Ninguno):
"Renderiza el objeto de formulario dado y devuelve una respuesta Http".
olddata = solicitud. Publicación
prev_fields = []
Si los datos son antiguos:
hidden=form. HiddenInput()
#Recopila todos los datos de los pasos anteriores y preséntalos como campos ocultos HTML.
Para I (tamaño de paso) en el rango:
old_form = self.get_form(i, old_data)
hash_name = 'hash_%s' % i
prev _ campos . extend([BF . as _ oculto() para BF en antiguo _ formulario])
prev _ campos . .get(hash_name, self.security_hash(request, old_form)))
Si el tamaño del paso == 1:
CG = old_data.get('0-category', 1)
cs = Characteristic.objects.all(). Filtro (categoría__id=cg)
Para c:
formulario en campos cs[' carácter istic-. '+str(c . id)]=formulario CharField(label = c.name)
g =form.field
#Debug()
Si el tamaño del paso == 2:
Debug()
devuelve super(asistente de creación de equipo, self).
render(formulario, solicitud, paso, contexto = Ninguno)
def get_template(self, paso):
Devolver el paso "Device/Wizard_%s.html"
EquipmentCreateWizard también se puede colocar en views.py, lo cual creo que es más razonable.
En EquipmentCreateWizard, intenté modificar la función Process_step, pero no pude obtener el resultado correcto. Más tarde, modifiqué get_form. Quería copiarlo y modificarlo desde Wizard.py de FormTools de Django.
La modificación de get_form no obtuvo el resultado correcto. Posteriormente modifiqué la función de renderizado y en el segundo paso mostré la cantidad de parámetros dinámicos. Pero al final del enlace hecho, el segundo formulario tiene datos de formulario y es un {} vacío.
Así que modifiqué la función get_form nuevamente, solo para determinar si es el segundo paso, y luego agregué dinámicamente varios campos al segundo formulario:
[python] Ver texto sin formato
Si el tamaño del paso == 1:
CG = old_data.get('0-category',1)
cs = Characteristic.objects all(). . filter(category__id=cg)
Para c en cs:
form .fields[' caracter istic-'+str(c . id)] = formulario. CharField(label = c.name)
g =Form.Field
#Debug()
Este código se encuentra en get_form y render, es el juicio Si es el segundo paso, luego consulte la clasificación específica de acuerdo con la clasificación del dispositivo seleccionado en el paso 1, luego obtenga los parámetros del dispositivo clasificado de acuerdo con la clasificación y luego modifique el número de campos de parámetros en el formulario de acuerdo al número de parámetros.
Característica-'+str(c.id) se usa para dividir la cadena al guardar datos en el futuro, obtener la identificación del parámetro y guardar los valores de característica-1, característica-2 ... en el centro de la tabla de valores de parámetros.
g =Form.Field
#Debug()
Se utiliza como punto de interrupción para ver cuántos campos de parámetros hay y si la modificación se realizó correctamente.