Features: 1) Introduce ALLOW validation for generic actions in permissions; 2) Add AllowAny permission class to seo_meta endpoints; 3) Update SEO URL patterns and breadcrumbs with language codes; 4) Improve compatibility of attributes in serializers with ListField and DictField.;
Fixes: 1) Correct dynamic URL generation for images in Open Graph (`og_image` and `logo_url`); 2) Address potential missing or empty `crumbs` handling in breadcrumbs generation; Extra: 1) Refactor redundant action names (e.g., `seo` to `seo_meta`) for better consistency; 2) Clean up and optimize serializer structures and Graphene object fields; 3) Improve readability and maintainability of action and object implementations.;
This commit is contained in:
parent
733b249643
commit
ea22ca69a3
4 changed files with 49 additions and 23 deletions
|
|
@ -554,7 +554,7 @@ class ProductType(DjangoObjectType):
|
|||
description = (self.description or "")[:180]
|
||||
|
||||
first_img = self.images.order_by("priority").first()
|
||||
og_image = _abs(info.context, first_img.image.url) if first_img else ""
|
||||
og_image = graphene_abs(info.context, first_img.image.url) if first_img else ""
|
||||
|
||||
og = {
|
||||
"title": title,
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ class EvibesPermission(permissions.BasePermission):
|
|||
app_label = model._meta.app_label
|
||||
model_name = model._meta.model_name
|
||||
|
||||
if view.additional.get(action) == "ALLOW":
|
||||
return True
|
||||
|
||||
if action == "create" and view.additional.get("create") == "ALLOW":
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ class RecursiveField(Field):
|
|||
|
||||
class AddOrderProductSerializer(Serializer):
|
||||
product_uuid = CharField(required=True)
|
||||
attributes = JSONField(required=False, default=dict)
|
||||
attributes = ListField(required=False, child=DictField(), default=list)
|
||||
|
||||
|
||||
class BulkAddOrderProductsSerializer(Serializer):
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from drf_spectacular.utils import extend_schema_view
|
|||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.renderers import MultiPartRenderer
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
|
@ -283,7 +284,7 @@ class CategoryViewSet(EvibesViewSet):
|
|||
action_serializer_classes = {
|
||||
"list": CategorySimpleSerializer,
|
||||
}
|
||||
additional = {"seo": "ALLOW"}
|
||||
additional = {"seo_meta": "ALLOW"}
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
|
|
@ -291,8 +292,15 @@ class CategoryViewSet(EvibesViewSet):
|
|||
return qs
|
||||
return qs.filter(is_active=True)
|
||||
|
||||
@action(detail=True, methods=["get"], url_path="seo")
|
||||
def seo(self, request, **kwargs):
|
||||
@action(
|
||||
detail=True,
|
||||
methods=["get"],
|
||||
url_path="meta",
|
||||
permission_classes=[
|
||||
AllowAny,
|
||||
],
|
||||
)
|
||||
def seo_meta(self, request, **kwargs):
|
||||
lookup_key = getattr(self, "lookup_url_kwarg", "pk")
|
||||
lookup_val = kwargs.get(lookup_key)
|
||||
|
||||
|
|
@ -304,13 +312,14 @@ class CategoryViewSet(EvibesViewSet):
|
|||
title = f"{category.name} | {config.PROJECT_NAME}"
|
||||
description = (category.description or "")[:180]
|
||||
canonical = f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{category.slug}"
|
||||
og_image = request.build_absolute_uri(category.image.url) if getattr(category, "image", None) else ""
|
||||
|
||||
og = {
|
||||
"title": title,
|
||||
"description": description,
|
||||
"type": "website",
|
||||
"url": canonical,
|
||||
"image": category.image.url if getattr(category, "image", None) else "",
|
||||
"image": og_image,
|
||||
}
|
||||
tw = {"card": "summary_large_image", "title": title, "description": description}
|
||||
|
||||
|
|
@ -379,7 +388,7 @@ class BrandViewSet(EvibesViewSet):
|
|||
action_serializer_classes = {
|
||||
"list": BrandSimpleSerializer,
|
||||
}
|
||||
additional = {"seo": "ALLOW"}
|
||||
additional = {"seo_meta": "ALLOW"}
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Brand.objects.all()
|
||||
|
|
@ -393,8 +402,15 @@ class BrandViewSet(EvibesViewSet):
|
|||
|
||||
return queryset
|
||||
|
||||
@action(detail=True, methods=["get"], url_path="seo")
|
||||
def seo(self, request, **kwargs):
|
||||
@action(
|
||||
detail=True,
|
||||
methods=["get"],
|
||||
url_path="meta",
|
||||
permission_classes=[
|
||||
AllowAny,
|
||||
],
|
||||
)
|
||||
def seo_meta(self, request, **kwargs):
|
||||
lookup_key = getattr(self, "lookup_url_kwarg", "pk")
|
||||
lookup_val = kwargs.get(lookup_key)
|
||||
brand = get_object_or_404(Brand, slug=str(lookup_val))
|
||||
|
|
@ -406,9 +422,11 @@ class BrandViewSet(EvibesViewSet):
|
|||
canonical = f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/brand/{brand.slug}"
|
||||
|
||||
logo_url = (
|
||||
brand.big_logo.url
|
||||
request.build_absolute_uri(brand.big_logo.url)
|
||||
if getattr(brand, "big_logo", None)
|
||||
else (brand.small_logo.url if getattr(brand, "small_logo", None) else None)
|
||||
else request.build_absolute_uri(brand.small_logo.url)
|
||||
if getattr(brand, "small_logo", None)
|
||||
else ""
|
||||
)
|
||||
|
||||
og = {
|
||||
|
|
@ -416,7 +434,7 @@ class BrandViewSet(EvibesViewSet):
|
|||
"description": description,
|
||||
"type": "website",
|
||||
"url": canonical,
|
||||
"image": logo_url or "",
|
||||
"image": logo_url,
|
||||
}
|
||||
tw = {"card": "summary_large_image", "title": title, "description": description}
|
||||
|
||||
|
|
@ -479,7 +497,7 @@ class ProductViewSet(EvibesViewSet):
|
|||
}
|
||||
lookup_field = "lookup_value"
|
||||
lookup_url_kwarg = "lookup_value"
|
||||
additional = {"seo": "ALLOW"}
|
||||
additional = {"seo_meta": "ALLOW"}
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
|
|
@ -530,30 +548,35 @@ class ProductViewSet(EvibesViewSet):
|
|||
name = "Product"
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": _(f"{name} does not exist: {uuid}")})
|
||||
|
||||
@action(detail=True, methods=["get"], url_path="seo")
|
||||
def seo(self, request, slug):
|
||||
p = get_object_or_404(Product.objects.select_related("brand", "category"), slug=slug)
|
||||
@action(
|
||||
detail=True,
|
||||
methods=["get"],
|
||||
url_path="meta",
|
||||
permission_classes=[
|
||||
AllowAny,
|
||||
],
|
||||
)
|
||||
def seo_meta(self, request, **kwargs):
|
||||
p = self.get_object()
|
||||
images = list(p.images.all()[:6])
|
||||
rating = {"value": p.rating, "count": p.feedbacks_count}
|
||||
title = f"{p.name} | {config.PROJECT_NAME}"
|
||||
description = (p.description or "")[:180]
|
||||
canonical = f"https://{config.BASE_DOMAIN}/products/{p.slug}"
|
||||
|
||||
canonical = f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}"
|
||||
og = {
|
||||
"title": title,
|
||||
"description": description,
|
||||
"type": "product",
|
||||
"url": canonical,
|
||||
"image": images[0].image.url if images else "",
|
||||
"image": request.build_absolute_uri(images[0].image.url) if images else "",
|
||||
}
|
||||
tw = {"card": "summary_large_image", "title": title, "description": description}
|
||||
|
||||
crumbs = []
|
||||
crumbs = [("Home", f"https://{config.BASE_DOMAIN}/")]
|
||||
if p.category:
|
||||
crumbs.append(("Home", f"https://{config.BASE_DOMAIN}/"))
|
||||
for c in p.category.get_ancestors(include_self=True):
|
||||
crumbs.append((c.name, f"https://{config.BASE_DOMAIN}/c/{c.slug}"))
|
||||
crumbs.append((p.name, canonical))
|
||||
crumbs.append((c.name, f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{c.slug}"))
|
||||
crumbs.append((p.name, canonical))
|
||||
|
||||
json_ld = [org_schema(), website_schema()]
|
||||
if crumbs:
|
||||
|
|
|
|||
Loading…
Reference in a new issue