Features: 1) Add image field to search result types (SearchProductsResultsType, SearchCategoriesResultsType, SearchBrandsResultsType); 2) Enhance tags field for categories with DjangoFilterConnectionField; 3) Implement descriptive error handling in rebuild_slugs script.
Fixes: 1) Correct inconsistent function calls with improved attribute formatting; 2) Fix invalid key usage (`uuid` vs. `pk`) in slug rebuilding logic to prevent uniqueness errors; 3) Ensure correct `query` usage in `process_query` function calls. Extra: Reformat code for readability by updating indentation, and breaking long function signatures and expressions into multiple lines.
This commit is contained in:
parent
d4ff637169
commit
30144c5b6a
3 changed files with 153 additions and 42 deletions
|
|
@ -35,7 +35,9 @@ class CacheOperator(BaseMutation):
|
|||
description = _("cache I/O")
|
||||
|
||||
class Arguments:
|
||||
key = String(required=True, description=_("key to look for in or set into the cache"))
|
||||
key = String(
|
||||
required=True, description=_("key to look for in or set into the cache")
|
||||
)
|
||||
data = GenericScalar(required=False, description=_("data to store in cache"))
|
||||
timeout = Int(
|
||||
required=False,
|
||||
|
|
@ -65,7 +67,9 @@ class RequestCursedURL(BaseMutation):
|
|||
try:
|
||||
data = cache.get(url, None)
|
||||
if not data:
|
||||
response = requests.get(url, headers={"content-type": "application/json"})
|
||||
response = requests.get(
|
||||
url, headers={"content-type": "application/json"}
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = camelize(response.json())
|
||||
cache.set(url, data, 86400)
|
||||
|
|
@ -93,7 +97,9 @@ class AddOrderProduct(BaseMutation):
|
|||
if not (user.has_perm("core.add_orderproduct") or user == order.user):
|
||||
raise PermissionDenied(permission_denied_message)
|
||||
|
||||
order = order.add_product(product_uuid=product_uuid, attributes=format_attributes(attributes))
|
||||
order = order.add_product(
|
||||
product_uuid=product_uuid, attributes=format_attributes(attributes)
|
||||
)
|
||||
|
||||
return AddOrderProduct(order=order)
|
||||
except Order.DoesNotExist:
|
||||
|
|
@ -119,7 +125,9 @@ class RemoveOrderProduct(BaseMutation):
|
|||
if not (user.has_perm("core.change_orderproduct") or user == order.user):
|
||||
raise PermissionDenied(permission_denied_message)
|
||||
|
||||
order = order.remove_product(product_uuid=product_uuid, attributes=format_attributes(attributes))
|
||||
order = order.remove_product(
|
||||
product_uuid=product_uuid, attributes=format_attributes(attributes)
|
||||
)
|
||||
|
||||
return AddOrderProduct(order=order)
|
||||
except Order.DoesNotExist:
|
||||
|
|
@ -198,7 +206,11 @@ class BuyOrder(BaseMutation):
|
|||
billing_address=None,
|
||||
):
|
||||
if not any([order_uuid, order_hr_id]) or all([order_uuid, order_hr_id]):
|
||||
raise BadRequest(_("please provide either order_uuid or order_hr_id - mutually exclusive"))
|
||||
raise BadRequest(
|
||||
_(
|
||||
"please provide either order_uuid or order_hr_id - mutually exclusive"
|
||||
)
|
||||
)
|
||||
user = info.context.user
|
||||
try:
|
||||
order = None
|
||||
|
|
@ -222,7 +234,11 @@ class BuyOrder(BaseMutation):
|
|||
case "<class 'core.models.Order'>":
|
||||
return BuyOrder(order=instance)
|
||||
case _:
|
||||
raise TypeError(_(f"wrong type came from order.buy() method: {type(instance)!s}"))
|
||||
raise TypeError(
|
||||
_(
|
||||
f"wrong type came from order.buy() method: {type(instance)!s}"
|
||||
)
|
||||
)
|
||||
|
||||
except Order.DoesNotExist:
|
||||
raise Http404(_(f"order {order_uuid} not found"))
|
||||
|
|
@ -250,7 +266,11 @@ class BulkOrderAction(BaseMutation):
|
|||
order_hr_id=None,
|
||||
):
|
||||
if not any([order_uuid, order_hr_id]) or all([order_uuid, order_hr_id]):
|
||||
raise BadRequest(_("please provide either order_uuid or order_hr_id - mutually exclusive"))
|
||||
raise BadRequest(
|
||||
_(
|
||||
"please provide either order_uuid or order_hr_id - mutually exclusive"
|
||||
)
|
||||
)
|
||||
user = info.context.user
|
||||
try:
|
||||
order = None
|
||||
|
|
@ -432,14 +452,20 @@ class BuyWishlist(BaseMutation):
|
|||
):
|
||||
order.add_product(product_uuid=product.pk)
|
||||
|
||||
instance = order.buy(force_balance=force_balance, force_payment=force_payment)
|
||||
instance = order.buy(
|
||||
force_balance=force_balance, force_payment=force_payment
|
||||
)
|
||||
match str(type(instance)):
|
||||
case "<class 'payments.models.Transaction'>":
|
||||
return BuyWishlist(transaction=instance)
|
||||
case "<class 'core.models.Order'>":
|
||||
return BuyWishlist(order=instance)
|
||||
case _:
|
||||
raise TypeError(_(f"wrong type came from order.buy() method: {type(instance)!s}"))
|
||||
raise TypeError(
|
||||
_(
|
||||
f"wrong type came from order.buy() method: {type(instance)!s}"
|
||||
)
|
||||
)
|
||||
|
||||
except Wishlist.DoesNotExist:
|
||||
raise Http404(_(f"wishlist {wishlist_uuid} not found"))
|
||||
|
|
@ -453,7 +479,9 @@ class BuyProduct(BaseMutation):
|
|||
product_uuid = UUID(required=True)
|
||||
attributes = String(
|
||||
required=False,
|
||||
description=_("please send the attributes as the string formatted like attr1=value1,attr2=value2"),
|
||||
description=_(
|
||||
"please send the attributes as the string formatted like attr1=value1,attr2=value2"
|
||||
),
|
||||
)
|
||||
force_balance = Boolean(required=False)
|
||||
force_payment = Boolean(required=False)
|
||||
|
|
@ -462,10 +490,19 @@ class BuyProduct(BaseMutation):
|
|||
transaction = Field(TransactionType, required=False)
|
||||
|
||||
@staticmethod
|
||||
def mutate(_parent, info, product_uuid, attributes=None, force_balance=False, force_payment=False):
|
||||
def mutate(
|
||||
_parent,
|
||||
info,
|
||||
product_uuid,
|
||||
attributes=None,
|
||||
force_balance=False,
|
||||
force_payment=False,
|
||||
):
|
||||
user = info.context.user
|
||||
order = Order.objects.create(user=user, status="MOMENTAL")
|
||||
order.add_product(product_uuid=product_uuid, attributes=format_attributes(attributes))
|
||||
order.add_product(
|
||||
product_uuid=product_uuid, attributes=format_attributes(attributes)
|
||||
)
|
||||
instance = order.buy(force_balance=force_balance, force_payment=force_payment)
|
||||
match str(type(instance)):
|
||||
case "<class 'payments.models.Transaction'>":
|
||||
|
|
@ -473,7 +510,9 @@ class BuyProduct(BaseMutation):
|
|||
case "<class 'core.models.Order'>":
|
||||
return BuyProduct(order=instance)
|
||||
case _:
|
||||
raise TypeError(_(f"wrong type came from order.buy() method: {type(instance)!s}"))
|
||||
raise TypeError(
|
||||
_(f"wrong type came from order.buy() method: {type(instance)!s}")
|
||||
)
|
||||
|
||||
|
||||
class CreateProduct(BaseMutation):
|
||||
|
|
@ -489,7 +528,9 @@ class CreateProduct(BaseMutation):
|
|||
if not info.context.user.has_perm("core.add_product"):
|
||||
raise PermissionDenied(permission_denied_message)
|
||||
category = Category.objects.get(uuid=category_uuid)
|
||||
product = Product.objects.create(name=name, description=description, category=category)
|
||||
product = Product.objects.create(
|
||||
name=name, description=description, category=category
|
||||
)
|
||||
return CreateProduct(product=product)
|
||||
|
||||
|
||||
|
|
@ -536,7 +577,9 @@ class DeleteProduct(BaseMutation):
|
|||
|
||||
class CreateAddress(BaseMutation):
|
||||
class Arguments:
|
||||
raw_data = String(required=True, description=_("original address string provided by the user"))
|
||||
raw_data = String(
|
||||
required=True, description=_("original address string provided by the user")
|
||||
)
|
||||
|
||||
address = Field(AddressType)
|
||||
|
||||
|
|
@ -632,7 +675,7 @@ class Search(BaseMutation):
|
|||
|
||||
@staticmethod
|
||||
def mutate(_parent, info, query):
|
||||
data = process_query(query)
|
||||
data = process_query(query=query, request=info.context)
|
||||
|
||||
return Search(
|
||||
results=SearchResultsType(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,18 @@ from django.core.cache import cache
|
|||
from django.db.models import Max, Min
|
||||
from django.db.models.functions import Length
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from graphene import UUID, Field, Float, InputObjectType, Int, List, NonNull, ObjectType, String, relay
|
||||
from graphene import (
|
||||
UUID,
|
||||
Field,
|
||||
Float,
|
||||
InputObjectType,
|
||||
Int,
|
||||
List,
|
||||
NonNull,
|
||||
ObjectType,
|
||||
String,
|
||||
relay,
|
||||
)
|
||||
from graphene.types.generic import GenericScalar
|
||||
from graphene_django import DjangoObjectType
|
||||
from graphene_django.filter import DjangoFilterConnectionField
|
||||
|
|
@ -90,10 +101,16 @@ class BrandType(DjangoObjectType):
|
|||
return self.categories.filter(is_active=True)
|
||||
|
||||
def resolve_big_logo(self: Brand, info):
|
||||
return info.context.build_absolute_uri(self.big_logo.url) if self.big_logo else ""
|
||||
return (
|
||||
info.context.build_absolute_uri(self.big_logo.url) if self.big_logo else ""
|
||||
)
|
||||
|
||||
def resolve_small_logo(self: Brand, info):
|
||||
return info.context.build_absolute_uri(self.small_logo.url) if self.small_logo else ""
|
||||
return (
|
||||
info.context.build_absolute_uri(self.small_logo.url)
|
||||
if self.small_logo
|
||||
else ""
|
||||
)
|
||||
|
||||
|
||||
class FilterableAttributeType(ObjectType):
|
||||
|
|
@ -115,14 +132,22 @@ class CategoryType(DjangoObjectType):
|
|||
markup_percent = Float(required=False, description=_("markup percentage"))
|
||||
filterable_attributes = List(
|
||||
NonNull(FilterableAttributeType),
|
||||
description=_("which attributes and values can be used for filtering this category."),
|
||||
description=_(
|
||||
"which attributes and values can be used for filtering this category."
|
||||
),
|
||||
)
|
||||
min_max_prices = Field(
|
||||
NonNull(MinMaxPriceType),
|
||||
description=_("minimum and maximum prices for products in this category, if available."),
|
||||
description=_(
|
||||
"minimum and maximum prices for products in this category, if available."
|
||||
),
|
||||
)
|
||||
tags = DjangoFilterConnectionField(
|
||||
lambda: CategoryTagType, description=_("tags for this category")
|
||||
)
|
||||
products = DjangoFilterConnectionField(
|
||||
lambda: ProductType, description=_("products in this category")
|
||||
)
|
||||
tags = DjangoFilterConnectionField(lambda: CategoryTagType, description=_("tags for this category"))
|
||||
products = DjangoFilterConnectionField(lambda: ProductType, description=_("products in this category"))
|
||||
|
||||
class Meta:
|
||||
model = Category
|
||||
|
|
@ -198,9 +223,14 @@ class CategoryType(DjangoObjectType):
|
|||
)
|
||||
min_max_prices["min_price"] = price_aggregation.get("min_price", 0.0)
|
||||
min_max_prices["max_price"] = price_aggregation.get("max_price", 0.0)
|
||||
cache.set(key=f"{self.name}_min_max_prices", value=min_max_prices, timeout=86400)
|
||||
cache.set(
|
||||
key=f"{self.name}_min_max_prices", value=min_max_prices, timeout=86400
|
||||
)
|
||||
|
||||
return {"min_price": min_max_prices["min_price"], "max_price": min_max_prices["max_price"]}
|
||||
return {
|
||||
"min_price": min_max_prices["min_price"],
|
||||
"max_price": min_max_prices["max_price"],
|
||||
}
|
||||
|
||||
|
||||
class VendorType(DjangoObjectType):
|
||||
|
|
@ -245,7 +275,9 @@ class AddressType(DjangoObjectType):
|
|||
|
||||
class FeedbackType(DjangoObjectType):
|
||||
comment = String(description=_("comment"))
|
||||
rating = Int(description=_("rating value from 1 to 10, inclusive, or 0 if not set."))
|
||||
rating = Int(
|
||||
description=_("rating value from 1 to 10, inclusive, or 0 if not set.")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Feedback
|
||||
|
|
@ -258,7 +290,9 @@ class FeedbackType(DjangoObjectType):
|
|||
class OrderProductType(DjangoObjectType):
|
||||
attributes = GenericScalar(description=_("attributes"))
|
||||
notifications = GenericScalar(description=_("notifications"))
|
||||
download_url = String(description=_("download url for this order product if applicable"))
|
||||
download_url = String(
|
||||
description=_("download url for this order product if applicable")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = OrderProduct
|
||||
|
|
@ -292,7 +326,9 @@ class OrderType(DjangoObjectType):
|
|||
billing_address = Field(AddressType, description=_("billing address"))
|
||||
shipping_address = Field(
|
||||
AddressType,
|
||||
description=_("shipping address for this order, leave blank if same as billing address or if not applicable"),
|
||||
description=_(
|
||||
"shipping address for this order, leave blank if same as billing address or if not applicable"
|
||||
),
|
||||
)
|
||||
total_price = Float(description=_("total price of this order"))
|
||||
total_quantity = Int(description=_("total quantity of products in order"))
|
||||
|
|
@ -350,7 +386,9 @@ class ProductType(DjangoObjectType):
|
|||
images = DjangoFilterConnectionField(ProductImageType, description=_("images"))
|
||||
feedbacks = DjangoFilterConnectionField(FeedbackType, description=_("feedbacks"))
|
||||
brand = Field(BrandType, description=_("brand"))
|
||||
attribute_groups = DjangoFilterConnectionField(AttributeGroupType, description=_("attribute groups"))
|
||||
attribute_groups = DjangoFilterConnectionField(
|
||||
AttributeGroupType, description=_("attribute groups")
|
||||
)
|
||||
price = Float(description=_("price"))
|
||||
quantity = Float(description=_("quantity"))
|
||||
feedbacks_count = Int(description=_("number of feedbacks"))
|
||||
|
|
@ -387,7 +425,9 @@ class ProductType(DjangoObjectType):
|
|||
def resolve_attribute_groups(self: Product, info):
|
||||
info.context._product_uuid = self.uuid
|
||||
|
||||
return AttributeGroup.objects.filter(attributes__values__product=self).distinct()
|
||||
return AttributeGroup.objects.filter(
|
||||
attributes__values__product=self
|
||||
).distinct()
|
||||
|
||||
def resolve_quantity(self, _info) -> int:
|
||||
return self.quantity or 0
|
||||
|
|
@ -422,14 +462,20 @@ class PromoCodeType(DjangoObjectType):
|
|||
description = _("promocodes")
|
||||
|
||||
def resolve_discount(self: PromoCode, _info) -> float:
|
||||
return float(self.discount_percent) if self.discount_percent else float(self.discount_amount)
|
||||
return (
|
||||
float(self.discount_percent)
|
||||
if self.discount_percent
|
||||
else float(self.discount_amount)
|
||||
)
|
||||
|
||||
def resolve_discount_type(self: PromoCode, _info) -> str:
|
||||
return "percent" if self.discount_percent else "amount"
|
||||
|
||||
|
||||
class PromotionType(DjangoObjectType):
|
||||
products = DjangoFilterConnectionField(ProductType, description=_("products on sale"))
|
||||
products = DjangoFilterConnectionField(
|
||||
ProductType, description=_("products on sale")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Promotion
|
||||
|
|
@ -452,7 +498,9 @@ class StockType(DjangoObjectType):
|
|||
|
||||
|
||||
class WishlistType(DjangoObjectType):
|
||||
products = DjangoFilterConnectionField(ProductType, description=_("wishlisted products"))
|
||||
products = DjangoFilterConnectionField(
|
||||
ProductType, description=_("wishlisted products")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Wishlist
|
||||
|
|
@ -462,7 +510,9 @@ class WishlistType(DjangoObjectType):
|
|||
|
||||
|
||||
class ProductTagType(DjangoObjectType):
|
||||
product_set = DjangoFilterConnectionField(ProductType, description=_("tagged products"))
|
||||
product_set = DjangoFilterConnectionField(
|
||||
ProductType, description=_("tagged products")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ProductTag
|
||||
|
|
@ -473,7 +523,9 @@ class ProductTagType(DjangoObjectType):
|
|||
|
||||
|
||||
class CategoryTagType(DjangoObjectType):
|
||||
category_set = DjangoFilterConnectionField(CategoryType, description=_("tagged categories"))
|
||||
category_set = DjangoFilterConnectionField(
|
||||
CategoryType, description=_("tagged categories")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CategoryTag
|
||||
|
|
@ -489,7 +541,11 @@ class ConfigType(ObjectType):
|
|||
company_name = String(description=_("company name"))
|
||||
company_address = String(description=_("company address"))
|
||||
company_phone_number = String(description=_("company phone number"))
|
||||
email_from = String(description=_("email from, sometimes it must be used instead of host user value"))
|
||||
email_from = String(
|
||||
description=_(
|
||||
"email from, sometimes it must be used instead of host user value"
|
||||
)
|
||||
)
|
||||
email_host_user = String(description=_("email host user"))
|
||||
payment_gateway_maximum = Float(description=_("maximum amount for payment"))
|
||||
payment_gateway_minimum = Float(description=_("minimum amount for payment"))
|
||||
|
|
@ -513,18 +569,21 @@ class SearchProductsResultsType(ObjectType):
|
|||
uuid = UUID()
|
||||
name = String()
|
||||
slug = String()
|
||||
image = String()
|
||||
|
||||
|
||||
class SearchCategoriesResultsType(ObjectType):
|
||||
uuid = UUID()
|
||||
name = String()
|
||||
slug = String()
|
||||
image = String()
|
||||
|
||||
|
||||
class SearchBrandsResultsType(ObjectType):
|
||||
uuid = UUID()
|
||||
name = String()
|
||||
slug = String()
|
||||
image = String()
|
||||
|
||||
|
||||
class SearchPostsResultsType(ObjectType):
|
||||
|
|
@ -534,9 +593,15 @@ class SearchPostsResultsType(ObjectType):
|
|||
|
||||
|
||||
class SearchResultsType(ObjectType):
|
||||
products = List(description=_("products search results"), of_type=SearchProductsResultsType)
|
||||
categories = List(description=_("products search results"), of_type=SearchCategoriesResultsType)
|
||||
brands = List(description=_("products search results"), of_type=SearchBrandsResultsType)
|
||||
products = List(
|
||||
description=_("products search results"), of_type=SearchProductsResultsType
|
||||
)
|
||||
categories = List(
|
||||
description=_("products search results"), of_type=SearchCategoriesResultsType
|
||||
)
|
||||
brands = List(
|
||||
description=_("products search results"), of_type=SearchBrandsResultsType
|
||||
)
|
||||
posts = List(description=_("posts search results"), of_type=SearchPostsResultsType)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ class Command(BaseCommand):
|
|||
)
|
||||
for idx, instance in enumerate(queryset.iterator(), start=1):
|
||||
try:
|
||||
if (
|
||||
queryset.filter(name=instance.name).exclude(pk=instance.pk).count()
|
||||
while (
|
||||
queryset.filter(name=instance.name)
|
||||
.exclude(uuid=instance.uuid)
|
||||
.count()
|
||||
>= 1
|
||||
):
|
||||
instance.name = f"{instance.name} - {get_random_string(length=3, allowed_chars='0123456789')}"
|
||||
|
|
@ -34,7 +36,8 @@ class Command(BaseCommand):
|
|||
except Exception as e:
|
||||
self.stderr.write(
|
||||
self.style.ERROR(
|
||||
f"[{idx}/{total}] ({queryset.model._meta.verbose_name_plural}: {instance.pk}) ERROR: {e}"
|
||||
f"[{idx}/{total}] ({queryset.model._meta.verbose_name_plural}: {instance.name}/{instance.uuid})"
|
||||
f" ERROR: {e}"
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue