Features: 1) RUFFVCKED

This commit is contained in:
Egor Pavlovich Gorbunov 2025-07-03 15:00:18 +03:00
parent aa8f15d0ee
commit 29005527bb
15 changed files with 126 additions and 106 deletions

View file

@ -9,6 +9,9 @@
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/blog/templates" />
<option value="$MODULE_DIR$/core/templates" />
<option value="$MODULE_DIR$/payments/templates" />
<option value="$MODULE_DIR$/vibes_auth/templates" />
</list>
</option>
</component>

View file

@ -7,7 +7,7 @@ from markdown_field import MarkdownField
from core.abstract import NiceModel
class Post(NiceModel):
class Post(NiceModel): # type: ignore [django-manager-missing]
"""
Represents a blog post model extending NiceModel.
@ -21,8 +21,8 @@ class Post(NiceModel):
Attributes:
is_publicly_visible (bool): Specifies whether the post is visible to the public.
author (ForeignKey): A reference to the user who authored the post.
title (CharField): The title of the post, must be unique and non-empty.
content (MarkdownField): The content of the post written in markdown format.
title (CharField): The title of the post. Must be unique and non-empty.
content (MarkdownField): The content of the post written in Markdown format.
file (FileField): An optional file attachment for the post.
slug (AutoSlugField): A unique, automatically generated slug based on the title.
tags (ManyToManyField): Tags associated with the post for categorization.
@ -102,9 +102,9 @@ class PostTag(NiceModel):
Attributes:
is_publicly_visible (bool): Determines if the tag is visible publicly.
tag_name (CharField): An internal tag identifier for the post tag. It is a required
tag_name (CharField): An internal tag identifier for the post's tag. It is a required
field with a maximum length of 255 characters.
name (CharField): A user-friendly, unique display name for the post tag
name (CharField): A user-friendly, unique display name for the post's tag
with a maximum length of 255 characters.
Meta:

View file

@ -82,40 +82,17 @@ class FieldsetsMixin:
return fieldsets
# noinspection PyUnresolvedReferences
class ActivationActionsMixin:
@action(description=str(_("activate selected %(verbose_name_plural)s")))
def activate_selected(self, request, queryset, **kwargs) -> str:
if kwargs:
pass
if request:
pass
@action(description=_("activate selected %(verbose_name_plural)s"))
def activate_selected(self, request, queryset):
queryset.update(is_active=True)
return str(_("%(verbose_name_plural)s activated successfully!"))
self.message_user(request, _("selected items have been activated."))
@action(description=str(_("deactivate selected %(verbose_name_plural)s")))
def deactivate_selected(self, request, queryset, **kwargs) -> str:
if kwargs:
pass
if request:
pass
@action(description=_("deactivate selected %(verbose_name_plural)s"))
def deactivate_selected(self, request, queryset):
queryset.update(is_active=False)
return str(_("%(verbose_name_plural)s deactivated successfully."))
def get_actions(self, request, **kwargs):
if kwargs:
pass
actions = super().get_actions(request)
actions["activate_selected"] = (
self.activate_selected,
"activate_selected",
str(_("activate selected %(verbose_name_plural)s")),
)
actions["deactivate_selected"] = (
self.deactivate_selected,
"deactivate_selected",
str(_("deactivate selected %(verbose_name_plural)s")),
)
return actions
self.message_user(request, _("selected items have been deactivated."))
class AttributeValueInline(TabularInline):
@ -173,7 +150,8 @@ class CategoryChildrenInline(TabularInline):
@register(AttributeGroup)
class AttributeGroupAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = AttributeGroup
# noinspection PyClassVar
model = AttributeGroup # type: ignore [misc]
list_display = ("name", "modified")
search_fields = ("uuid", "name")
readonly_fields = ("uuid", "modified", "created")
@ -184,7 +162,8 @@ class AttributeGroupAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Attribute)
class AttributeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Attribute
# noinspection PyClassVar
model = Attribute # type: ignore [misc]
list_display = ("name", "group", "value_type", "modified")
list_filter = ("value_type", "group", "is_active")
search_fields = ("uuid", "name", "group__name")
@ -197,7 +176,8 @@ class AttributeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(AttributeValue)
class AttributeValueAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = AttributeValue
# noinspection PyClassVar
model = AttributeValue # type: ignore [misc]
list_display = ("attribute", "value", "modified")
list_filter = ("attribute__group", "is_active")
search_fields = ("uuid", "value", "attribute__name")
@ -210,6 +190,7 @@ class AttributeValueAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Category)
class CategoryAdmin(FieldsetsMixin, ActivationActionsMixin, DraggableMPTTAdmin):
# noinspection PyClassVar
model = Category
list_display = ("indented_title", "parent", "is_active", "modified")
# noinspection PyUnresolvedReferences
@ -225,7 +206,8 @@ class CategoryAdmin(FieldsetsMixin, ActivationActionsMixin, DraggableMPTTAdmin):
@register(Brand)
class BrandAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Brand
# noinspection PyClassVar
model = Brand # type: ignore [misc]
list_display = ("name",)
list_filter = ("categories", "is_active")
search_fields = ("uuid", "name", "categories__name")
@ -237,7 +219,8 @@ class BrandAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Product)
class ProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Product
# noinspection PyClassVar
model = Product # type: ignore [misc]
list_display = (
"name",
"partnumber",
@ -276,7 +259,8 @@ class ProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(ProductTag)
class ProductTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = ProductTag
# noinspection PyClassVar
model = ProductTag # type: ignore [misc]
list_display = ("tag_name",)
search_fields = ("tag_name",)
readonly_fields = ("uuid", "modified", "created")
@ -287,7 +271,8 @@ class ProductTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(CategoryTag)
class CategoryTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = CategoryTag
# noinspection PyClassVar
model = CategoryTag # type: ignore [misc]
list_display = ("tag_name",)
search_fields = ("tag_name",)
readonly_fields = ("uuid", "modified", "created")
@ -298,7 +283,8 @@ class CategoryTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Vendor)
class VendorAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Vendor
# noinspection PyClassVar
model = Vendor # type: ignore [misc]
list_display = ("name", "markup_percent", "modified")
list_filter = ("markup_percent", "is_active")
search_fields = ("name",)
@ -311,7 +297,8 @@ class VendorAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Feedback)
class FeedbackAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Feedback
# noinspection PyClassVar
model = Feedback # type: ignore [misc]
list_display = ("order_product", "rating", "comment", "modified")
list_filter = ("rating", "is_active")
search_fields = ("order_product__product__name", "comment")
@ -323,7 +310,8 @@ class FeedbackAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Order)
class OrderAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Order
# noinspection PyClassVar
model = Order # type: ignore [misc]
list_display = (
"human_readable_id",
"user",
@ -351,7 +339,8 @@ class OrderAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(OrderProduct)
class OrderProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = OrderProduct
# noinspection PyClassVar
model = OrderProduct # type: ignore [misc]
list_display = ("order", "product", "quantity", "buy_price", "status", "modified")
list_filter = ("status",)
search_fields = ("order__user__email", "product__name")
@ -364,7 +353,8 @@ class OrderProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(PromoCode)
class PromoCodeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = PromoCode
# noinspection PyClassVar
model = PromoCode # type: ignore [misc]
list_display = (
"code",
"discount_percent",
@ -392,7 +382,8 @@ class PromoCodeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Promotion)
class PromotionAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Promotion
# noinspection PyClassVar
model = Promotion # type: ignore [misc]
list_display = ("name", "discount_percent", "modified")
search_fields = ("name",)
readonly_fields = ("uuid", "modified", "created")
@ -404,7 +395,8 @@ class PromotionAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Stock)
class StockAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Stock
# noinspection PyClassVar
model = Stock # type: ignore [misc]
list_display = ("product", "vendor", "sku", "quantity", "price", "modified")
list_filter = ("vendor", "quantity")
search_fields = ("product__name", "vendor__name", "sku")
@ -424,7 +416,8 @@ class StockAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Wishlist)
class WishlistAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Wishlist
# noinspection PyClassVar
model = Wishlist # type: ignore [misc]
list_display = ("user", "modified")
search_fields = ("user__email",)
readonly_fields = ("uuid", "modified", "created")
@ -435,7 +428,8 @@ class WishlistAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(ProductImage)
class ProductImageAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = ProductImage
# noinspection PyClassVar
model = ProductImage # type: ignore [misc]
list_display = ("alt", "product", "priority", "modified")
list_filter = ("priority",)
search_fields = ("alt", "product__name")
@ -448,7 +442,8 @@ class ProductImageAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
@register(Address)
class AddressAdmin(FieldsetsMixin, GISModelAdmin):
model = Address
# noinspection PyClassVar
model = Address # type: ignore [misc]
list_display = ("street", "city", "region", "country", "user")
list_filter = ("country", "region")
search_fields = ("street", "city", "postal_code", "user__email")
@ -511,7 +506,7 @@ class ConstanceConfig:
# noinspection PyTypeChecker
site.unregister([Config])
# noinspection PyTypeChecker
site.register([ConstanceConfig], BaseConstanceAdmin)
site.site_title = CONSTANCE_CONFIG["PROJECT_NAME"][0]
site.register([ConstanceConfig], BaseConstanceAdmin) # type: ignore [list-item]
site.site_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore [assignment]
site.site_header = "eVibes"
site.index_title = CONSTANCE_CONFIG["PROJECT_NAME"][0]
site.index_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore [assignment]

View file

@ -860,7 +860,7 @@ class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel): # t
image (ImageField): The image file associated with the product.
priority (int): The display priority of the image. Images with lower
priority values are displayed first.
product (ForeignKey): The product to which this image is associated.
product (ForeignKey): The product associated with this image.
"""
is_publicly_visible = True
@ -1278,11 +1278,11 @@ class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel): # type: ig
promo_amount = order.total_price
if self.discount_type == "percent":
promo_amount -= round(promo_amount * (float(self.discount_percent) / 100), 2)
promo_amount -= round(promo_amount * (float(self.discount_percent) / 100), 2) # type: ignore [arg-type]
order.attributes.update({"promocode": str(self.uuid), "final_price": promo_amount})
order.save()
elif self.discount_type == "amount":
promo_amount -= round(float(self.discount_amount), 2)
promo_amount -= round(float(self.discount_amount), 2) # type: ignore [arg-type]
order.attributes.update({"promocode": str(self.uuid), "final_price": promo_amount})
order.save()
else:
@ -1447,7 +1447,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi
promotions = Promotion.objects.filter(is_active=True, products__in=[product]).order_by("discount_percent")
if promotions.exists():
buy_price -= round(product.price * (promotions.first().discount_percent / 100), 2)
buy_price -= round(product.price * (promotions.first().discount_percent / 100), 2) # type: ignore [union-attr]
order_product, is_created = OrderProduct.objects.get_or_create(
product=product,
@ -1588,6 +1588,12 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi
amount = self.apply_promocode(promocode_uuid) if promocode_uuid else self.total_price
if not self.user:
raise ValueError(_("you cannot buy an order without a user"))
if not self.user.payments_balance:
raise ValueError(_("a user without a balance cannot buy with balance"))
match force:
case "balance":
if self.user.payments_balance.amount < amount:
@ -1786,7 +1792,10 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel): # t
)
def __str__(self) -> str:
return f"{self.product.name} for ({self.order.user.email if self.order.user else 'unregistered user'})"
return (
f"{self.product.name if self.product else self.uuid}"
f" for ({self.order.user.email if self.order and self.order.user else 'unregistered user'})"
)
class Meta:
verbose_name = _("order product")
@ -1827,16 +1836,18 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel): # t
@property
def total_price(self: Self) -> float:
return round(float(self.buy_price) * self.quantity, 2)
return round(float(self.buy_price) * self.quantity, 2) # type: ignore [arg-type]
@property
def download_url(self: Self) -> str:
if self.product:
if self.product.is_digital and self.product.stocks.first().digital_asset:
if self.product and self.product.stocks:
if self.product.is_digital and self.product.stocks.first().digital_asset: # type: ignore [union-attr]
return self.download.url
return ""
def do_feedback(self, rating=10, comment="", action="add") -> Optional["Feedback"]:
if not self.order:
raise ValueError(_("order product must have an order"))
if action not in ["add", "remove"]:
raise ValueError(_(f"wrong action specified for feedback: {action}"))
if action == "remove" and self.feedback:
@ -1904,7 +1915,7 @@ class Feedback(ExportModelOperationsMixin("feedback"), NiceModel): # type: igno
This class is designed to capture and store user feedback for specific products
that they have purchased. It contains attributes to store user comments,
a reference to the related product in the order, and a user-assigned rating. The
class utilizes database fields to effectively model and manage feedback data.
class uses database fields to effectively model and manage feedback data.
Attributes:
is_publicly_visible (bool): Indicates whether the feedback is visible to the public.
@ -1940,7 +1951,9 @@ class Feedback(ExportModelOperationsMixin("feedback"), NiceModel): # type: igno
)
def __str__(self) -> str:
if self.order_product and self.order_product.order and self.order_product.order.user:
return f"{self.rating} by {self.order_product.order.user.email}"
return f"{self.rating} | {self.uuid}"
class Meta:
verbose_name = _("feedback")

View file

@ -1,4 +1,5 @@
from contextlib import suppress
from typing import Collection, Any
from rest_framework.fields import JSONField, SerializerMethodField
from rest_framework.relations import PrimaryKeyRelatedField
@ -26,8 +27,8 @@ from core.serializers.utility import AddressSerializer
class AttributeGroupSimpleSerializer(ModelSerializer):
parent = PrimaryKeyRelatedField(read_only=True)
children = PrimaryKeyRelatedField(many=True, read_only=True)
parent = PrimaryKeyRelatedField(read_only=True) # type: ignore [assignment, var-annotated]
children = PrimaryKeyRelatedField(many=True, read_only=True) # type: ignore [assignment, var-annotated]
class Meta:
model = AttributeGroup
@ -58,7 +59,7 @@ class CategorySimpleSerializer(ModelSerializer):
return obj.image.url
return None
def get_children(self, obj) -> list:
def get_children(self, obj) -> Collection[Any]:
request = self.context.get("request")
if request is not None and request.user.has_perm("view_category"):
children = obj.children.all()

View file

@ -89,7 +89,7 @@ class DoFeedbackSerializer(Serializer):
class CacheOperatorSerializer(Serializer):
key = CharField(required=True)
data = JSONField(required=False)
data = JSONField(required=False) # type: ignore [assignment]
timeout = IntegerField(required=False)

View file

@ -83,6 +83,9 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
if len(ops) <= 0:
return
if not order.user:
return
activate(order.user.language)
set_email_settings()
@ -94,11 +97,11 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
template_name="digital_order_delivered_email.html",
context={
"order_uuid": order.human_readable_id,
"user_first_name": order.user.first_name,
"user_first_name": "" or order.user.first_name,
"order_products": ops,
"project_name": config.PROJECT_NAME,
"contact_email": config.EMAIL_FROM,
"total_price": round(sum(op.buy_price for op in ops), 2),
"total_price": round(sum(0.0 or op.buy_price for op in ops), 2), # type: ignore [misc]
"display_system_attributes": order.user.has_perm("core.view_order"),
"today": datetime.today(),
},
@ -113,6 +116,8 @@ def send_order_finished_email(order_pk: str) -> tuple[bool, str]:
def send_thank_you_email(ops: list[OrderProduct]):
if ops:
pass
if not order.user:
return
activate(order.user.language)
set_email_settings()

View file

@ -149,7 +149,7 @@ class SupportedLanguagesView(APIView):
"""
Handles retrieving the list of supported languages.
This class provides an endpoint to return available languages information.
This class provides an endpoint to return information about available languages.
It is configured with relevant serializers, permission classes, and renderers
for flexibility in response formats and access permissions. The endpoint
supports retrieving the list of languages with their respective codes, names,
@ -328,7 +328,8 @@ class RequestCursedURLView(APIView):
renderer_classes (list): Configures the response format renderers available for this view.
Methods:
post: Handles the POST request to validate the URL, fetch its data if valid, and returns the processed response.
post: Handles the POST request to validate the URL, fetches its data if valid,
and returns the processed response.
"""
permission_classes = [

View file

@ -245,7 +245,7 @@ class CategoryViewSet(EvibesViewSet):
Attributes:
queryset: The base queryset used to retrieve category data, including
prefetching related objects such as parent, children, attributes,
prefetching related objects such as parents, children, attributes,
and tags.
filter_backends: A list of backends for applying filters to the category
data.
@ -315,8 +315,8 @@ class ProductViewSet(EvibesViewSet):
Manages operations related to the `Product` model in the system.
This class provides a viewset for managing products, including their filtering, serialization,
and operations on specific instances. It extends from `EvibesViewSet` to utilize common
functionality and integrates with Django REST framework for RESTful API operations.
and operations on specific instances. It extends from `EvibesViewSet` to use common
functionality and integrates with the Django REST framework for RESTful API operations.
Includes methods for retrieving product details, applying permissions, and accessing
related feedback of a product.
@ -383,6 +383,7 @@ class ProductViewSet(EvibesViewSet):
if request.user.has_perm("core.view_feedback")
else Feedback.objects.filter(order_product__product=product, is_active=True)
)
# noinspection PyTypeChecker
return Response(data=FeedbackDetailSerializer(feedbacks, many=True).data)
except Product.DoesNotExist:
name = "Product"
@ -784,7 +785,7 @@ class PromoCodeViewSet(EvibesViewSet):
This class extends the functionality of the EvibesViewSet to provide a
customized view set for PromoCode objects. It includes filtering capabilities,
utilizes specific serializers for different actions, and limits data access
uses specific serializers for different actions, and limits data access
based on user permissions. The primary purpose is to enable API operations
related to PromoCodes while enforcing security and filtering.
@ -840,7 +841,7 @@ class StockViewSet(EvibesViewSet):
Handles operations related to Stock data in the system.
The StockViewSet class is a viewset that provides methods for retrieving,
filtering, and serializing Stock data. It utilizes Django's filter
filtering, and serializing Stock data. It uses Django's filter
backends to enable filtering based on specified fields and supports
custom serializers for different actions.
@ -875,7 +876,7 @@ class WishlistViewSet(EvibesViewSet):
allowing for the retrieval, modification, and customization of products within
the wish list. This ViewSet facilitates functionality such as adding, removing,
and bulk actions for wishlist products. Permission checks are integrated to
ensure that users can only manage their own wishlists, unless explicit permissions
ensure that users can only manage their own wishlists unless explicit permissions
are granted.
Attributes
@ -1096,10 +1097,10 @@ class ProductTagViewSet(EvibesViewSet):
filterset_fields: Fields available for filtering the queryset. Includes
'tag_name' for filtering by the name of the tag, and 'is_active' for
filtering active/inactive tags.
serializer_class: The default serializer class used when no specific
serializer_class: The default serializer class is used when no specific
serializer is defined for an action.
action_serializer_classes: A dictionary mapping specific actions (e.g.,
'list') to custom serializers. Utilizes ProductTagSimpleSerializer
'list') to custom serializers. Uses ProductTagSimpleSerializer
for the 'list' action.
"""

View file

@ -227,7 +227,7 @@ CURRENCIES: tuple[tuple[str, str], ...] = (
("zh-hans", "CNY"),
)
CURRENCY_CODE: str | None = dict(CURRENCIES).get(LANGUAGE_CODE)
CURRENCY_CODE: str = dict(CURRENCIES).get(LANGUAGE_CODE) # type: ignore [assignment]
MODELTRANSLATION_FALLBACK_LANGUAGES: tuple = (LANGUAGE_CODE, "en-us", "de-de")

View file

@ -1,9 +1,9 @@
from evibes.settings import CONSTANCE_CONFIG
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = CONSTANCE_CONFIG.get("EMAIL_HOST")[0]
EMAIL_PORT = CONSTANCE_CONFIG.get("EMAIL_PORT")[0]
EMAIL_USE_TLS = CONSTANCE_CONFIG.get("EMAIL_USE_TLS")[0]
EMAIL_USE_SSL = CONSTANCE_CONFIG.get("EMAIL_USE_SSL")[0]
EMAIL_HOST_USER = CONSTANCE_CONFIG.get("EMAIL_HOST_USER")[0]
EMAIL_HOST_PASSWORD = CONSTANCE_CONFIG.get("EMAIL_HOST_PASSWORD")[0]
EMAIL_HOST = CONSTANCE_CONFIG.get("EMAIL_HOST")[0] # type: ignore [index]
EMAIL_PORT = CONSTANCE_CONFIG.get("EMAIL_PORT")[0] # type: ignore [index]
EMAIL_USE_TLS = CONSTANCE_CONFIG.get("EMAIL_USE_TLS")[0] # type: ignore [index]
EMAIL_USE_SSL = CONSTANCE_CONFIG.get("EMAIL_USE_SSL")[0] # type: ignore [index]
EMAIL_HOST_USER = CONSTANCE_CONFIG.get("EMAIL_HOST_USER")[0] # type: ignore [index]
EMAIL_HOST_PASSWORD = CONSTANCE_CONFIG.get("EMAIL_HOST_PASSWORD")[0] # type: ignore [index]

View file

@ -6,10 +6,10 @@ from payments.serializers import TransactionSerializer
class TransactionViewSet(ReadOnlyModelViewSet):
"""
ViewSet for handling read-only operations on Transaction model.
ViewSet for handling read-only operations on the Transaction model.
This class provides a read-only interface for interacting with transaction
data. It utilizes the TransactionSerializer for serializing and deserializing
data. It uses the TransactionSerializer for serializing and deserializing
the data. The class ensures that only authorized users, who meet specific
permissions, can access the transactions.

View file

@ -20,7 +20,6 @@ from rest_framework_simplejwt.token_blacklist.models import (
OutstandingToken as BaseOutstandingToken,
)
from blog.models import Post
from core.abstract import NiceModel
from core.models import Order, Wishlist
from evibes.settings import LANGUAGE_CODE, LANGUAGES
@ -29,7 +28,7 @@ from vibes_auth.managers import UserManager
from vibes_auth.validators import validate_phone_number
class User(AbstractUser, NiceModel):
class User(AbstractUser, NiceModel): # type: ignore [django-manager-missing]
"""
Represents a User entity with customized fields and methods for extended functionality.
@ -54,9 +53,9 @@ class User(AbstractUser, NiceModel):
attributes: JSONField for custom storage of user-specific additional attributes.
USERNAME_FIELD: Specifies the unique identifier for the user (email in this case).
REQUIRED_FIELDS: A list of fields required when creating a user via createsuperuser, left empty here.
objects: Custom manager for User model providing additional methods for user creation.
payments_balance: Reference to the user's payment balance (related to external model).
user_related_wishlist: Reference to the user's wishlist (related to external model).
objects: Custom manager for the User model providing additional methods for user creation.
payments_balance: Reference to the user's payment balance (related to the external model).
user_related_wishlist: Reference to the user's wishlist (related to the external model).
orders: QuerySet representing the user's associated orders.
Methods:
@ -96,7 +95,6 @@ class User(AbstractUser, NiceModel):
],
)
username: None = None # type: ignore [assignment]
posts: "Post"
first_name = CharField(_("first_name"), max_length=150, blank=True, null=True) # type: ignore [assignment]
last_name = CharField(_("last_name"), max_length=150, blank=True, null=True) # type: ignore [assignment]
avatar = ImageField(
@ -207,7 +205,7 @@ class BlacklistedToken(BaseBlacklistedToken):
blacklisted tokens. It inherits from a base class provided for this purpose,
allowing for the extension of functionality or customization of behavior
without altering the original base class's structure. It also defines the
meta options for the model, affecting its database and administrative
Meta options for the model, affecting its database and administrative
representation.
"""

View file

@ -162,7 +162,10 @@ class TokenObtainPairSerializer(TokenObtainSerializer):
logger.debug("Data formed")
if api_settings.UPDATE_LAST_LOGIN:
update_last_login(self.user, self.user)
if not self.user:
raise ValidationError(_("no active account"))
# noinspection PyTypeChecker
update_last_login(User, self.user)
logger.debug("Updated last login")
logger.debug("Returning data")
@ -191,7 +194,7 @@ class TokenRefreshSerializer(Serializer):
data["refresh"] = str(refresh)
user = User.objects.get(uuid=refresh.payload["user_uuid"])
# noinspection PyTypeChecker
data["user"] = UserSerializer(user).data
data["user"] = UserSerializer(user).data # type: ignore [assignment]
return data
@ -224,7 +227,7 @@ class TokenVerifySerializer(Serializer):
raise ValidationError(_("user does not exist")) from dne
# noinspection PyTypeChecker
attrs["user"] = UserSerializer(user).data
attrs["user"] = UserSerializer(user).data # type: ignore [assignment]
return attrs

View file

@ -47,8 +47,8 @@ class TokenObtainPairView(TokenViewBase):
subject to rate limiting depending on the global DEBUG setting.
"""
serializer_class = TokenObtainPairSerializer
_serializer_class = TokenObtainPairSerializer
serializer_class = TokenObtainPairSerializer # type: ignore [assignment]
_serializer_class = TokenObtainPairSerializer # type: ignore [assignment]
@method_decorator(ratelimit(key="ip", rate="10/h" if not DEBUG else "888/h"))
def post(self, request, *args, **kwargs):
@ -82,8 +82,8 @@ class TokenRefreshView(TokenViewBase):
whether the application is in DEBUG mode or not.
"""
serializer_class = TokenRefreshSerializer
_serializer_class = TokenRefreshSerializer
serializer_class = TokenRefreshSerializer # type: ignore [assignment]
_serializer_class = TokenRefreshSerializer # type: ignore [assignment]
@method_decorator(ratelimit(key="ip", rate="10/h" if not DEBUG else "888/h"))
def post(self, request, *args, **kwargs):
@ -104,8 +104,8 @@ class TokenVerifyView(TokenViewBase):
error response.
"""
serializer_class = TokenVerifySerializer
_serializer_class = TokenVerifySerializer
serializer_class = TokenVerifySerializer # type: ignore [assignment]
_serializer_class = TokenVerifySerializer # type: ignore [assignment]
def post(self, request, *args, **kwargs):
try: