¿Cómo usar Codenerix GenList?
🇬🇧 Read it in English, “GenList“ Anteriormente hablábamos sobre CodenerixModel para entender como se construye un modelo funcional con CODENERIX. Sin embargo, para que...
Filter by Category
Filter by Author
🇬🇧 Read it in English, “GenList“ Anteriormente hablábamos sobre CodenerixModel para entender como se construye un modelo funcional con CODENERIX. Sin embargo, para que...
Posted by Juanmi Taboada
🇬🇧 Read it in English, “CodenerixModel“ En el artículo anterior hablábamos sobre CODENERIX y sus bondades. Ahora vamos a comenzar una serie de artículos que permitan...
Posted by Juanmi Taboada
Tal y como Google anunciaba en su blog oficial, aquellos usuarios que accedan al calendario desde la web comenzarán a ver un nuevo diseño mucho más limpio basado en el estándar de...
Posted by Juanmi Taboada
Poodle se convierte en una amenaza para la red, se recomienda desactivar SSL3 del navegador. Hace tan sólo 2 días Google reveló una nueva vulnerabilidad que afecta a la...
Posted by Juanmi Taboada
¡¡¡ATENCIÓN: ESTE ES UN COMUNICADO DE VITAL IMPORTANCIA PARA SU EMPRESA!!! Desde finales de la semana pasada se habla en las noticias del Ransomware Wannacry y esta...
Posted by Juanmi Taboada
Open South Code – 5 y 6 Mayo (La Térmica, Málaga) EVENTO GRATUITO DE SOFWARE LIBRE CREATIVIDAD, TENDENCIAS EN PROGRAMACIÓN, FUTURO Tiene lugar la 2ª edición del...
Posted by Juanmi Taboada
🇬🇧 Read it in English, “What is it CODENERIX?“ Es el nombre que recibe el software libre generado por Centrologic para desarrollo de herramientas de gestión...
Posted by Juanmi Taboada
En este artículo hablamos del ciclo de vida de un dominio. El nombre de dominio está registrado o renovado. Los nombres de dominio pueden estar registrados o...
Posted by Juanmi Taboada
En primer lugar debemos conocer que para que exista el fichero .ibd debemos tener activado el sistema de almacenamiento Barracuda mediante la separación de ficheros por cada tabla...
Posted by Juanmi Taboada
Cada año durante las vacaciones, como buen informático, aprovecho para leer cosas diferentes y aprender algo nuevo, en esta ocasión he podido estudiar sobre la librería...
Posted by Juanmi Taboada
🇬🇧 Read it in English, “GenList“
Anteriormente hablábamos sobre CodenerixModel para entender como se construye un modelo funcional con CODENERIX. Sin embargo, para que todo funciocune en Django, además de los modelos también hay que definir las vistas. Para permitir visualizar los contenidos webs existen a disposición del programador un conjunto de vistas heredables, todas ellas basadas en Vistas Genéricas de Django, y que siguiendo la política de CODENERIX son tan simples de usar como heredar de ellas.
En este artículo os hablaré sobre GenList, que es la vista genérica de CODENERIX que permite visualizar el listado de un modelo. GenList hereda directamente de ViewList de Django, por lo que todas las propiedades de un ViewList están disponibles en GenList. Veamos como sería el listado de un modelo “Contact”:
from codenerix.views import GenList from app.models import Contact class ContactList(GenList): model = Contact
Tan simple como eso, y el resultado visual equivaldría a algo parecido a esto (si usas los Ejemplos de CODENERIX):
De este modo hemos conseguido visualizar el modelo Contact del ejemplo “agenda” que tenéis aquí:
class Contact(CodenerixModel): first_name = models.CharField(verbose_name=_(u'Nombre(s)'), max_length=128) last_name = models.CharField(verbose_name=_(u'Apellidos'), max_length=128, blank=True, null=True) alias = models.CharField(verbose_name=_(u'Alias'), max_length=32, blank=True, null=True) organization = models.CharField(verbose_name=_(u'Organización'), max_length=64, blank=True, null=True) borndate = models.DateField(verbose_name=_(u'Cumpleaños'), blank=True, null=True) address = models.TextField(verbose_name=_(u'Dirección'), blank=True, null=True) created_by = models.ForeignKey(User, verbose_name=_(u'Creado por'), on_delete=models.CASCADE, related_name='contacts') def __fields__(self, info): return ( ('first_name', _(u'Nombre(s)')), ('last_name', _(u'Apellidos')), ('alias', _(u'Alias')), ('organization', _(u'Organization')), )
Ya te habrás percatado que CODENERIX ha usado el método __fields__ del modelo para descubrir qué campos debe mostrar en el listado. Además se ha tomado la libertad de ofrecernos automáticamente un listado que permite ordenación ascendente/descendente acumulativa en cada uno de sus campos (ver columna “Last name” en la siguiente captura) y si pulsamos el icono del filtro (color celeste en la siguiente captura) incluso podremos ver que también se ha ocupado de permitirnos buscar en los campos uno por uno (ver columna “Organization” donde buscamos “Inc” limitando el listado a 2 resultados):
En fin, que heredando de GenList hemos conseguido un gran trabajo con un resultado visual excelente para nuestros usuarios.
Pero GenList al igual que CodenerixModel permite usar los método: __fields__, __limitQ__, __searchF__ y __searchQ__. De este modo cuando se crea alguno de estos métodos en un GenList se usará el método de CodenerixModel o GenList según existan. De hecho su funcionamiento es exactamente igual que en CodenerixModel.
En las siguientes 3 tablas se muestran 3 columnas:
__fields__ |
||
CodenerixModel | GenList | Se aplica… |
NO | NO | — ERROR — |
NO | SI | — ERROR — |
SI | NO | CodenerixModel |
SI | SI | GenList |
__limitQ__ |
||
CodenerixModel | GenList | Se aplica… |
NO | NO | No se aplica ningún filtro |
NO | SI | Se aplica filtro de GenList |
SI | NO | Se aplica filtro de CodenerixModel |
SI | SI | Se aplica filtro de GenList y de CodenerixModel uniendo a ambos mediante el operador lógico “AND“. |
__searchQ__ & __searchF__ |
||
CodenerixModel | GenList | Se aplica… |
NO | NO | No se aplica ningún filtro |
NO | SI | Se aplica filtro de GenList |
SI | NO | Se aplica filtro de CodenerixModel |
SI | SI | Se aplica filtro de GenList |
Por lo tanto ahora podemos decidir que nuestra vista se comporte de forma diferente a nuestro modelo. Esto se hace porque algunas veces queremos crear más de un listado del mismo modelo donde cada listado tiene propiedades diferentes, muestra información diferente dependiendo del usuario o bien usa un template distinto.
GenList además puede contener diferentes atributos que permiten configurar el funcionamiento de este de muchas maneras diferentes. Aquí dispones de una tabla de lo que existe a día de hoy:
Tabla de atributos permitidos por GenList |
|
Atributo | Descripción |
annotations | Es posible introducir agregadores dentro de los Queryset que usa CODENERIX (aprende más sobre agregadores en Django v2.0). Es muy útil cuando deseamos que calcule máximos, mínimos, medias o simplemente agrupe resultados por fecha, tipos u otros campos. Es posible que annotations también funcione como un método de la clase (ver ejemplo).
annotations = { 'min_price': Min('books__price'), 'max_price': Max('books__price') } def annotations(self, info): anot = [] if info.user.is_superuser: anot['min_price'] = Min('books__price') anot['max_price'] = Max('books_price') return anot |
appname | Ignora el sistema automático de generación de URLs para que puedas usar en las urls una componente de aplicación diferente al de la aplicación de la vista. Esto es comúnmente usado para manejar el mismo modelo en diferentes aplicaciones (por ejemplo varios GenList). Sé cuidadoso, este campo altera el funcionamiento normal de CODENERIX y puede confundirte si lo usas incorrectamente. Puedes encontrar un ejemplo completo en funcionamiento en Github. |
autofiltering | En caso de estar a False desactiva el sistema de generación de filtros automáticos. Por defecto está a True.
|
client_context | Es un diccionario y su contenido será enviado directamente en la respuesta JSON dentro de la estructura “meta” como “context“. Es muy útil para enviar datos de forma directa desde la vista al controlador o template de AngularJS.
{ "filter": ..., "meta": { ... "context": { "var1": "value1", }, ... }, "table": ..., } |
default_ordering | El atributo puede ser una cadena o una lista de cadenas. La cadena puede comenzar con “-” o no. En este campo se indica el campo o los campos que van a usarse en la ordenación y el símbolo “-” indica que la ordenación se hará de forma descendente (sentido inverso), si no se indica nada se hará de forma ascendente. Si se da una lista, la ordenación se aplicará siguiendo escrupulosamente el orden en que aparecen los campos en esta lista.
|
default_rows_per_page | Usado para indicar el número de filas por página que deseamos visualizar por defecto en este listado. El valor estándar usado en GenList es de 50.
|
export | Fuerza la descarga de una lista como un fichero. Aquí se indica el formato. Por defecto es None.
|
export_excel | Muestra el botón de exportar a Excel. Por defecto está a True.
|
export_name | Nombre del fichero resultante de la exportación. Por defecto es “list“.
|
extends_base | Contiene la ruta del template base del que heredará el template de List. Generalmente acude a un “base/base.html“, pero cambiando esta variable se puede indicar que cargue otro fichero diferente para ofrecer al usuario una experiencia de entorno diferente.
|
extra_context | Contiene un diccionario que será mezclado con el contexto final que se entregará al render. Durante el proceso GenList puede sobreescribir las variables usadas aquí.
extra_context = { "variable1": "This is a CODENERIX variable that will be ready to use in the Template's Context", "variable2": { "a": 1, "b": 2}, "variable3": 3.4, } |
field_check | Activa la visualización del checkbox en los listados, es un checkbox que al ser marcado almacena el pk de la fila y puede ser usado en el $scope de AngularJS para hacer operaciones personalizadas. Tiene 3 estados posible este atributo:
|
field_delete | Activa la visualización del campo delete en los listados, es el icono de una papelera y cuando se hace click en él se envía al usuario a un borrado de dicho registro. Por defecto está a False.
|
haystack | Activa el soporte para Haystack en este listado. Por defecto está a False.
|
json | Cuando este atributo está a True, la vista responderá siempre en formato JSON. Si la vista responde en formato JSON y se desea modificar la respuesta se puede trabajar sobre el método json_builder() de la vista, reescribiéndolo o simplemente reprocesando su resultado. Debemos saber que cuando las vistas reciben un parámetro “json” en GET/POST se activará automáticamente la respuesta JSON. También quedará activada este atributo cuando se reciba una cabecera “HTTP_X_REST” y su valor se pueda evaluar como un Booleano “True“. Es importante saber que por defecto este atributo se establece a True.
|
linkadd | Muestra el botón de “Añadir” en los listados. Por defecto está a True.linkadd = False |
linkedit | Al hacer click en una fila de un listado pasa al modo edición de ese registro concreto. Por defecto está a True.
|
model | Modelo a usar.
|
modelname | Ignora el sistema automático de generación de URLs para que puedas usar en las urls una componente de modelo diferente al del modelo de la vista. Esto es comúnmente usado para manejar el mismo modelo en diferentes aplicaciones (por ejemplo varios GenList). Sé cuidadoso, este campo altera el funcionamiento normal de CODENERIX y puede confundirte si lo usas incorrectamente. Puedes encontrar un ejemplo completo en funcionamiento en Github. |
must_be_staff | Si el usuario debe ser o no staff (capacidad para ver /admin/ de Django según el model User) para poder visualizar el contenido de este listado. Por defecto su valor es False.
|
must_be_superuser | Si el usuario debe ser o no superuser (según el model User de Django) para poder visualizar el contenido de este listado. Por defecto su valor es False.
|
ngincludes | Mantiene el control sobre los posibles ng-includes que puedan aparecer en los parciales.
|
onlybase | Provocará que GenListe actue sólo como un fichero base para otra vista vista. Por defecto está a False.
|
permission | Puede ser una cadena o una lista de cadenas. Cada cadena de texto representa un permiso. El usuario podrá ver el listado cuando tenga de forma directa al menos uno de los permisos que aparecen en este atributo.
|
permission_group | Puede ser una cadena o una lista de cadenas. Cada cadena de texto representa un permiso de grupo. El usuario podrá ver el listado cuando alguno de los grupos del usuario tenga alguno de los permisos que aparecen en este atributo.
|
search_filter_button | En caso de estar a False desactiva el botón de filtros por campos. Por defecto está a True.
|
show_details | Al hacer click en una fila de un listado pasa al modo detalle de ese registro concreto. Por defecto está a False.
|
show_modal | Cuando está activado, empuja al sistema a renderizar en una ventana modal.
|
static_app_row | Carga el fichero de aplicación de AngularJS que aquí se indique, en caso contrario usa el por defecto: codenerix/js/apps.js.
|
static_controllers_row | Carga el fichero de controladores de AngularJS que aquí se indique, en caso contrario no carga ninguno.
|
static_filters_row | Carga el fichero de filtros de AngularJS que aquí se indique, en caso contrario usa el por defecto: codenerix/js/rows.js.
|
static_partial_header | Provoca que se cargue el parcial de cabecera de tabla indicado, si no se especifica no habrá parcial de cabecera.
|
static_partial_row | Provoca que se cargue el parcial de fila indicado en este atributo en vez del parcial por defecto: codenerix/partials/rows.html.
|
static_partial_summary | Provoca que se cargue el parcial de cola de la tabla indicado en este atributo en vez del parcial por defecto: codenerix/partials/summary.html.
|
template_base | Es el template que actúa de base para el resto. Generalmente GenList renderiza mediante una serie de templates que van saltando de unos a otros por herencia. En el último paso los templates de CODENERIX llaman a un template base que por lo general suele ser “base/base” sin embargo esto se puede cambiar con este atributo para que use otro template de base. Recuerda que CODENERIX intentará localizar el resto del nombre del fichero “web” mediante los algoritmos de detección de templates.
|
template_base_ext | Extensión usada para template_base.
|
template_model | Este es el template usado como punto de entrada para el renderizador de CODENERIX. Generalmente si no existe la ruta terminará usando “codenerix/list” (que terminará siendo “codenerix/list.html”) sin embargo es posible definir nuestro propio punto de entrada mediante este atributo. Esto es muy útil cuando queremos añadir “extra_css” o “extra_js” en el renderizado y nuestro template base soporta estos bloques.
|
template_model_ext | Extensión usada para template_model.
|
user | Permite hacer que una vista se comporte de forma específica como un usuario concreto. Es muy útil cuando se desea visualizar el listado como si fuésemos otro usuario del sistema. Es un objeto User de Django que será usado en todos los procesos de la vista y entregado a todos los métodos que lo soliciten.
IMPORTANTE: el sistema de gestión de permisos no se ve afectado por esta variable, es decir, seguirá recibiendo el usuario que ha solicitado visualizar el listado y se le aplicarán a este las políticas de restricción de acceso, no al que aparece en este atributo.
|
vtable | VTable es una tecnología mediante la cual el listado pasa a no estar paginado y CODENERIX hace una labor proactiva para estimar el ancho real del listado en pantalla y virtualiza el entorno de tal modo que precarga unas cuantas páginas en el navegador pero no todas y ajusta el scroll de la misma como debería ser si todos los registros estuviesen cargados de este modo cuando nos movemos en el listado CODENERIX carga páginas nuevas y descarga las antiguas para permitir al navegador funcionar a una velocidad adecuada de renderizado, pero el usuario percibe la sensación de estar trabajando en un listado inmenso y que todos los registros están ahí. Esta funcionalidad es experimental debido a que no ha sido testeada lo suficiente. Por defecto este atributo está a False.
|
ws_entry_point | Este atributo define la ruta hacia el punto de entrada para las operaciones que desee realizar el listado, por ejemplo, al cargar el listado se acude a la ruta indicada por el “ws_entry_point”, el editar se repite su uso. Generalmente se usa en vistas extras que se añaden sobre una ya existente sobre el modelo y cuando se desea introducir un nuevo controlador de Angular o alguna otra cosa que modifique el comportamiento estándar de CODENERIX.
|
Además de estos atributos debemos conocer cómo GenList intenta localizar los templates para el renderizado de la página. GenList va a intentar construir una serie de URLs basándose en el perfil del usuario y su idioma, de tal modo que si el usuario es superadmin este tendrá un perfil “admin” y en caso contrario no tendrá perfil alguno. El idioma del usuario es detectado por CODENERIX y será entregado igualmente a la función de detección de templates.
Las posibles rutas para los templates serán (siguiendo este orden de prioridad):
De este modo si el usuario es “luis“, sí es superuser y su idioma en el momento de la consulta es el Español con código “es“, y la ruta al fichero es “base/home“, el sistema probará la existencia de los siguientes ficheros:
GenList también soporte la detección automática de ficheros estáticos para la carga de parciales y ficheros de AngularJS como lo son el fichero de aplicación, los controladores o los filtros. Para ello GenList intentará localizar estos ficheros en la carpeta de la aplicación dentro de “/static” (o cual sea la ruta de ficheros estáticos en disco según la constante STATIC_ROOT de settings de Django). Para esta detección usará el mismo algoritmo que usa la detección de templates. De este modo probará para cada fichero estático la posible existencia de este en disco y cargará estos en vez de los que existen por defecto en CODENERIX. Para la generar las rutas el sistema usará:
Por último debemos hablar largo y tendido sobre el método json_builder() dado que es un método de GenList que permite controlar el resultado que se va a ofrecer al usuario. El método se define con 2 parámetros:
Existen varias técnicas para trabajar con json_builder():
Técnica 1: “Body building on your own” en la que tú mismo construyes el cuerpo completo de la respuesta:
def json_builder(self,answer,context): # El body es un listado de registros (body o cuerpo del listado) body=[] # Procesamos cada elemento del object_list del contexto a fin de procesar todas las filas for o in context['object_list']: t={} t['name']=o.name t['surname']=o.surname phones=[] for o2 in o.phone.all(): phone={} phone['country']=o2.country phone['prefix']=o2.prefix phone['number']=o2.number phones.append(t2) t['phone']=phones t['address']=o.address body.append(t) # Introduce el body en el cuerpo de la respuesta answer['table']['body']=body # Devuelve la respuesta ya lista return answer
En este ejemplo construimos una lista de forma manual donde nosotros somos responsables de rellenar cada fila o registro del body. Al terminar de procesar las filas que queremos enviar al navegador del usuario, cargamos estas filas (listado) en la respuesta y devolvemos esta.
Técnica 2: “Body building with bodybuilder()” en la que usas el método bodybuilder() para ayudarte a construir el cuerpo de la respuesta:
def json_builder(self,answer,context): # Rellenamos directamente usando bodybuilder answer['table']['body']=self.bodybuilder(context['object_list'],{ 'id:user__register__id':None, 'name':None, 'surname':None, 'phone': { 'country':None, 'prefix':None, 'number':None, }, 'address':None, }) # Answer the new context return answer
Cuando se usa bodybuilder() este espera recibir el listado de objetos a procesar y la forma de los registros que debe construir este. Lo que hace bodybuilder() es que por cada registro del object_list genera un entrada en el body, donde cada una de esas entradas son diccionarios que tienen la forma del segundo parámetro de la llamada, de este modo cada registro de este ejemplo tendría 5 claves en el diccionario:
Técnica 3: “Body building with autorules() and bodybuilder()” en la que usas autorules() y bodybuilder() para construir la respuesta:
def json_builder(self,answer,context): # Solicita las reglas a autorules rules=self.autorules() # Saca una de las claves del autorules (campo que no queremos en la respuesta) rules.pop('id') # Añade otra clave (campo que deseamos en la respuesta, en este caso un alias de user->id) rules['id:user__id']=None # Llama a bodybuider el nuevo conjunto de reglas answer['table']['body']=self.bodybuilder(context['object_list'],rules) # Devuelve el resultado return answer
En este caso lo que hacemos es pedir a GenList el conjunto de reglas que deberíamos usar, adaptamos ese conjunto de reglas a nuestro antojo y lo entregamos a bodybuilder() para que termine el trabajo de generar el cuerpo de la respuesta.
Debemos tener en cuenta que GenList incluye un optimizador de consultas para reducir del trabajo que se hace en Python y pasar parte de este al gestor de base de datos. Este optimizador no funciona cuando intervienen en el resultado datos que la base de datos no puede computar por si sola, esto es generalmente, cuando se incluyen métodos en los resultados y no son datos puramente almacenados en los objetos.
Otro detalle importante es que el optimizador convierte las filas del resultado en objetos de un diccionario, de tal modo que solo contiene los datos tal cual y no instancia los objetos del modelo como sería normal en Django.
Finalmente cuando no podemos trabajar con los filtros o al introducir ciertas limitaciones (annotates por ejemplo) el resultado ya no funciona como deseamos….siempre nos quedará un “AS en la manga“, el método “custom_queryset(queryset, info)“. Este método debes reescribirlo en tu vista a fin de que CODENERIX lo use, dado que de otro modo operará normalmente. Este método recibe dos parámetros: el primero es el QuerySet calculado por GenList, el segundo es el objeto MODELINF que contiene información interna de la consulta, del modelo, argumentos, etc… y espera que devuelvas un QuerySet válido. Es por ello que puedes devolver el QuerySet que has recibido con algunos parámetros más aprovechando la potencia del ORM de Django o incluso podría ser un QuerySet de un modelo diferente.
Puedes leer más sobre las vistas genéricas de CODENERIX en “Como usar Codenerix GenCreate, GenUpdate, GenDetail y GenDelete“.
It happened some time (years) while I was thinking about the problem of programming languages nowadays. I mean, I was asking questions myself like: – Is there some program...
In my last post, “How I designed the frame for my Underwater ROV“, I gave all details about the design I used for the frame for Alioli Underwater ROV. In this post, I...