Añadidas tandas: 4, 4b y 7 #33
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Sustituye {{ form.as_p }} (o el bucle for field in form repetido a mano en expense_form.html) por {% include "expenses/_form_fields.html" %} y anade class="app-form" al <form> en: - categories/form.html, categories/list.html (alta de categoria) - expenses/account_form.html - expenses/expense_form.html - expenses/income_form.html - expenses/tag_form.html - fuel/create.html - goals/form.html - registration/login.html De paso: - categories/list.html: se corrige indentacion de la tabla y las etiquetas <a> de "Editar"/"Eliminar" (estaban dentro de una celda sin problema funcional, solo desalineadas) y se anade una <th> vacia para la columna de acciones. - goals/form.html: el bloque title y el <h1> tenian "Nuevo objetivo" fijo aunque la vista goal_edit ya pasa title="Editar objetivo" en el contexto; ahora la plantilla usa {{ title }}, asi que el formulario de edicion deja de decir "Nuevo objetivo" por error. No se toca la logica de las vistas ni los formularios en forms.py.Ambas plantillas hacian {% extends "base.html" %}, pero en este proyecto no existe ninguna plantilla llamada asi a nivel raiz: el layout de la app se llama "expenses/base.html" (esta dentro de expenses/templates/expenses/, no de expenses/templates/). Con APP_DIRS=True y DIRS=[] en settings.py, Django busca "base.html" tal cual en la carpeta templates/ de cada app instalada y no lo encuentra en ninguna, asi que un {% extends %} a esa ruta lanza TemplateDoesNotExist en cuanto se intenta renderizar la plantilla. Esto llevaba tiempo sin detectarse porque, hasta el commit de INSTALLED_APPS de esta misma tanda, django.contrib.admin sombreaba estas dos plantillas con las suyas propias (ver ese commit): la plantilla rota de la app nunca llegaba a cargarse, asi que el error nunca saltaba. Al arreglar el orden de INSTALLED_APPS estas plantillas pasan a usarse de verdad, así que había que arreglarlas ahora sí en el mismo movimiento. Cambios: - Ambas extienden "expenses/base.html". - Se migran al parcial expenses/_form_fields.html y a class="app-form", igual que el resto de formularios (ver commit anterior). - Se anade un enlace de "Volver" a {% url 'home' %} en las dos, y un {% block title %} (heredaban el titulo generico "Expenses manager").Sintoma: /accounts/password_change/ renderizaba la pantalla del admin de Django (cabecera "Administracion de Django", breadcrumb "Inicio > Cambio de contrasena") en vez de la plantilla de la app. Causa: con APP_DIRS=True (settings.py) y sin DIRS explicito, Django busca plantillas recorriendo INSTALLED_APPS en orden y usa la PRIMERA coincidencia por ruta relativa. django.contrib.admin trae sus propias registration/password_change_form.html y registration/password_change_done.html. Como 'django.contrib.admin' aparecia antes que 'expenses' en la lista, sus plantillas ganaban siempre a las de nuestra app, que ni siquiera llegaban a evaluarse. Verificado con el shell de Django (get_template(...).origin.name): Antes del cambio: registration/password_change_form.html -> .../django/contrib/admin/templates/registration/password_change_form.html registration/password_change_done.html -> .../django/contrib/admin/templates/registration/password_change_done.html Despues del cambio: registration/password_change_form.html -> .../expenses/templates/registration/password_change_form.html registration/password_change_done.html -> .../expenses/templates/registration/password_change_done.html Esto tambien explica por que el bug de las plantillas rotas (extendian "base.html", que no existe; ver commit anterior) llevaba tanto tiempo sin dar error: al estar sombreadas, esas plantillas nunca se renderizaban, asi que su TemplateDoesNotExist nunca saltaba. Y explica por que el login SI funcionaba pese a la colision de app: el admin publica su login en "admin/login.html", no en "registration/login.html", asi que ahi no habia conflicto. Efecto secundario esperado y comprobado (no es una regresion, pero hay que saberlo): /admin/password_change/, que Django resuelve tambien por la ruta "registration/password_change_form.html" cuando un admin cambia su propia contrasena, ahora renderiza con el layout completo de la app (menu de navegacion, etc.) en vez del look propio del admin. Probado con un superusuario contra /admin/, /admin/login/, /admin/password_change/ y /admin/password_change/done/: los cuatro responden 200 y el formulario funciona (AdminPasswordChangeForm hereda los mismos campos old_password/new_password1/new_password2 de PasswordChangeForm, asi que el parcial generico de campos los pinta sin problema). El resto del admin (listados, cambio de contrasena de otros usuarios, etc.) no usa ninguna plantilla bajo registration/ ni admin/ que la app sobrescriba, asi que no le afecta. No se reordena alfabeticamente esta lista en el futuro sin tener en cuenta esto: hay un comentario en el propio settings.py explicandolo.El listado de etiquetas era una lista sin mas, sin forma de saber cuales estaban en uso. Pasa a tabla, en linea con el resto de listados, y anade una columna con el numero de gastos que usa cada etiqueta. El contador se calcula con annotate(Count("expenses")) en la vista, en vez de contar desde la plantilla, para no provocar una query por fila. Incluye tambien el estado vacio, que la lista no tenia. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Tag era el unico de los tres modelos con nombre sin ordering en su Meta, asi que las etiquetas salian en orden de insercion. Al anadirlo aparecio el problema de fondo: con ordering = ["name"], SQLite ordena por valor binario y en ASCII todas las mayusculas van antes que todas las minusculas, de modo que salia AA, MK, ZZ, mk, ms en vez de AA, MK, mk, ms, ZZ. Importa mas de lo que parece porque en produccion la base de datos es PostgreSQL, que ordena segun la configuracion regional. El orden pasa a calcularse en la consulta con Lower("name") en vez de depender de la colacion del motor, asi que local y NAS coinciden. Los tres Meta usan ahora ordering = [Lower("name")]. Django admite expresiones ahi, pero no se propaga solo a todos los sitios: habia tres sitios con orden explicito que lo pisaban y tambien se corrigen. - tag_list y account_list tenian .order_by("name") - goal_list ordenaba igual - _category_tree reordena los hermanos en memoria, y Python compara por code point igual que SQLite, asi que el arbol tampoco quedaba bien Las dos migraciones son AlterModelOptions, sin cambios de esquema, pero hay que desplegarlas. Lo que no resuelve: los acentos. Lower() normaliza mayusculas y minusculas, pero como se ordenan "N", "a" o "u" sigue dependiendo de la colacion de cada motor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>El listado de categorias no daba ninguna pista de cuales estan en uso. Anade la columna con el mismo criterio que el de etiquetas: annotate en la vista para no disparar una query por fila. Cuenta solo los gastos directos de cada categoria, no los de sus descendientes. Si el numero incluyera a los hijos, un padre sin gastos propios mostraria una cifra que no corresponde a ninguna fila suya y que no cuadraria con lo que se ve al filtrar por esa categoria. Con Category.Meta.ordering = [Lower("name")], Django anade la expresion al GROUP BY del annotate. Verificado que no multiplica filas ni fusiona categorias cuyo nombre solo difiere en mayusculas. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>