diff --git a/ProyectoFinalDjango b/ProyectoFinalDjango new file mode 160000 index 0000000..7ba05f8 --- /dev/null +++ b/ProyectoFinalDjango @@ -0,0 +1 @@ +Subproject commit 7ba05f8c2fe5ea0ed5c12dc0365023dbee6cf278 diff --git a/app/forms.py b/app/forms.py index ed8ce76..f5c5218 100644 --- a/app/forms.py +++ b/app/forms.py @@ -7,11 +7,13 @@ # ------------------------------------------------------------------------------------------------------------------------------------------------------------ -# FORM DE PRODUCTO class ProductCreateForms(forms.ModelForm): class Meta: model = Producto fields = ("articulo", "seccion", "descripcion", "precio_unitario", "imagen") + widgets = { + 'seccion': forms.Select(attrs={'class': 'form-select'}), + } # ------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -56,3 +58,33 @@ class UserEditForm(UserCreationForm): class Meta: model = User fields = ["email", "password1", "password2", "last_name", "first_name"] + +#---------------------------- + +from django import forms +from .models import Comentario + +class ComentarioForm(forms.ModelForm): + class Meta: + model = Comentario + fields = ['texto'] + +#---------------------- + +from django.views import View +from django.shortcuts import render, redirect + +class ProductCreateView(View): + def get(self, request, *args, **kwargs): + form = ProductCreateForms() + print(form.fields['seccion'].widget) # <-- para verificar que es un Select + context = {'form': form} + return render(request, 'crear_producto.html', context) + + def post(self, request): + form = ProductCreateForms(request.POST, request.FILES) + if form.is_valid(): + form.save() + return redirect('app:productos') + context = {'form': form} + return render(request, 'crear_producto.html', context) diff --git a/app/models.py b/app/models.py index 5d1a2f5..dca0624 100644 --- a/app/models.py +++ b/app/models.py @@ -1,7 +1,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models - -# Create your models here. +from django.contrib.auth.models import User # <-- Importa User +from datetime import datetime # Clase Cliente @@ -15,11 +15,12 @@ class Cliente(models.Model): def __str__(self) -> str: return f"Nombre: {self.nombre} | Apellido: {self.apellido} | Dirección: {self.direccion} | email: {self.email} | telefono: {self.telefono}" +# ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------------------------------------------------------------------------------------ - +from django.db import models +from django.contrib.auth.models import User +from django.core.validators import MaxValueValidator, MinValueValidator -# Clase Producto class Producto(models.Model): GENERO_CHOICES = [ ("ACC", "Acción"), @@ -41,15 +42,13 @@ class Producto(models.Model): help_text="Puntaje de 1 a 5 estrellas", ) imagen = models.ImageField("Portada", null=True, blank=True) + usuario = models.ForeignKey(User, on_delete=models.CASCADE, related_name='productos', null=True, blank=True) + fecha_publicacion = models.DateTimeField(auto_now_add=True) def __str__(self) -> str: - return ( - f"{self.articulo} ({self.get_seccion_display()}) - {self.precio_unitario}★" - ) - - -# ------------------------------------------------------------------------------------------------------------------------------------------------------------ + return f"{self.articulo} ({self.get_seccion_display()}) - {self.precio_unitario}★" +# ------------------------------------------------------------------------------ # Clase Contacto (Feedback Clientes) class Contacto(models.Model): @@ -62,5 +61,16 @@ class Contacto(models.Model): def __str__(self) -> str: return f"Mensaje - Asunto: {self.asunto} | Mensaje: {self.mensaje}" +# ------------------------------------------------------------------------------ + +class Comentario(models.Model): + producto = models.ForeignKey(Producto, on_delete=models.CASCADE, related_name='comentarios') + texto = models.TextField() + creado_en = models.DateTimeField(auto_now_add=True) + usuario = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comentarios', null=True, blank=True) + + def __str__(self): + return f'Comentario en {self.producto.articulo} - {self.creado_en}' + + -# ------------------------------------------------------------------------------------------------------------------------------------------------------------ diff --git a/app/urls.py b/app/urls.py index 95e9a4c..37ed51e 100644 --- a/app/urls.py +++ b/app/urls.py @@ -1,9 +1,16 @@ # AGERGADO PARA EL LOGOUT -from django.contrib.auth.views import LogoutView +from django.contrib.auth.views import LogoutView, LoginView as auth_views_LoginView from django.urls import path +from . import views from app.views import * +# Import productos_list if it's defined elsewhere, otherwise define it in views.py +try: + from app.views import productos_list +except ImportError: + pass # productos_list should be defined in app/views.py + app_name = "app" urlpatterns = [ @@ -15,9 +22,12 @@ path("contacto/", ContactCreateView.as_view(), name="contacto"), path("/modificar/", ProductUpdateView.as_view(), name="modificar_producto"), path("/delete/", ProductDeleteView.as_view(), name="borrar_producto"), - path("login", login_request, name="login"), path("registro", register, name="registro"), path("editar_perfil", EditProfile, name="editar_perfil"), path("logout", LogoutView.as_view(), name="logout"), path("nosotros/", AboutUsView, name="nosotros"), + path('pelicula//', views.producto_detalle, name='producto_detalle'), + path('login/', auth_views_LoginView.as_view(template_name='login.html'), name='login'), ] + + diff --git a/app/views.py b/app/views.py index b34ecf6..fab13a9 100644 --- a/app/views.py +++ b/app/views.py @@ -14,17 +14,21 @@ from django.core.mail import EmailMessage from django.conf import settings +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin +from django.utils.decorators import method_decorator +from django.contrib.auth.models import User + + #---------------------------------------------------------------------------------------------- ## VISTAS DE CLIENTE # Vista de Clase que lista los clientes + class ClientListView(ListView): - def get(self,request): - clientes = Cliente.objects.all() - context = { - 'clientes':clientes - } - return render(request,'clientes_list.html',context) + def get(self, request): + clientes = User.objects.all() + return render(request, 'clientes_list.html', {'clientes': clientes}) + #---------------------------------------------------------------------------------------------- @@ -63,50 +67,48 @@ def ToFindProductView(request): # Vista de clase - permite crear un nuevo Producto -class ProductCreateView(View): - def get(self,request,*args,**kwargs): - #parte creada para llamar el contenido del en forms.py (donde generamos producto) + +class ProductCreateView(LoginRequiredMixin, View): + def get(self, request, *args, **kwargs): form = ProductCreateForms() - context={ - 'form':form - } - return render(request,'crear_producto.html',context) - def post(self,request): - if request.method=="POST": - form = ProductCreateForms(request.POST,request.FILES) - if form.is_valid(): - articulo = form.cleaned_data.get('articulo') - seccion = form.cleaned_data.get('seccion') - descripcion = form.cleaned_data.get('descripcion') - precio_unitario = form.cleaned_data.get('precio_unitario') - imagen = form.cleaned_data.get('imagen') + return render(request, 'crear_producto.html', {'form': form}) - p, created = Producto.objects.get_or_create(articulo=articulo,seccion=seccion,descripcion=descripcion,precio_unitario=precio_unitario,imagen=imagen) - p.save() - return redirect('app:productos') + def post(self, request): + form = ProductCreateForms(request.POST, request.FILES) + if form.is_valid(): + producto = form.save(commit=False) + producto.usuario = request.user # guardo usuario actual + producto.save() + return redirect('app:productos') + + return render(request, 'crear_producto.html', {'form': form}) - context = { - } - return render(request,'crear_producto.html',context) # Vista de clase que permite modificar (update) nuestro producto -class ProductUpdateView(UpdateView): + +class ProductUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView): model = Producto - fields = ['articulo','seccion','descripcion','precio_unitario','imagen'] + fields = ['articulo', 'seccion', 'descripcion', 'precio_unitario', 'imagen'] template_name = 'modifcar_producto.html' def get_success_url(self): - pk = self.kwargs['pk'] return reverse_lazy('app:productos') + def test_func(self): + producto = self.get_object() + return self.request.user == producto.usuario + -# Vista de clase que nos permite eliminar un producto -class ProductDeleteView(DeleteView): +class ProductDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView): model = Producto template_name = 'borrar_producto.html' success_url = reverse_lazy('app:productos') + def test_func(self): + producto = self.get_object() + return self.request.user == producto.usuario + #---------------------------------------------------------------------------------------------- ## VISTAS DE CONTACTO @@ -262,3 +264,58 @@ def AboutUsView(request): } return render(request,"nosotros.html",context) +#-------------------- +from django.shortcuts import render, get_object_or_404, redirect +from .models import Producto, Comentario +from django.contrib.auth.decorators import login_required +# Vista de detalle del producto + +def producto_detalle(request, pk): + producto = get_object_or_404(Producto, pk=pk) + comentarios = producto.comentarios.order_by('-creado_en') + + if request.method == 'POST': + if not request.user.is_authenticated: + # Redirigir a login o mostrar mensaje de error + return redirect(f'/login/?next=/app/pelicula/{pk}/') # o la url que uses + + texto = request.POST.get('comentario') + if texto: + Comentario.objects.create(producto=producto, texto=texto, usuario=request.user) + return redirect('app:producto_detalle', pk=pk) + + return render(request, 'producto_detalle.html', { + 'producto': producto, + 'comentarios': comentarios, + }) + + +#----------------------- +from django.shortcuts import render +from .models import Producto + +def productos_list(request): + orden = request.GET.get('orden', '') + + productos = Producto.objects.all() + + + if orden == 'fecha_asc': + productos = productos.order_by('fecha_publicacion') + elif orden == 'fecha_desc': + productos = productos.order_by('-fecha_publicacion') + elif orden == 'alf_asc': + productos = productos.order_by('articulo') + elif orden == 'alf_desc': + productos = productos.order_by('-articulo') + + context = { + 'productos': productos, + 'orden_actual': orden + } + + return render(request, 'app/productos_list.html', context) + + + + diff --git a/core/settings/base.py b/core/settings/base.py index dba9509..865f57a 100644 --- a/core/settings/base.py +++ b/core/settings/base.py @@ -99,4 +99,7 @@ # --- Crispy Forms --- CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap4" -CRISPY_TEMPLATE_PACK = "bootstrap4" \ No newline at end of file +CRISPY_TEMPLATE_PACK = "bootstrap4" + +LOGIN_URL = '/login/' +LOGIN_REDIRECT_URL = '/' diff --git a/core/urls.py b/core/urls.py index 66e97b4..b6c5459 100644 --- a/core/urls.py +++ b/core/urls.py @@ -24,12 +24,15 @@ # VISTAS LOGIN_REQUEST Y REGISTER AGREGAR EN VERSION FINAL!! from core.views import HomeView +from django.contrib.auth.views import LoginView urlpatterns = [ path("admin/", admin.site.urls), path("", HomeView, name="inicio"), # llamamos nuestra página de inicio - "hOME" path("app/", include("app.urls")), # incluímos url's de la app + path("login/", LoginView.as_view(template_name="login.html"), name="login"), ] if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + diff --git a/core/views.py b/core/views.py index 16ec197..280d54e 100644 --- a/core/views.py +++ b/core/views.py @@ -8,3 +8,4 @@ def HomeView(request): # ------------------------------------------------------------------------------ + diff --git a/media/1125031-highest-2-lowest-0-1000-0-1500-crop_GLkkv79.jpg b/media/1125031-highest-2-lowest-0-1000-0-1500-crop_GLkkv79.jpg new file mode 100644 index 0000000..09fd837 Binary files /dev/null and b/media/1125031-highest-2-lowest-0-1000-0-1500-crop_GLkkv79.jpg differ diff --git a/media/1125031-highest-2-lowest-0-1000-0-1500-crop_nX19tlW.jpg b/media/1125031-highest-2-lowest-0-1000-0-1500-crop_nX19tlW.jpg new file mode 100644 index 0000000..09fd837 Binary files /dev/null and b/media/1125031-highest-2-lowest-0-1000-0-1500-crop_nX19tlW.jpg differ diff --git a/media/972109-weapons-2025-0-1000-0-1500-crop_DAia36H.jpg b/media/972109-weapons-2025-0-1000-0-1500-crop_DAia36H.jpg new file mode 100644 index 0000000..a4daf70 Binary files /dev/null and b/media/972109-weapons-2025-0-1000-0-1500-crop_DAia36H.jpg differ diff --git a/templates/buscar_producto.html b/templates/buscar_producto.html index a33686d..32e4130 100644 --- a/templates/buscar_producto.html +++ b/templates/buscar_producto.html @@ -1,20 +1,27 @@ {% extends "padre.html" %} {% load static %} {% block loquecambia %} -

🔎

-

BÚSQUEDA

-
-
- - -
-
- -
-
+ +
+

🔎

+

BÚSQUEDA

+ +
+
+ + +
+ + +
- -{% endblock %} \ No newline at end of file + +{% endblock %} diff --git a/templates/clientes_list.html b/templates/clientes_list.html index 5179704..ecd669c 100644 --- a/templates/clientes_list.html +++ b/templates/clientes_list.html @@ -67,31 +67,25 @@

COMUNIDAD

Usuario - Nombre Apellido - Dirección + Nombre Email - Teléfono - {% for c in clientes %} - - {{ c.pk }} - {{ c.nombre }} - {{ c.apellido }} - {{ c.direccion }} - {{ c.email }} - {{ c.telefono }} - - {% empty %} - - - No hay usuarios registrados aún. - - - {% endfor %} - + {% for c in clientes %} + + {{ c.pk }} + {{ c.first_name }} + {{ c.last_name }} + {{ c.email }} + + {% empty %} + + No hay usuarios registrados aún. + + {% endfor %} +
diff --git a/templates/crear_producto.html b/templates/crear_producto.html index 2a51582..2147f26 100644 --- a/templates/crear_producto.html +++ b/templates/crear_producto.html @@ -47,27 +47,116 @@

Agregar Película

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/modifcar_producto.html b/templates/modifcar_producto.html index d6b4bf2..df634b1 100644 --- a/templates/modifcar_producto.html +++ b/templates/modifcar_producto.html @@ -16,8 +16,94 @@ + + + -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/producto_detalle.html b/templates/producto_detalle.html new file mode 100644 index 0000000..29c9e80 --- /dev/null +++ b/templates/producto_detalle.html @@ -0,0 +1,74 @@ +{% extends "padre.html" %} +{% block loquecambia %} + +
+

{{ producto.articulo }}

+ + {% if producto.imagen %} + {{ producto.articulo }} + {% endif %} + +

Género: {{ producto.get_seccion_display }}

+

Reseña: {{ producto.descripcion }}

+

Puntaje: {{ producto.precio_unitario }}/5

+ +
+ +

Comentarios

+
    + {% for comentario in comentarios %} +
  • + {{ comentario.usuario.username }} dijo:
    + {{ comentario.texto }}
    + {{ comentario.creado_en|date:"d/m/Y H:i" }} +
  • + {% empty %} +
  • + No hay comentarios aún. +
  • + {% endfor %} +
+ + {% if user.is_authenticated %} +
+ {% csrf_token %} +
+ + +
+ Por favor ingresa un comentario. +
+
+ + Volver +
+ {% else %} +

+ Inicia sesión para agregar comentarios. +

+ {% endif %} +
+ + + +{% endblock %} + diff --git a/templates/productos_list.html b/templates/productos_list.html index e71f8a0..162c466 100644 --- a/templates/productos_list.html +++ b/templates/productos_list.html @@ -1,22 +1,29 @@ {% extends "padre.html" %} {% block loquecambia %}

Listado de Películas

+
+ + + +
- Agregar Película 📝 + {% if user.is_authenticated %} + Agregar Película 📝 + {% else %} + Inicia sesión para agregar película + {% endif %} Buscar 🔍
@@ -82,7 +157,11 @@

Listado de Películas

{% endif %}
-
{{ p.articulo }}
+
+ + {{ p.articulo }} + +
{{ p.get_seccion_display }}

{{ p.descripcion }}

@@ -97,8 +176,12 @@
{{ p.articulo }}
@@ -107,3 +190,4 @@
{{ p.articulo }}
{% endblock %} + diff --git a/templates/resultado_buscar_producto.html b/templates/resultado_buscar_producto.html index 570177f..36bd244 100644 --- a/templates/resultado_buscar_producto.html +++ b/templates/resultado_buscar_producto.html @@ -1,49 +1,52 @@ {% extends "padre.html" %} {% block loquecambia %} -
-

Has buscado: {{query}}

+ +
+ +

Has buscado: {{query}}

{% if productos %} +

Películas encontradas: {{productos|length}} película{% if productos|length > 1 %}s{% endif %}

-

Películas encontradas: {{productos|length}} película

-
-
- - - - - - - - - +
+
Cód. película #PelículaGéneroReseñaPuntajePóster
+ + + + + + + + - {% for p in productos %} + {% for p in productos %} - - - - - - - + + + + + + + + {% endfor %} - {% endfor %}
Cód. película #PelículaGéneroReseñaPuntajePóster
00{{p.pk}}{{p.articulo}}{{p.seccion}}{{p.descripcion}} {{p.precio_unitario}}{{p.articulo}}
00{{p.pk}}{{p.articulo}}{{p.seccion}}{{p.descripcion}}{{p.precio_unitario}} + {{p.articulo}} +
-
+ {% else %} -

-

La película buscada no se encuentra.

- {% endif %} +

+

La película buscada no se encuentra.

+ {% endif %} +
- - + {% endblock %}