feat: added seo template and prioduct.slug

This commit is contained in:
2024-11-22 21:40:27 +01:00
parent 2d7119c6c1
commit b057b70a31
13 changed files with 644 additions and 33 deletions
+7 -1
View File
@@ -1,6 +1,6 @@
import django_filters
from django.utils.text import gettext_lazy as _
from django_filters import OrderingFilter
from shop.models import Product
@@ -9,6 +9,11 @@ class ProductFilter(django_filters.FilterSet):
field_name="name", lookup_expr="icontains", label=_("Nombre")
)
tags = django_filters.BaseInFilter(field_name="tags", label=_("Etiquetas"))
ordering = OrderingFilter(
fields={
'name': 'name'
}
)
class Meta:
model = Product
@@ -17,4 +22,5 @@ class ProductFilter(django_filters.FilterSet):
"stock",
"is_digital_asset",
"tags",
"ordering",
)
@@ -0,0 +1,40 @@
# Generated by Django 5.1.3 on 2024-11-22 19:53
from django.db import migrations, models
from django.utils.text import slugify
def add_product_slug(apps, schema_editor):
Product = apps.get_model("shop", "Product")
for product in Product.objects.all():
product.slug = slugify(product.name)[:128]
product.save()
class Migration(migrations.Migration):
dependencies = [
("shop", "0003_alter_brand_creation_date_and_more"),
]
operations = [
migrations.AlterModelOptions(
name="product",
options={
"ordering": ("id",),
"verbose_name": "producto",
"verbose_name_plural": "productos",
},
),
migrations.AddField(
model_name="product",
name="slug",
field=models.SlugField(
blank=True, default="", max_length=128, null=True, verbose_name="Slug"
),
),
migrations.RunPython(
add_product_slug,
)
]
+12 -2
View File
@@ -3,7 +3,7 @@ from uuid import uuid4
from django.db import models
from django.utils import timezone
from django.utils.text import gettext_lazy as _
from django.utils.text import gettext_lazy as _, slugify
class TimestampedModel(models.Model):
@@ -70,10 +70,20 @@ class Product(TimestampedModel):
on_delete=models.SET_NULL,
verbose_name=_("marca"),
)
slug = models.SlugField(default="", blank=True, null=True, max_length=128, verbose_name=_('Slug'))
def __str__(self):
return self.name
def save(
self,
*args,
**kwargs,
):
if not self.pk:
self.slug = slugify(self.name)[:128]
super().save(*args, **kwargs)
@property
def price(self):
return self.prices.filter(current=True).first()
@@ -81,7 +91,7 @@ class Product(TimestampedModel):
class Meta:
verbose_name = _("producto")
verbose_name_plural = _("productos")
ordering = ("-id",)
ordering = ("id",)
class ProductPrice(TimestampedModel):
+2 -2
View File
@@ -2,7 +2,7 @@
<div
class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm dark:border-gray-700 dark:bg-gray-800">
<div class="h-56 w-full">
<a href="#">
<a href="{% url 'shop:product_detail' pk=product.pk slug=product.slug %}">
<img class="mx-auto h-full dark:hidden" src="{{ product.images.all|first }}"
alt="{{ product.name }}"/>
<img class="mx-auto hidden h-full dark:block" src="{{ product.images.all|first }}"
@@ -11,7 +11,7 @@
</div>
<div class="pt-6">
<a href="{% url 'shop:product_detail' pk=product.pk %}"
<a href="{% url 'shop:product_detail' pk=product.pk slug=product.slug %}"
class="text-lg font-semibold leading-tight text-gray-900 hover:underline dark:text-white">{{ product.name }}</a>
<div class="mt-2 flex items-center gap-2">
@@ -8,6 +8,7 @@
hx-target="#products"
hx-trigger="keyup changed delay:500ms"
hx-swap="innerHTML"
hx-push="true"
>
<label for="simple-search" class="sr-only">Buscar</label>
<div class="relative w-full">
+26 -24
View File
@@ -1,32 +1,34 @@
{% extends 'theme/base.html' %}
{% load static %}
{% load static i18n %}
{% block main %}
<div>
{% include 'components/products/list_filters.html' %}
<section
class="p-8"
hx-get="{% url 'shop:list_products' %}{% querystring request.GET %}"
hx-trigger="load"
hx-target="#products"
hx-swap="innerHTML"
>
<div>
<div id="products" class="mb-4 grid gap-4 sm:grid-cols-2 md:mb-8 lg:grid-cols-3 xl:grid-cols-4">
<section
class="p-8"
hx-get="{% url 'shop:list_products' %}{% querystring request.GET %}"
hx-trigger="load"
hx-target="#products"
hx-swap="innerHTML"
>
<div>
<ul id="products" class="mb-4 grid gap-4 sm:grid-cols-2 md:mb-8 lg:grid-cols-3 xl:grid-cols-4">
</ul>
<div class="w-full text-center">
<button type="button"
class="rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-900
hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:outline-none focus:ring-4
focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700
dark:hover:text-white dark:focus:ring-gray-700"
hx-get="{% url 'shop:list_products' %}{% querystring request.GET %}{% querystring page=9 %}"
hx-trigger="click"
hx-target="#products"
hx-swap="beforeend"
>
{% translate 'Mostrar mas' %}
</button>
</div>
</div>
<div class="w-full text-center">
<button type="button"
class="rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
hx-get="{% url 'shop:list_products' %}{% querystring request.GET %}{% querystring page=9 %}"
hx-trigger="click"
hx-target="#products"
hx-swap="beforeend"
>
Mostrar mas
</button>
</div>
</div>
</section>
</section>
</div>
{% endblock %}
+3 -1
View File
@@ -1,5 +1,7 @@
{% load i18n %}
{% load static %}
{% for product in products %}
{% include 'components/product_card.html' %}
<li>
{% include 'components/product_card.html' %}
</li>
{% endfor %}
+1 -1
View File
@@ -7,6 +7,6 @@ app_name = "shop"
urlpatterns = [
path("", index, name="index"),
path("products/<int:pk>/", product_detail, name="product_detail"),
path("products/<int:pk>/<str:slug>/", product_detail, name="product_detail"),
path("shop/components/products/", list_products, name="list_products"),
]
+9 -1
View File
@@ -9,6 +9,11 @@ from shop.models import Product
class IndexView(TemplateView):
template_name = "shop/index.html"
def get_context_data(self, **kwargs):
return {
"title": "Shoppy",
}
class ListProducts(TemplateView, FilteredQuerysetMixin, PaginatedQuerysetMixin):
template_name = "shop/list_products.html"
@@ -27,11 +32,14 @@ class ListProducts(TemplateView, FilteredQuerysetMixin, PaginatedQuerysetMixin):
class ProductDetail(TemplateView):
template_name = "shop/product_detail.html"
def get_context_data(self, pk, **kwargs):
def get_context_data(self, pk, slug, **kwargs):
product = get_object_or_404(Product, pk=pk)
return {
"product": product,
"title": product.name,
"description": product.description,
"image": product.images.first(),
}