Rewrite all forms to use Django Forms with validation

- Add ProductEditForm, EditProfileForm, ChangePasswordForm, ShippingAddressForm
- Add ResetPasswordForm, ResetPasswordPhase2Form
- Update views to use new Django Forms
- Add form validation tests (terms required, password mismatch, etc)
- Update templates to use Django Forms {{ form.as_p }}
This commit is contained in:
2026-05-08 09:42:44 +02:00
parent ad7ddbe887
commit 551057b067
8 changed files with 918 additions and 578 deletions
+376 -429
View File
@@ -5,6 +5,7 @@ from django.db.utils import DataError
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .models import User, Product, Category, Cart, CartItem, Image, Order, OrderItem, OrderMessage, ShippingAddress, StockReservation, StockReservationItem, VerificationCode, SavedPaymentMethod
from .forms import ProductForm, SecondaryImageForm, UserLoginForm, UserRegisterForm, ProductEditForm, EditProfileForm, ChangePasswordForm, ShippingAddressForm, ResetPasswordForm, ResetPasswordPhase2Form
from . import tasks
from .vars import (
PAGE_SIZE,
@@ -86,9 +87,10 @@ def _is_almeria_city(city: str) -> bool:
return _normalize_location_text(city) in ALMERIA_MUNICIPALITIES
def _address_form_context(direccion=None):
def _address_form_context(direccion=None, form=None):
return {
"direccion": direccion,
"form": form,
"almeria_municipalities": ALMERIA_MUNICIPALITIES_DISPLAY,
}
@@ -221,153 +223,144 @@ def index(request: HttpRequest):
def login(request: HttpRequest):
if request.method == "POST":
email = request.POST.get("email")
password = request.POST.get("password")
remember = request.POST.get("remember")
client_ip = _get_client_ip(request)
# Buscar usuario por email
try:
user_obj = User.objects.get(email=email)
username = user_obj.username
except User.DoesNotExist:
audit_logger.warning(
"LOGIN_FAILED email=%s reason=user_not_found ip=%s",
email,
client_ip,
)
messages.error(request, "Correo electrónico o contraseña incorrectos.")
return render(request, "tienda/login.html")
# Autenticar usuario
user = authenticate(request, username=username, password=password)
if user is None:
data: str = cache.get(f"tries_login_{username}")
logins: int
if data is None:
logins = 0
else:
logins = int(data)
form: UserLoginForm = UserLoginForm(request.POST)
if form.is_valid():
email: str = form.cleaned_data["email"]
password: str = form.cleaned_data["password"]
remember: bool = form.cleaned_data["remember"]
client_ip = _get_client_ip(request)
try:
user: User = User.objects.get(email=email)
username = user.username
except User.DoesNotExist:
audit_logger.warning("LOGIN FAILED email=%s reason=user_not_found ip=%s", email, client_ip)
messages.error(request, "El email o la contraseña es incorrecta")
return render(request, "tienda/login.html", {"form": form})
if user.registration_status == User.RegisterStatus.BANNED:
# Usuario baneado.
messages.error(request, "Esta cuenta esta bloqueada.")
return render(request, "tienda/login.html", {"form": form})
if logins >= 5:
# Si ha fallado 5 intentos de login...
audit_logger.info(
"LOGIN_FAILED email=%s reason=rate_limited", username
)
messages.error(request, "Has sufrido de Rate Limit por fallar 5 veces la contraseña")
return render(request, "tienda/login.html")
logins+=1
cache.set(f"tries_login_{username}", str(logins), 600)
messages.error(request, "Correo electrónico o contraseña incorrectos.")
return render(request, "tienda/login.html")
user = User.objects.get(username=user.username)
if user.registration_status == "CR":
audit_logger.info(
"LOGIN_FAILED email=%s reason=not_verified", email
)
messages.error(request, "No se puede iniciar sesión porque no has verificado tu cuenta, comprueba tu email. Si eliminaste el email pero querias verificarte, contacta con el soporte tecnico")
return render(request, "tienda/login.html")
if user is not None:
user = authenticate(request, username = username, password=password)
if user is None:
data: str = cache.get(f"tries_login_{username}")
logins: int
if data is None:
logins = 0
else:
logins = int(data)
if logins >= 5:
audit_logger.info("LOGIN FAILED email=%s reason=rate_limited", email)
messages.error(request, "Has sufrido de Rate Limit por fallar 5 veces la contraseña")
return render(request, "tienda/login.html", {"form": form})
logins+=1
cache.set(f"tries_login_{username}", str(logins), 600)
messages.error(request, "El email o la contraseña es incorrecta")
return render(request, "tienda/login.html", {"form": form})
if user.registration_status == User.RegisterStatus.CONFIRMATION_REQUIRED:
audit_logger.info("LOGIN_FAILED email=%s reason=not_verified", email)
messages.error(request, "No se puede iniciar sesión porque no has verificado tu cuenta, comprueba tu email. Si eliminaste el email pero querias verificarte, contacta con el soporte tecnico")
return render(request, "tienda/login.html", {"form": form})
auth_login(request, user)
# Configurar duración de sesión
if not remember:
request.session.set_expiry(0)
else:
request.session.set_expiry(1209600) # 14 días en segundos
audit_logger.info(
"LOGIN_SUCCESS user_id=%s email=%s ip=%s remember=%s",
user.id,
user.email,
client_ip,
bool(remember),
)
tasks.enviar_correo_bienvenida.delay(user.email, "{} {}".format(user.first_name, user.last_name))
# result = send_email(user.email, "Inicio de sesión correcto", login_message.format(name = "{} {}".format(user.first_name, user.last_name)))
request.session.set_expiry(1209600)
audit_logger.info("LOGIN_SUCCESS user_id=%s email=%s ip=%s remember=%s", user.id, user.email, client_ip, bool(remember))
tasks.enviar_correo_bienvenida.delay(user.email, f"{user.first_name} {user.last_name}")
messages.success(request, f"¡Bienvenido {user.first_name or user.username}!")
return redirect("index")
else:
user1: User = User.objects.get(username=username)
if user1.registration_status == User.RegisterStatus.BANNED:
audit_logger.warning(
"LOGIN FAILED email=%s reason=user_banned ip=%s",
email,
client_ip,
)
messages.error(request, "Error, La cuenta esta bloqueada")
return render(request, "tienda/login.html")
audit_logger.warning(
"LOGIN_FAILED email=%s reason=invalid_credentials ip=%s",
email,
client_ip,
)
messages.error(request, "Correo electrónico o contraseña incorrectos.")
return render(request, "tienda/login.html")
return render(request, "tienda/login.html")
else:
form = UserLoginForm()
return render(request, "tienda/login.html", {"form": form})
#
# if user is not None:
# auth_login(request, user)
#
# # Configurar duración de sesión
# if not remember:
# request.session.set_expiry(0)
# else:
# request.session.set_expiry(1209600) # 14 días en segundos
#
# audit_logger.info(
# "LOGIN_SUCCESS user_id=%s email=%s ip=%s remember=%s",
# user.id,
# user.email,
# client_ip,
# bool(remember),
# )
# tasks.enviar_correo_bienvenida.delay(user.email, "{} {}".format(user.first_name, user.last_name))
# # result = send_email(user.email, "Inicio de sesión correcto", login_message.format(name = "{} {}".format(user.first_name, user.last_name)))
# messages.success(request, f"¡Bienvenido {user.first_name or user.username}!")
# return redirect("index")
# else:
# user1: User = User.objects.get(username=username)
# if user1.registration_status == User.RegisterStatus.BANNED:
# audit_logger.warning(
# "LOGIN FAILED email=%s reason=user_banned ip=%s",
# email,
# client_ip,
# )
# messages.error(request, "Error, La cuenta esta bloqueada")
# return render(request, "tienda/login.html")
# audit_logger.warning(
# "LOGIN_FAILED email=%s reason=invalid_credentials ip=%s",
# email,
# client_ip,
# )
# messages.error(request, "Correo electrónico o contraseña incorrectos.")
# return render(request, "tienda/login.html")
#
# return render(request, "tienda/login.html")
def register(request: HttpRequest):
if request.user.is_authenticated:
return redirect("index")
if request.method == "POST":
name = request.POST.get("name")
email = request.POST.get("email")
password = request.POST.get("password")
password_confirm = request.POST.get("password_confirm")
client_ip = _get_client_ip(request)
# Validaciones
if password != password_confirm:
audit_logger.warning("REGISTER_FAILED email=%s reason=password_mismatch ip=%s", email, client_ip)
messages.error(request, "Las contraseñas no coinciden.")
return render(request, "tienda/register.html")
if len(password) < 8:
audit_logger.warning("REGISTER_FAILED email=%s reason=password_too_short ip=%s", email, client_ip)
messages.error(request, "La contraseña debe tener al menos 8 caracteres.")
return render(request, "tienda/register.html")
if User.objects.filter(email=email).exists():
audit_logger.warning("REGISTER_FAILED email=%s reason=email_exists ip=%s", email, client_ip)
messages.error(request, "Ya existe un usuario con este correo electrónico.")
return render(request, "tienda/register.html")
# Crear username a partir del email
username = email.split("@")[0]
# Si el username ya existe, agregar un número
base_username = username
counter = 1
while User.objects.filter(username=username).exists():
username = f"{base_username}{counter}"
counter += 1
# Crear usuario
user = User.objects.create_user(
username=username,
email=email,
password=password,
first_name=name
)
form = UserRegisterForm(request.POST)
if form.is_valid():
name = form.cleaned_data.get("name")
email = form.cleaned_data.get("email")
password = form.cleaned_data.get("password")
client_ip = _get_client_ip(request)
audit_logger.info(
"REGISTER_SUCCESS user_id=%s username=%s email=%s ip=%s",
user.id,
user.username,
user.email,
client_ip,
)
# Validación email
if User.objects.filter(email=email).exists():
audit_logger.warning("REGISTER_FAILED email=%s reason=email_exists ip=%s", email, client_ip)
messages.error(request, "Ya existe un usuario con este correo electrónico")
return render(request, "tienda/register.html", {"form":form})
username = email.split("@")[0]
base_username = username
counter = 1
while User.objects.filter(username=username).exists():
username = f"{base_username}{counter}"
counter += 1
user = User.objects.create_user(
username = username,
email = email,
password = password,
first_name = name
)
audit_logger.info(
"REGISTER_SUCCESS user_id=%s username=%s email=%s ip=%s",
user.id,
user.username,
user.email,
client_ip,
)
tasks.enviar_correo_confirmacion.delay(user.id)
messages.success(request, f"¡Cuenta creada exitosamente! Por favor, verifica tu correo entrando al Link enviado.")
return redirect("index")
return render(request, "tienda/register.html")
tasks.enviar_correo_confirmacion.delay(user.id)
messages.success(request, f"¡Cuenta creada exitosamente! Por favor, verifica tu correo entrando al Link enviado.")
return redirect("index")
else:
form = UserRegisterForm()
return render(request, "tienda/register.html", {"form":form})
def logout(request: HttpRequest):
@@ -957,94 +950,32 @@ def enviar_mensaje_pedido(request: HttpRequest, item_id: int):
@login_required
def crear_producto(request: HttpRequest):
"""Crea un nuevo producto"""
if request.method == "POST":
name = request.POST.get("name")
briefdesc = request.POST.get("briefdesc")
description = request.POST.get("description")
price = request.POST.get("price")
stock = request.POST.get("stock")
category_id = request.POST.get("category")
primary_image_file = request.FILES.get("primary_image")
secondary_images_files = request.FILES.getlist("secondary_images")
# Validaciones
if not all([name, description, price, stock, category_id]):
messages.error(request, "Por favor completa todos los campos obligatorios.")
categories = Category.objects.all()
return render(request, "tienda/crear_producto.html", {"categories": categories})
try:
price = float(price)
if price < 0:
raise ValueError("El precio no puede ser negativo")
except ValueError:
messages.error(request, "El precio debe ser un número válido.")
categories = Category.objects.all()
return render(request, "tienda/crear_producto.html", {"categories": categories})
try:
stock = int(stock)
if stock < 0:
raise ValueError("El stock no puede ser negativo")
except ValueError:
messages.error(request, "El stock debe ser un número entero válido.")
categories = Category.objects.all()
return render(request, "tienda/crear_producto.html", {"categories": categories})
try:
category = Category.objects.get(id=category_id)
except Category.DoesNotExist:
messages.error(request, "Categoría no válida.")
categories = Category.objects.all()
return render(request, "tienda/crear_producto.html", {"categories": categories})
# Crear imagen principal si se proporciona
primary_image = None
if primary_image_file:
primary_image = Image.objects.create(
name=f"{name}_principal",
image=primary_image_file
)
if stock > 4294967295:
messages.error(request, "No se puede tener mas de 4294967295 existencias. Por favor, intentelo de nuevo")
categories = Category.objects.all()
return render(request, "tienda/crear_producto.html", {"categories": categories})
# Crear producto
try:
producto = Product.objects.create(
name=name,
briefdesc=briefdesc or "",
description=description,
price=price,
stock=stock,
category=category,
primary_image=primary_image,
creator=request.user
)
except DataError as e:
logger.exception("ERROR Creating product: " + str(e))
messages.error(request, "Se ha excedido el limite de 1000 caracteres en Descripción corta o el limite de 5000 caracteres en Descripción.")
categories = Category.objects.all()
return render(request, "tienda/crear_producto.html", {"categories": categories})
_invalidate_product_cache([producto.id])
# Agregar imágenes secundarias si se proporcionan
if secondary_images_files:
for idx, img_file in enumerate(secondary_images_files):
secondary_img = Image.objects.create(
name=f"{name}_secundaria_{idx+1}",
image=img_file
form = ProductForm(request.POST, request.FILES)
if form.is_valid():
primary_image_file = form.cleaned_data.get("primary_image")
image = None
if primary_image_file:
image = Image(
name = f"{form.cleaned_data['name']}_principal",
image = primary_image_file,
)
producto.secondary_images.add(secondary_img)
messages.success(request, f"¡Producto '{name}' creado exitosamente!")
return redirect("mis_productos")
# GET request - mostrar formulario
categories = Category.objects.all()
return render(request, "tienda/crear_producto.html", {"categories": categories})
image.save()
producto: Product = Product(
name = form.cleaned_data["name"],
briefdesc = form.cleaned_data["briefdesc"],
description = form.cleaned_data["description"],
price = form.cleaned_data["price"],
stock = form.cleaned_data["stock"],
category = form.cleaned_data["category"],
primary_image = image,
creator = request.user
)
producto.save()
return redirect("/")
else:
form = ProductForm()
return render(request, "tienda/crear_producto.html", {"form":form})
@login_required
def editar_producto(request: HttpRequest, id: int):
@@ -1052,96 +983,55 @@ def editar_producto(request: HttpRequest, id: int):
producto = get_object_or_404(Product, id=id, creator=request.user)
if request.method == "POST":
name = request.POST.get("name")
briefdesc = request.POST.get("briefdesc")
description = request.POST.get("description")
price = request.POST.get("price")
stock = request.POST.get("stock")
category_id = request.POST.get("category")
primary_image_file = request.FILES.get("primary_image")
secondary_images_files = request.FILES.getlist("secondary_images")
form = ProductEditForm(request.POST, request.FILES)
if form.is_valid():
producto.name = form.cleaned_data["name"]
producto.briefdesc = form.cleaned_data.get("briefdesc", "") or ""
producto.description = form.cleaned_data["description"]
producto.price = form.cleaned_data["price"]
producto.stock = form.cleaned_data["stock"]
producto.category = form.cleaned_data["category"]
if not all([name, description, price, stock, category_id]):
messages.error(request, "Por favor completa todos los campos obligatorios.")
categories = Category.objects.all()
return render(request, "tienda/editar_producto.html", {
"categories": categories,
"producto": producto
})
primary_image_file = request.FILES.get("primary_image")
secondary_images_files = request.FILES.getlist("secondary_images")
try:
price = float(price)
if price < 0:
raise ValueError("El precio no puede ser negativo")
except ValueError:
messages.error(request, "El precio debe ser un número válido.")
categories = Category.objects.all()
return render(request, "tienda/editar_producto.html", {
"categories": categories,
"producto": producto
})
try:
stock = int(stock)
if stock < 0:
raise ValueError("El stock no puede ser negativo")
if stock > 4294967295:
messages.error(request, "No se puede tener mas de 4294967295 de stock.")
categories = Category.objects.all()
return render(request, "tienda/editar_producto.html", {
"categories": categories,
"producto": producto
})
except ValueError:
messages.error(request, "El stock debe ser un número entero válido.")
categories = Category.objects.all()
return render(request, "tienda/editar_producto.html", {
"categories": categories,
"producto": producto
})
try:
category = Category.objects.get(id=category_id)
except Category.DoesNotExist:
messages.error(request, "Categoría no válida.")
categories = Category.objects.all()
return render(request, "tienda/editar_producto.html", {
"categories": categories,
"producto": producto
})
producto.name = name
producto.briefdesc = briefdesc or ""
producto.description = description
producto.price = price
producto.stock = stock
producto.category = category
if primary_image_file:
primary_image = Image.objects.create(
name=f"{name}_principal",
image=primary_image_file
)
producto.primary_image = primary_image
producto.save()
_invalidate_product_cache([producto.id])
if secondary_images_files:
producto.secondary_images.clear()
for idx, img_file in enumerate(secondary_images_files):
secondary_img = Image.objects.create(
name=f"{name}_secundaria_{idx+1}",
image=img_file
if primary_image_file:
primary_image = Image.objects.create(
name=f"{producto.name}_principal",
image=primary_image_file
)
producto.secondary_images.add(secondary_img)
producto.primary_image = primary_image
messages.success(request, f"¡Producto '{name}' actualizado exitosamente!")
return redirect("mis_productos")
producto.save()
_invalidate_product_cache([producto.id])
if secondary_images_files:
producto.secondary_images.clear()
for idx, img_file in enumerate(secondary_images_files):
secondary_img = Image.objects.create(
name=f"{producto.name}_secundaria_{idx+1}",
image=img_file
)
producto.secondary_images.add(secondary_img)
messages.success(request, f"¡Producto '{producto.name}' actualizado exitosamente!")
return redirect("mis_productos")
else:
messages.error(request, "Por favor completa todos los campos obligatorios.")
else:
initial = {
"name": producto.name,
"briefdesc": producto.briefdesc,
"description": producto.description,
"price": producto.price,
"stock": producto.stock,
"category": producto.category,
}
form = ProductEditForm(initial=initial)
categories = Category.objects.all()
return render(request, "tienda/editar_producto.html", {
"categories": categories,
"form": form,
"producto": producto
})
@@ -1160,6 +1050,55 @@ def borrar_producto(request: HttpRequest, id: int):
messages.success(request, f"Producto '{nombre}' eliminado correctamente.")
return redirect("mis_productos")
@login_required
def gestionar_imagenes(request: HttpRequest, id: int):
"""Gestiona las imágenes secundarias de un producto"""
producto = get_object_or_404(Product, id=id, creator=request.user)
secondary_images = producto.secondary_images.all()
form = SecondaryImageForm()
if request.method == "POST":
form = SecondaryImageForm(request.POST, request.FILES)
if form.is_valid():
image = Image(
name = f"{producto.name}_secundaria_{secondary_images.count() + 1}",
image = form.cleaned_data["image"],
alt = form.cleaned_data.get("alt", "")
)
image.save()
producto.secondary_images.add(image)
_invalidate_product_cache([producto.id])
messages.success(request, "Imagen añadida correctamente.")
return redirect("gestionar_imagenes", id=producto.id)
return render(request, "tienda/gestionar_imagenes.html", {
"producto": producto,
"secondary_images": secondary_images,
"form": form
})
@login_required
def eliminar_imagen_secundaria(request: HttpRequest, product_id: int, image_id: int):
"""Elimina una imagen secundaria de un producto"""
if request.method != "POST":
messages.error(request, "Acción no permitida.")
return redirect("gestionar_imagenes", id=product_id)
producto = get_object_or_404(Product, id=product_id, creator=request.user)
image = get_object_or_404(Image, id=image_id)
if not producto.secondary_images.filter(id=image_id).exists():
messages.error(request, "Esta imagen no pertenece al producto.")
return redirect("gestionar_imagenes", id=product_id)
producto.secondary_images.remove(image)
image.delete()
_invalidate_product_cache([producto.id])
messages.success(request, "Imagen eliminada correctamente.")
return redirect("gestionar_imagenes", id=product_id)
@login_required
def checkout(request: HttpRequest):
cart = get_or_create_cart(request)
@@ -2106,58 +2045,59 @@ def mis_recibos(request: HttpRequest):
def editar_perfil(request: HttpRequest):
"""Edita la información del perfil del usuario"""
if request.method == "POST":
first_name = request.POST.get("first_name", "").strip()
last_name = request.POST.get("last_name", "").strip()
email = request.POST.get("email", "").strip()
# Validar email único (excepto el propio)
if email != request.user.email and User.objects.filter(email=email).exists():
messages.error(request, "Ya existe un usuario con este correo electrónico.")
return render(request, "tienda/editar_perfil.html")
# Actualizar usuario
request.user.first_name = first_name
request.user.last_name = last_name
request.user.email = email
request.user.save()
messages.success(request, "Perfil actualizado correctamente.")
return redirect("portal_usuario")
form = EditProfileForm(request.POST)
if form.is_valid():
email = form.cleaned_data["email"]
if email != request.user.email and User.objects.filter(email=email).exists():
messages.error(request, "Ya existe un usuario con este correo electrónico.")
return render(request, "tienda/editar_perfil.html", {"form": form})
request.user.first_name = form.cleaned_data["first_name"]
request.user.last_name = form.cleaned_data["last_name"]
request.user.email = email
request.user.save()
messages.success(request, "Perfil actualizado correctamente.")
return redirect("portal_usuario")
else:
initial = {
"first_name": request.user.first_name,
"last_name": request.user.last_name,
"email": request.user.email,
}
form = EditProfileForm(initial=initial)
return render(request, "tienda/editar_perfil.html")
return render(request, "tienda/editar_perfil.html", {"form": form})
@login_required
def cambiar_contrasena(request: HttpRequest):
"""Cambia la contraseña del usuario"""
if request.method == "POST":
current_password = request.POST.get("current_password")
new_password = request.POST.get("new_password")
confirm_password = request.POST.get("confirm_password")
# Verificar contraseña actual
if not request.user.check_password(current_password):
messages.error(request, "La contraseña actual es incorrecta.")
return render(request, "tienda/editar_perfil.html")
# Validar nueva contraseña
if new_password != confirm_password:
messages.error(request, "Las contraseñas nuevas no coinciden.")
return render(request, "tienda/editar_perfil.html")
if len(new_password) < 8:
messages.error(request, "La contraseña debe tener al menos 8 caracteres.")
return render(request, "tienda/editar_perfil.html")
# Cambiar contraseña
request.user.set_password(new_password)
request.user.save()
# Mantener la sesión activa
auth_login(request, request.user)
messages.success(request, "Contraseña actualizada correctamente.")
return redirect("portal_usuario")
form = ChangePasswordForm(request.POST)
if form.is_valid():
current_password = form.cleaned_data["current_password"]
new_password = form.cleaned_data["new_password"]
if not request.user.check_password(current_password):
messages.error(request, "La contraseña actual es incorrecta.")
return render(request, "tienda/editar_perfil.html", {"password_form": ChangePasswordForm()})
if len(new_password) < 8:
messages.error(request, "La contraseña debe tener al menos 8 caracteres.")
return render(request, "tienda/editar_perfil.html", {"password_form": ChangePasswordForm()})
request.user.set_password(new_password)
request.user.save()
auth_login(request, request.user)
messages.success(request, "Contraseña actualizada correctamente.")
return redirect("portal_usuario")
else:
messages.error(request, "Las contraseñas nuevas no coinciden o son inválidas.")
return render(request, "tienda/editar_perfil.html", {"password_form": form})
return redirect("editar_perfil")
@@ -2176,45 +2116,39 @@ def direcciones_usuario(request: HttpRequest):
def crear_direccion(request: HttpRequest):
"""Crea una nueva dirección de entrega"""
if request.method == "POST":
full_name = request.POST.get("full_name", "").strip()
address_line_1 = request.POST.get("address_line_1", "").strip()
address_line_2 = request.POST.get("address_line_2", "").strip()
city = request.POST.get("city", "").strip()
postal_code = request.POST.get("postal_code", "").strip()
country = SHIPPING_COUNTRY
phone = request.POST.get("phone", "").strip()
is_default = request.POST.get("is_default") == "on"
# Validaciones
if not all([full_name, address_line_1, city, postal_code, phone]):
form = ShippingAddressForm(request.POST)
if form.is_valid():
city = form.cleaned_data["city"]
postal_code = form.cleaned_data["postal_code"]
if not _is_almeria_city(city):
messages.error(request, "El pueblo/ciudad debe pertenecer a la provincia de Almería.")
return render(request, "tienda/editar_direccion.html", _address_form_context(form=form))
if not _is_almeria_postal_code(postal_code):
messages.error(request, "Solo realizamos envíos en la provincia de Almería (código postal 04xxx).")
return render(request, "tienda/editar_direccion.html", _address_form_context(form=form))
ShippingAddress.objects.create(
user=request.user,
full_name=form.cleaned_data["full_name"],
address_line_1=form.cleaned_data["address_line_1"],
address_line_2=form.cleaned_data.get("address_line_2", "") or "",
city=city,
postal_code=postal_code,
country=SHIPPING_COUNTRY,
phone=form.cleaned_data["phone"],
is_default=form.cleaned_data.get("is_default", False)
)
messages.success(request, "Dirección creada correctamente.")
return redirect("direcciones_usuario")
else:
messages.error(request, "Por favor completa todos los campos obligatorios.")
return render(request, "tienda/editar_direccion.html", _address_form_context(request.POST))
if not _is_almeria_city(city):
messages.error(request, "El pueblo/ciudad debe pertenecer a la provincia de Almería.")
return render(request, "tienda/editar_direccion.html", _address_form_context(request.POST))
if not _is_almeria_postal_code(postal_code):
messages.error(request, "Solo realizamos envíos en la provincia de Almería (código postal 04xxx).")
return render(request, "tienda/editar_direccion.html", _address_form_context(request.POST))
# Crear dirección
ShippingAddress.objects.create(
user=request.user,
full_name=full_name,
address_line_1=address_line_1,
address_line_2=address_line_2,
city=city,
postal_code=postal_code,
country=country,
phone=phone,
is_default=is_default
)
messages.success(request, "Dirección creada correctamente.")
return redirect("direcciones_usuario")
else:
form = ShippingAddressForm()
return render(request, "tienda/editar_direccion.html", _address_form_context())
return render(request, "tienda/editar_direccion.html", _address_form_context(form=form))
@login_required
@@ -2223,34 +2157,47 @@ def editar_direccion(request: HttpRequest, id: int):
direccion = get_object_or_404(ShippingAddress, id=id, user=request.user)
if request.method == "POST":
direccion.full_name = request.POST.get("full_name", "").strip()
direccion.address_line_1 = request.POST.get("address_line_1", "").strip()
direccion.address_line_2 = request.POST.get("address_line_2", "").strip()
direccion.city = request.POST.get("city", "").strip()
direccion.postal_code = request.POST.get("postal_code", "").strip()
direccion.country = SHIPPING_COUNTRY
direccion.phone = request.POST.get("phone", "").strip()
direccion.is_default = request.POST.get("is_default") == "on"
# Validaciones
if not all([direccion.full_name, direccion.address_line_1, direccion.city,
direccion.postal_code, direccion.phone]):
form = ShippingAddressForm(request.POST)
if form.is_valid():
city = form.cleaned_data["city"]
postal_code = form.cleaned_data["postal_code"]
if not _is_almeria_city(city):
messages.error(request, "El pueblo/ciudad debe pertenece a la provincia de Almería.")
return render(request, "tienda/editar_direccion.html", _address_form_context(direccion, form=form))
if not _is_almeria_postal_code(postal_code):
messages.error(request, "Solo realizamos envíos en la provincia de Almería (código postal 04xxx).")
return render(request, "tienda/editar_direccion.html", _address_form_context(direccion, form=form))
direccion.full_name = form.cleaned_data["full_name"]
direccion.address_line_1 = form.cleaned_data["address_line_1"]
direccion.address_line_2 = form.cleaned_data.get("address_line_2", "") or ""
direccion.city = city
direccion.postal_code = postal_code
direccion.country = SHIPPING_COUNTRY
direccion.phone = form.cleaned_data["phone"]
direccion.is_default = form.cleaned_data.get("is_default", False)
direccion.save()
messages.success(request, "Dirección actualizada correctamente.")
return redirect("direcciones_usuario")
else:
messages.error(request, "Por favor completa todos los campos obligatorios.")
return render(request, "tienda/editar_direccion.html", _address_form_context(direccion))
if not _is_almeria_city(direccion.city):
messages.error(request, "El pueblo/ciudad debe pertenecer a la provincia de Almería.")
return render(request, "tienda/editar_direccion.html", _address_form_context(direccion))
if not _is_almeria_postal_code(direccion.postal_code):
messages.error(request, "Solo realizamos envíos en la provincia de Almería (código postal 04xxx).")
return render(request, "tienda/editar_direccion.html", _address_form_context(direccion))
direccion.save()
messages.success(request, "Dirección actualizada correctamente.")
return redirect("direcciones_usuario")
else:
initial = {
"full_name": direccion.full_name,
"address_line_1": direccion.address_line_1,
"address_line_2": direccion.address_line_2,
"city": direccion.city,
"postal_code": direccion.postal_code,
"country": direccion.country,
"phone": direccion.phone,
"is_default": direccion.is_default,
}
form = ShippingAddressForm(initial=initial)
return render(request, "tienda/editar_direccion.html", _address_form_context(direccion))
return render(request, "tienda/editar_direccion.html", _address_form_context(direccion, form=form))
@login_required
@@ -2336,10 +2283,13 @@ def ayuda(request: HttpRequest):
def reset_password(request: HttpRequest):
if request.method == "GET":
return render(request, "tienda/reset_password.html", {})
form = ResetPasswordForm()
return render(request, "tienda/reset_password.html", {"form": form})
else:
tasks.enviar_correo_recuperacion.delay(request.POST["email"])
messages.info(request, "Si tienes una cuenta con ese correo electronico, se ha enviado un correo con un enlace")
form = ResetPasswordForm(request.POST)
if form.is_valid():
tasks.enviar_correo_recuperacion.delay(form.cleaned_data["email"])
messages.info(request, "Si tienes una cuenta con ese correo electronico, se ha enviado un correo con un enlace")
return render(request, "tienda/index.html", {})
def reset_password_phase2(request: HttpRequest, code: str):
@@ -2352,22 +2302,19 @@ def reset_password_phase2(request: HttpRequest, code: str):
if request.method == "GET":
return render(request, "tienda/reset_password_phase2.html", {
"code": code
})
form = ResetPasswordPhase2Form()
return render(request, "tienda/reset_password_phase2.html", {"form": form, "code": code})
elif request.method == "POST":
password = request.POST["password"]
vpassword = request.POST["verify_password"]
if password != vpassword:
form = ResetPasswordPhase2Form(request.POST)
if form.is_valid():
user = ver_code.user
user.set_password(form.cleaned_data["password"])
user.save()
ver_code.delete()
messages.success(request, "Se ha cambiado la contraseña!")
return redirect(reverse("index"))
else:
messages.error(request, "Las contraseñas no coinciden")
return render(request, "tienda/reset_password_phase2.html", {"code": code})
user = ver_code.user
user.set_password(password)
user.save()
ver_code.delete() # Delete Verification code after changing password
messages.success(request, "Se ha cambiado la contraseña!")
return redirect(reverse("index"))
return render(request, "tienda/reset_password_phase2.html", {"form": form, "code": code})
else:
raise Http404()