From 856f2ff51640abdf5b530f6070743772352de70a Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Sun, 29 Jun 2025 20:03:33 +0300 Subject: [PATCH] Features: 1) Add `app_name` attribute in multiple `urls.py` files across apps to support namespacing; Fixes: 1) Simplify Prometheus and GraphQL path definitions in `evibes/api_urls.py`; Extra: 1) Add line breaks across multiple files for improved code readability. --- blog/models.py | 2 ++ blog/urls.py | 2 ++ blog/viewsets.py | 1 + core/api_urls.py | 2 ++ core/b2b_urls.py | 2 ++ core/views.py | 9 +++++++++ core/viewsets.py | 18 ++++++++++++++++++ evibes/api_urls.py | 7 ++----- payments/urls.py | 2 ++ payments/views.py | 2 ++ payments/viewsets.py | 1 + vibes_auth/models.py | 4 ++++ vibes_auth/urls.py | 2 ++ vibes_auth/views.py | 3 +++ vibes_auth/viewsets.py | 1 + 15 files changed, 53 insertions(+), 5 deletions(-) diff --git a/blog/models.py b/blog/models.py index 28b15a11..485d3b8f 100644 --- a/blog/models.py +++ b/blog/models.py @@ -28,6 +28,7 @@ class Post(NiceModel): tags (ManyToManyField): Tags associated with the post for categorization. """ + is_publicly_visible = True author: ForeignKey = ForeignKey( @@ -112,6 +113,7 @@ class PostTag(NiceModel): verbose_name (str): Human-readable singular name of the PostTag model. verbose_name_plural (str): Human-readable plural name of the PostTag model. """ + is_publicly_visible = True tag_name: CharField = CharField( diff --git a/blog/urls.py b/blog/urls.py index 1a6c8b34..ac04560f 100644 --- a/blog/urls.py +++ b/blog/urls.py @@ -3,6 +3,8 @@ from rest_framework.routers import DefaultRouter from blog.viewsets import PostViewSet +app_name = "blog" + payment_router = DefaultRouter() payment_router.register(prefix=r"posts", viewset=PostViewSet, basename="posts") diff --git a/blog/viewsets.py b/blog/viewsets.py index c5b338f3..deb26f23 100644 --- a/blog/viewsets.py +++ b/blog/viewsets.py @@ -23,6 +23,7 @@ class PostViewSet(ReadOnlyModelViewSet): filterset_class: Defines the set of filters used for filtering Post objects. additional: Contains additional configuration, such as specific action permissions. """ + serializer_class = PostSerializer permission_classes = (EvibesPermission,) queryset = Post.objects.filter(is_active=True) diff --git a/core/api_urls.py b/core/api_urls.py index 3faadbc6..22f47a02 100644 --- a/core/api_urls.py +++ b/core/api_urls.py @@ -30,6 +30,8 @@ from core.viewsets import ( WishlistViewSet, ) +app_name = "core" + core_router = DefaultRouter() core_router.register(r"products", ProductViewSet, basename="products") core_router.register(r"orders", OrderViewSet, basename="orders") diff --git a/core/b2b_urls.py b/core/b2b_urls.py index 7d4c5e60..53a4f04c 100644 --- a/core/b2b_urls.py +++ b/core/b2b_urls.py @@ -5,6 +5,8 @@ from core.views import ( GlobalSearchView, ) +app_name = "core" + urlpatterns = [ path("search/", GlobalSearchView.as_view(), name="global_search"), path("orders/buy_as_business/", BuyAsBusinessView.as_view(), name="request_cursed_url"), diff --git a/core/views.py b/core/views.py index d4711a39..560d4504 100644 --- a/core/views.py +++ b/core/views.py @@ -106,6 +106,7 @@ class CustomGraphQLView(FileUploadGraphQLView): ---------- None """ + def get_context(self, request): return request @@ -120,6 +121,7 @@ class CustomSwaggerView(SpectacularSwaggerView): This can be useful in scenarios where the script or reference URL needs to be dynamically generated and included in the context. """ + def get_context_data(self, **kwargs): # noinspection PyUnresolvedReferences context = super().get_context_data(**kwargs) @@ -137,6 +139,7 @@ class CustomRedocView(SpectacularRedocView): for rendering ReDoc UI is required, specifically adapting the script URL for the current request environment. """ + def get_context_data(self, **kwargs): # noinspection PyUnresolvedReferences context = super().get_context_data(**kwargs) @@ -164,6 +167,7 @@ class SupportedLanguagesView(APIView): get(self, request): Retrieves the list of supported languages. """ + serializer_class = LanguageSerializer permission_classes = [ AllowAny, @@ -218,6 +222,7 @@ class WebsiteParametersView(APIView): get(request) Handles HTTP GET requests to fetch website parameters. """ + serializer_class = None permission_classes = [ AllowAny, @@ -253,6 +258,7 @@ class CacheOperatorView(APIView): post(request, *args, **kwargs): Handles HTTP POST requests to set cache data based on the provided key and timeout. """ + serializer_class = CacheOperatorSerializer permission_classes = [ AllowAny, @@ -294,6 +300,7 @@ class ContactUsView(APIView): post: Handles POST requests to process form submissions. """ + serializer_class = ContactUsSerializer renderer_classes = [ CamelCaseJSONRenderer, @@ -326,6 +333,7 @@ class RequestCursedURLView(APIView): Methods: post: Handles the POST request to validate the URL, fetch its data if valid, and returns the processed response. """ + permission_classes = [ AllowAny, ] @@ -411,6 +419,7 @@ class BuyAsBusinessView(APIView): post(request, *_args, **kwargs): Handles the post request to process a business purchase. """ + @ratelimit(key="ip", rate="2/h", block=True) def post(self, request, *_args, **kwargs): serializer = BuyAsBusinessOrderSerializer(data=request.data) diff --git a/core/viewsets.py b/core/viewsets.py index 0372ca51..05ea987f 100644 --- a/core/viewsets.py +++ b/core/viewsets.py @@ -131,6 +131,7 @@ class EvibesViewSet(ModelViewSet): Returns the serializer class for the current action or the default serializer class from the parent ModelViewSet. """ + action_serializer_classes: dict = {} additional: dict = {} permission_classes = [EvibesPermission] @@ -162,6 +163,7 @@ class AttributeGroupViewSet(EvibesViewSet): specific serializer classes, allowing customization of serialization behavior for certain actions. """ + queryset = AttributeGroup.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["is_active"] @@ -193,6 +195,7 @@ class AttributeViewSet(EvibesViewSet): specific actions, such as returning less detailed data for a `list` action. """ + queryset = Attribute.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["group", "value_type", "is_active"] @@ -220,6 +223,7 @@ class AttributeValueViewSet(EvibesViewSet): action_serializer_classes (dict): A dictionary mapping action names to their corresponding serializer classes. """ + queryset = AttributeValue.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["attribute", "is_active"] @@ -258,6 +262,7 @@ class CategoryViewSet(EvibesViewSet): and filtering out inactive categories for users without sufficient permissions. """ + queryset = Category.objects.all().prefetch_related("parent", "children", "attributes", "tags") filter_backends = [DjangoFilterBackend] filterset_class = CategoryFilter @@ -294,6 +299,7 @@ class BrandViewSet(EvibesViewSet): to their corresponding serializer classes. The "list" action uses the BrandSimpleSerializer class. """ + queryset = Brand.objects.all() filter_backends = [DjangoFilterBackend] filterset_class = BrandFilter @@ -328,6 +334,7 @@ class ProductViewSet(EvibesViewSet): get_object: Fetches a single object based on its identifier, applying permissions. feedbacks: Fetches feedback associated with a specific product. """ + queryset = Product.objects.prefetch_related("tags", "attributes", "stocks", "images").all() filter_backends = [DjangoFilterBackend] filterset_class = ProductFilter @@ -402,6 +409,7 @@ class VendorViewSet(EvibesViewSet): action_serializer_classes: A dictionary mapping specific actions (e.g., "list") to custom serializer classes for those actions. """ + queryset = Vendor.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["name", "markup_percent", "is_active"] @@ -433,6 +441,7 @@ class FeedbackViewSet(EvibesViewSet): serializer classes. For example, the "list" action uses `FeedbackSimpleSerializer`. """ + queryset = Feedback.objects.all() filter_backends = [DjangoFilterBackend] filterset_class = FeedbackFilter @@ -495,6 +504,7 @@ class OrderViewSet(EvibesViewSet): bulk_remove_order_products: Removes multiple products with optional attributes from an order. """ + lookup_field = "lookup_value" lookup_url_kwarg = "lookup_value" queryset = Order.objects.prefetch_related("order_products").all() @@ -692,6 +702,7 @@ class OrderProductViewSet(EvibesViewSet): do_feedback: Custom action to add, remove, or manage feedback for an OrderProduct instance. """ + queryset = OrderProduct.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["order", "product", "status", "is_active"] @@ -756,6 +767,7 @@ class ProductImageViewSet(EvibesViewSet): serializer classes. For the "list" action, ProductImageSimpleSerializer is used. """ + queryset = ProductImage.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["product", "priority", "is_active"] @@ -785,6 +797,7 @@ class PromoCodeViewSet(EvibesViewSet): action_serializer_classes: A dictionary mapping specific actions (like "list") to their corresponding serializer classes. """ + queryset = PromoCode.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["code", "discount_amount", "discount_percent", "start_time", "end_time", "used_on", "is_active"] @@ -812,6 +825,7 @@ class PromotionViewSet(EvibesViewSet): queryset management, filter backends, and serializer customization for handling different views or actions efficiently. """ + queryset = Promotion.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["name", "discount_percent", "is_active"] @@ -842,6 +856,7 @@ class StockViewSet(EvibesViewSet): to their respective serializers. For the "list" action, StockSimpleSerializer is used. """ + queryset = Stock.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["vendor", "product", "sku", "is_active"] @@ -891,6 +906,7 @@ class WishlistViewSet(EvibesViewSet): bulk_remove_wishlist_products(request, **kwargs) Removes multiple products from a specific wishlist. """ + queryset = Wishlist.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["user", "is_active"] @@ -1006,6 +1022,7 @@ class AddressViewSet(EvibesViewSet): serializer_class: Default serializer class for address objects. additional: Dictionary of additional options for this viewset. """ + pagination_class = None filter_backends = [DjangoFilterBackend] filterset_class = AddressFilter @@ -1085,6 +1102,7 @@ class ProductTagViewSet(EvibesViewSet): 'list') to custom serializers. Utilizes ProductTagSimpleSerializer for the 'list' action. """ + queryset = ProductTag.objects.all() filter_backends = [DjangoFilterBackend] filterset_fields = ["tag_name", "is_active"] diff --git a/evibes/api_urls.py b/evibes/api_urls.py index 6780aaaa..016efca4 100644 --- a/evibes/api_urls.py +++ b/evibes/api_urls.py @@ -18,11 +18,8 @@ from evibes.settings import SPECTACULAR_PLATFORM_SETTINGS urlpatterns = [ path(r"health/", include("health_check.urls", namespace="health_check")), - path("prometheus/", include("django_prometheus.urls", namespace="prometheus")), - path( - r"graphql/", - csrf_exempt(CustomGraphQLView.as_view(graphiql=True, schema=schema)), name="graphql-platform" - ), + path("prometheus/", include("django_prometheus.urls")), + path(r"graphql/", csrf_exempt(CustomGraphQLView.as_view(graphiql=True, schema=schema)), name="graphql-platform"), path( r"docs/", SpectacularAPIView.as_view(urlconf="evibes.api_urls", custom_settings=SPECTACULAR_PLATFORM_SETTINGS), diff --git a/payments/urls.py b/payments/urls.py index 6f703c4a..4c567fde 100644 --- a/payments/urls.py +++ b/payments/urls.py @@ -4,6 +4,8 @@ from rest_framework.routers import DefaultRouter from payments.views import CallbackAPIView, DepositView from payments.viewsets import TransactionViewSet +app_name = "payments" + payment_router = DefaultRouter() payment_router.register(prefix=r"transactions", viewset=TransactionViewSet, basename="transactions") diff --git a/payments/views.py b/payments/views.py index 560b3481..7d39d90e 100644 --- a/payments/views.py +++ b/payments/views.py @@ -31,6 +31,7 @@ class DepositView(APIView): post: Processes the deposit request, validates the request data, ensures user authentication, and creates a transaction. """ + def post(self, request, *args, **kwargs): logger.debug(request.__dict__) serializer = DepositSerializer(data=request.data) @@ -65,6 +66,7 @@ class CallbackAPIView(APIView): based on the specified gateway. Handles exceptions gracefully by returning a server error response if an unknown gateway or other issues occur. """ + def post(self, request, *args, **kwargs): logger.debug(request.__dict__) try: diff --git a/payments/viewsets.py b/payments/viewsets.py index 5ee92209..43d2a360 100644 --- a/payments/viewsets.py +++ b/payments/viewsets.py @@ -20,5 +20,6 @@ class TransactionViewSet(ReadOnlyModelViewSet): the data. Includes custom permissions to restrict access based on ownership and other criteria. """ + serializer_class = TransactionSerializer permission_classes = (EvibesPermission, IsOwner) diff --git a/vibes_auth/models.py b/vibes_auth/models.py index c512ebf1..384a0947 100644 --- a/vibes_auth/models.py +++ b/vibes_auth/models.py @@ -79,6 +79,7 @@ class User(AbstractUser, NiceModel): verbose_name: Sets the human-readable name for singular instances of the model. verbose_name_plural: Sets the human-readable name for multiple instances of the model. """ + def get_uuid_as_path(self, *args): return str(self.uuid) + "/" + args[0] @@ -173,6 +174,7 @@ class Group(BaseGroup): Meta.verbose_name_plural (str): Human-readable plural name for the Group model. """ + class Meta: proxy = True verbose_name = _("group") @@ -194,6 +196,7 @@ class OutstandingToken(BaseOutstandingToken): the plural form of that name. """ + class Meta: proxy = True verbose_name = _("outstanding token") @@ -214,6 +217,7 @@ class BlacklistedToken(BaseBlacklistedToken): Attributes: None """ + class Meta: proxy = True verbose_name = _("blacklisted token") diff --git a/vibes_auth/urls.py b/vibes_auth/urls.py index ad3bc32c..d09ffedb 100644 --- a/vibes_auth/urls.py +++ b/vibes_auth/urls.py @@ -4,6 +4,8 @@ from rest_framework.routers import DefaultRouter from vibes_auth.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView from vibes_auth.viewsets import UserViewSet +app_name = "vibes_auth" + auth_router = DefaultRouter() auth_router.register(r"users", UserViewSet, basename="users") diff --git a/vibes_auth/views.py b/vibes_auth/views.py index 3a8dffe7..71751ecf 100644 --- a/vibes_auth/views.py +++ b/vibes_auth/views.py @@ -46,6 +46,7 @@ class TokenObtainPairView(TokenViewBase): post: Handles HTTP POST requests for token retrieval. This method is subject to rate limiting depending on the global DEBUG setting. """ + serializer_class = TokenObtainPairSerializer # type: ignore _serializer_class = TokenObtainPairSerializer # type: ignore @@ -80,6 +81,7 @@ class TokenRefreshView(TokenViewBase): the request. Rate limit settings are defined depending on whether the application is in DEBUG mode or not. """ + serializer_class = TokenRefreshSerializer # type: ignore _serializer_class = TokenRefreshSerializer # type: ignore @@ -101,6 +103,7 @@ class TokenVerifyView(TokenViewBase): user data can also be returned. Errors during token validation result in an appropriate error response. """ + serializer_class = TokenVerifySerializer # type: ignore _serializer_class = TokenVerifySerializer # type: ignore diff --git a/vibes_auth/viewsets.py b/vibes_auth/viewsets.py index a6c629a5..3ccd901a 100644 --- a/vibes_auth/viewsets.py +++ b/vibes_auth/viewsets.py @@ -78,6 +78,7 @@ class UserViewSet( OverflowError, TypeError: Raised for decoding or type conversion issues. """ + serializer_class = UserSerializer queryset = User.objects.filter(is_active=True) permission_classes = [AllowAny]