From 301b8f9b34e7f61764a779785abc3c503f7055a6 Mon Sep 17 00:00:00 2001 From: Pablo Moreno Date: Sun, 19 Jan 2025 12:43:13 +0100 Subject: [PATCH] feat: improve customer address views --- config/settings/base.py | 3 +- theme/templates/theme/base.html | 2 + ...bsettings_logo_240_websettings_logo_256.py | 24 ++++++++++ web/mixins.py | 46 ++++++++++++++++++- web/models.py | 25 +++++++++- web/templates/users/user_details.html | 4 +- web/tests/test_login.py | 4 +- web/tests/test_manifest.py | 7 +-- web/urls.py | 19 ++++++-- web/views/components.py | 16 +++++-- web/views/users.py | 10 ++-- web/views/web.py | 24 +++++++--- 12 files changed, 157 insertions(+), 27 deletions(-) create mode 100644 web/migrations/0002_remove_websettings_logo_240_websettings_logo_256.py diff --git a/config/settings/base.py b/config/settings/base.py index 5b36c59..4169e42 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -1,8 +1,9 @@ import datetime -from config.settings.environ import * # noqa from django.utils.text import gettext_lazy as _ +from config.settings.environ import * # noqa + APP_NAME = "Shop" DESCRIPTION = "" VERSION = "0.1.0" diff --git a/theme/templates/theme/base.html b/theme/templates/theme/base.html index 6120cb0..47cb67d 100644 --- a/theme/templates/theme/base.html +++ b/theme/templates/theme/base.html @@ -6,6 +6,8 @@ {{ title }} + + {% include "theme/seo.html" %} diff --git a/web/migrations/0002_remove_websettings_logo_240_websettings_logo_256.py b/web/migrations/0002_remove_websettings_logo_240_websettings_logo_256.py new file mode 100644 index 0000000..92d8afd --- /dev/null +++ b/web/migrations/0002_remove_websettings_logo_240_websettings_logo_256.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.4 on 2025-01-19 10:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("web", "0001_initial"), + ] + + operations = [ + migrations.RemoveField( + model_name="websettings", + name="logo_240", + ), + migrations.AddField( + model_name="websettings", + name="logo_256", + field=models.ImageField( + blank=True, null=True, upload_to="assets", verbose_name="logo 256x256" + ), + ), + ] diff --git a/web/mixins.py b/web/mixins.py index 7ba84fd..d75c4c8 100644 --- a/web/mixins.py +++ b/web/mixins.py @@ -1,6 +1,9 @@ from django.conf import settings from django.core.paginator import Paginator -from django.utils.text import gettext_lazy as _ +from django import forms +from django.db import models +from django.views.generic import TemplateView +from django.shortcuts import get_object_or_404, render class FilteredQuerysetMixin: @@ -55,3 +58,44 @@ class StylingMixin: "placeholder": self.placeholder_for_field.get(field_name), } ) + + +class ReadUpdateView(TemplateView): + model: models.Model = None + form_class: forms.ModelForm = None + template_name: str = "" + ok_template_name: str = "" + error_template_name: str = "" + + def get_model(self): + return self.model + + def get_object(self): + pk = self.kwargs.get('pk') + cls = self.get_model() + return get_object_or_404(cls, pk=pk) + + def get_initial_values(self, instance): + return {} + + def get_context_data(self, **kwargs): + instance = self.get_object() + form = self.form_class(initial=self.get_initial_values(instance)) + + return { + "form": form, + } + + def post(self, request, *args, **kwargs): + form = self.form_class(request.POST) + + if form.is_valid(): + instance = form.save() + return render(request, self.ok_template_name, { + "form": form, + "instance": instance, + }) + + return render(request, self.error_template_name, { + "form": form, + }) diff --git a/web/models.py b/web/models.py index e6cdcbc..738ec24 100644 --- a/web/models.py +++ b/web/models.py @@ -1,5 +1,9 @@ +from io import BytesIO + +from django.core.files.base import ContentFile from django.db import models from django.utils.text import gettext_lazy as _ +from PIL import Image from config.models import SingletonModel @@ -75,13 +79,30 @@ class WebSettings(SingletonModel): theme_color = models.CharField( max_length=9, default="", blank=True, null=True, verbose_name=_("color de tema") ) - logo_240 = models.ImageField( - upload_to="assets", blank=True, null=True, verbose_name=_("logo 240x240") + + logo_256 = models.ImageField( + upload_to="assets", blank=True, null=True, verbose_name=_("logo 256x256") ) logo_128 = models.ImageField( upload_to="assets", blank=True, null=True, verbose_name=_("logo 128x128") ) + def get_resized_logo(self, size): + width, height = size + image = Image.open(self.logo) + image = image.resize(size, Image.Resampling.LANCZOS) + buffer = BytesIO() + image.save(fp=buffer, format="PNG") + + return ContentFile(buffer.getvalue(), name=f"logo_{width}.png") + + def save(self, *args, **kwargs): + if self.logo: + self.logo_128 = self.get_resized_logo((128, 128)) + self.logo_256 = self.get_resized_logo((256, 256)) + + super().save(*args, **kwargs) + class Meta: verbose_name = _("ajustes de la web") verbose_name_plural = _("ajustes de la web") diff --git a/web/templates/users/user_details.html b/web/templates/users/user_details.html index c15a66c..cbdc187 100644 --- a/web/templates/users/user_details.html +++ b/web/templates/users/user_details.html @@ -31,7 +31,9 @@ {% translate 'No hay ninguna dirección' %} {% endif %} - {% include 'components/users/retrieve_update_address.html' %} +
+ +
diff --git a/web/tests/test_login.py b/web/tests/test_login.py index 12d84ba..e53dd88 100644 --- a/web/tests/test_login.py +++ b/web/tests/test_login.py @@ -122,7 +122,9 @@ class TestLogin(TestCase): ) assert response.status_code == 302 - assert response.url == f'{reverse("web:index")}?next={reverse("web:my_account")}' + assert ( + response.url == f'{reverse("web:index")}?next={reverse("web:my_account")}' + ) response = self.client.get(response.url) assert response.status_code == 302 diff --git a/web/tests/test_manifest.py b/web/tests/test_manifest.py index f6fe1dd..4ce126a 100644 --- a/web/tests/test_manifest.py +++ b/web/tests/test_manifest.py @@ -16,11 +16,8 @@ class TestManifest(TestCase): os.path.join(settings.BASE_DIR, "web", "tests", "images", "trolley.png"), "rb", ) as f: - file_128 = SimpleUploadedFile("logo_128.png", f.read()) - self.settings.logo_128 = file_128 - file_240 = SimpleUploadedFile("logo_240.png", f.read()) - self.settings.logo_240 = file_240 - self.settings.logo = file_240 + file = SimpleUploadedFile("logo.png", f.read()) + self.settings.logo = file self.settings.save() def test_manifest_view(self): diff --git a/web/urls.py b/web/urls.py index f9f380f..a918c08 100644 --- a/web/urls.py +++ b/web/urls.py @@ -6,10 +6,12 @@ from django.utils.text import gettext_lazy as _ from web.views.components import ( cart, cart_dropdown, + list_customer_addresses_component, list_products, list_wishlisted_products, + update_customer_address, + user_info_component, wishlist_button, - user_info_component, update_customer_address, ) from web.views.users import ( login, @@ -39,7 +41,9 @@ app_name = "web" i18n_resolvers = i18n_patterns( path("", index, name="index"), - path(_("wishlist"), login_required(wishlist, login_url=_("/login/")), name="wishlist"), + path( + _("wishlist"), login_required(wishlist, login_url=_("/login/")), name="wishlist" + ), path(_("categories//"), category_view, name="category_view"), path(_("products///"), product_detail, name="product_detail"), path(_("cart/"), cart_detail, name="cart_detail"), @@ -96,7 +100,16 @@ urlpatterns = [ delete_from_wishlist, name="delete_from_wishlist", ), - path("web/customer-address//", update_customer_address, name="update_customer_address"), + path( + "web/customer-address//", + update_customer_address, + name="update_customer_address", + ), + path( + "web/customer-addresses/", + list_customer_addresses_component, + name="list_customer_addresses_component", + ), # manifest path("manifest.json", manifest, name="manifest_json"), ] diff --git a/web/views/components.py b/web/views/components.py index ddbd7be..647cbbc 100644 --- a/web/views/components.py +++ b/web/views/components.py @@ -77,7 +77,7 @@ def cart_dropdown(request, *args, **kwargs): ) if not request.user.is_authenticated: - response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid) + response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid, samesite="strict") return response @@ -127,7 +127,7 @@ def cart(request, *args, **kwargs): ) if not request.user.is_authenticated: - response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid) + response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid, samesite="strict") return response @@ -179,13 +179,22 @@ class UserInfoComponentView(TemplateView): return render(request, self.template_name, {"form": form}) +class ListCustomerAddressComponent(TemplateView): + template_name = "components/users/retrieve_update_address.html" + + def get_context_data(self, **kwargs): + return { + "customer_addresses": CustomerAddress.objects.filter(user=self.request.user) + } + + def update_customer_address(request, pk): customer_address = get_object_or_404(CustomerAddress, pk=pk, user=request.user) form = CustomerAddressForm(request.POST, instance=customer_address) if form.is_valid(): form.save() - return HttpResponse(status=200) + return HttpResponse(status=200, headers={"HX-Trigger": "updated_addresses"}) return HttpResponse(form.errors, status=400) @@ -195,3 +204,4 @@ list_wishlisted_products = ListWishlistedProducts.as_view() wishlist_button = WishlistButton.as_view() user_info_component = UserInfoComponentView.as_view() +list_customer_addresses_component = ListCustomerAddressComponent.as_view() diff --git a/web/views/users.py b/web/views/users.py index d66b2fd..9a0241f 100644 --- a/web/views/users.py +++ b/web/views/users.py @@ -6,7 +6,11 @@ from django.contrib.auth.tokens import default_token_generator from django.core.exceptions import ValidationError from django.db import IntegrityError from django.shortcuts import redirect, render, reverse -from django.utils.http import url_has_allowed_host_and_scheme, urlsafe_base64_decode, urlencode +from django.utils.http import ( + url_has_allowed_host_and_scheme, + urlencode, + urlsafe_base64_decode, +) from django.utils.text import gettext_lazy as _ from django.views.generic import TemplateView, View @@ -30,10 +34,10 @@ class LoginView(View, RedirectionMixin): login_form_class = LoginForm def get_redirection(self): - next_page = self.request.GET.get('next', '') + next_page = self.request.GET.get("next", "") if next_page: - return f'{reverse(self.redirect_to)}?next={next_page}' + return f"{reverse(self.redirect_to)}?next={next_page}" return reverse(self.redirect_to) diff --git a/web/views/web.py b/web/views/web.py index 0d3126a..85b7fea 100644 --- a/web/views/web.py +++ b/web/views/web.py @@ -36,8 +36,8 @@ class IndexView(TemplateView): } def get(self, request, *args, **kwargs): - if self.request.GET.get('next'): - return redirect(self.request.GET.get('next')) + if self.request.GET.get("next"): + return redirect(self.request.GET.get("next")) return super().get(request, *args, **kwargs) @@ -230,7 +230,7 @@ def add_cart_item(request, *args, **kwargs): response = HttpResponse(status=201, headers={"HX-Trigger": "updated-cart"}) if created and request.user.is_anonymous: - response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid) + response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid, samesite="strict") # Comprobamos si existe una línea de carrito para ese carrito de ese producto existing_cart_item = CartItem.objects.filter(cart=cart, product=product).first() @@ -309,11 +309,21 @@ def manifest(request, *args, **kwargs): "orientation": "portrait", } - if settings.logo_240: + if settings.logo: data["icons"].append( { - "src": settings.logo_240.url, - "sizes": "240x240", + "src": settings.logo.url, + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable any", + } + ) + + if settings.logo_256: + data["icons"].append( + { + "src": settings.logo_256.url, + "sizes": "256x256", "type": "image/png", "purpose": "maskable any", } @@ -323,7 +333,7 @@ def manifest(request, *args, **kwargs): data["icons"].append( { "src": settings.logo_128.url, - "sizes": "240x240", + "sizes": "128x128", "type": "image/png", "purpose": "maskable any", }