From 8b6910fd6eaeef570d77af81d03643ed9eae27b4 Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Thu, 14 Aug 2025 17:27:46 +0500 Subject: [PATCH] add product folder models and apis --- .../management/commands/seed_permissions.py | 2 + core/apps/products/admin/__init__.py | 3 +- core/apps/products/admin/folder.py | 13 ++++ ...uct_product_code_product_unity_and_more.py | 67 ++++++++++++++++ core/apps/products/models/__init__.py | 3 +- core/apps/products/models/folder.py | 27 +++++++ core/apps/products/models/product.py | 16 +++- core/apps/products/serializers/folder.py | 34 +++++++++ core/apps/products/urls.py | 17 +++++ core/apps/products/views/folder.py | 76 +++++++++++++++++++ resources/logs/django.log.2025-08-13 | 1 + 11 files changed, 255 insertions(+), 4 deletions(-) create mode 100644 core/apps/products/admin/folder.py create mode 100644 core/apps/products/migrations/0005_folder_product_product_code_product_unity_and_more.py create mode 100644 core/apps/products/models/folder.py create mode 100644 core/apps/products/serializers/folder.py create mode 100644 core/apps/products/views/folder.py create mode 100644 resources/logs/django.log.2025-08-13 diff --git a/core/apps/accounts/management/commands/seed_permissions.py b/core/apps/accounts/management/commands/seed_permissions.py index 4b82b80..1bba4db 100644 --- a/core/apps/accounts/management/commands/seed_permissions.py +++ b/core/apps/accounts/management/commands/seed_permissions.py @@ -16,6 +16,8 @@ class Command(BaseCommand): {'code': 'delete_user', "name": 'can delete user permissions'}, {'code': 'user', 'name': 'all user access'}, {'code': 'settings', 'name': 'all settings access'}, + {'code': 'product_folder', 'name': 'all access to product folder'}, + {'code': 'product', 'name': 'all access to product'}, ] for perm in permissions: diff --git a/core/apps/products/admin/__init__.py b/core/apps/products/admin/__init__.py index ba3c14a..9ef0de0 100644 --- a/core/apps/products/admin/__init__.py +++ b/core/apps/products/admin/__init__.py @@ -1,2 +1,3 @@ from .product import * -from .unity import * \ No newline at end of file +from .unity import * +from .folder import * \ No newline at end of file diff --git a/core/apps/products/admin/folder.py b/core/apps/products/admin/folder.py new file mode 100644 index 0000000..0536095 --- /dev/null +++ b/core/apps/products/admin/folder.py @@ -0,0 +1,13 @@ +from django.contrib import admin + +from core.apps.products.models.folder import Folder, SubFolder + + +@admin.register(Folder) +class FolderAdmin(admin.ModelAdmin): + list_display = ['id', 'name'] + + +@admin.register(SubFolder) +class SubFolderAdmin(admin.ModelAdmin): + list_display = ['id', 'name', 'folder'] \ No newline at end of file diff --git a/core/apps/products/migrations/0005_folder_product_product_code_product_unity_and_more.py b/core/apps/products/migrations/0005_folder_product_product_code_product_unity_and_more.py new file mode 100644 index 0000000..54c6147 --- /dev/null +++ b/core/apps/products/migrations/0005_folder_product_product_code_product_unity_and_more.py @@ -0,0 +1,67 @@ +# Generated by Django 5.2.4 on 2025-08-14 16:59 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('products', '0004_product_type'), + ] + + operations = [ + migrations.CreateModel( + name='Folder', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=200)), + ], + options={ + 'verbose_name': 'Papka', + 'verbose_name_plural': 'Papkalar', + }, + ), + migrations.AddField( + model_name='product', + name='product_code', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AddField( + model_name='product', + name='unity', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='products', to='products.unity'), + ), + migrations.AlterField( + model_name='product', + name='type', + field=models.CharField(choices=[('MECHANISM', 'maxanizm'), ('PRODUCT', 'product'), ('HUMAN_RESOURCE', 'inson resursi'), ('SERVICE', 'xizmat')], default='TANGIBLE', max_length=20), + ), + migrations.AddField( + model_name='product', + name='folder', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='products', to='products.folder'), + ), + migrations.CreateModel( + name='SubFolder', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=200)), + ('folder', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sub_folders', to='products.folder')), + ], + options={ + 'verbose_name': 'Sub Papka', + 'verbose_name_plural': 'Sub Papkalar', + }, + ), + migrations.AddField( + model_name='product', + name='sub_folder', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='products', to='products.subfolder'), + ), + ] diff --git a/core/apps/products/models/__init__.py b/core/apps/products/models/__init__.py index ba3c14a..9ef0de0 100644 --- a/core/apps/products/models/__init__.py +++ b/core/apps/products/models/__init__.py @@ -1,2 +1,3 @@ from .product import * -from .unity import * \ No newline at end of file +from .unity import * +from .folder import * \ No newline at end of file diff --git a/core/apps/products/models/folder.py b/core/apps/products/models/folder.py new file mode 100644 index 0000000..3319c9b --- /dev/null +++ b/core/apps/products/models/folder.py @@ -0,0 +1,27 @@ +from django.db import models + +from core.apps.shared.models import BaseModel + + +class Folder(BaseModel): + name = models.CharField(max_length=200) + + def __str__(self): + return self.name + + class Meta: + verbose_name = 'Papka' + verbose_name_plural = 'Papkalar' + + +class SubFolder(BaseModel): + name = models.CharField(max_length=200) + folder = models.ForeignKey(Folder, on_delete=models.CASCADE, related_name='sub_folders') + + def __str__(self): + return self.name + + class Meta: + verbose_name = 'Sub Papka' + verbose_name_plural = 'Sub Papkalar' + diff --git a/core/apps/products/models/product.py b/core/apps/products/models/product.py index 4b21a11..83cdf7d 100644 --- a/core/apps/products/models/product.py +++ b/core/apps/products/models/product.py @@ -6,12 +6,24 @@ from core.apps.shared.models import BaseModel class Product(BaseModel): TYPE = ( - ("TANGIBLE", "ushlab bo'ladigan"), - ("INTANGIBLE", "ushlab bo'lmaydigan"), + ("MECHANISM", "maxanizm"), + ("PRODUCT", "product"), + ("HUMAN_RESOURCE", "inson resursi"), + ("SERVICE", 'xizmat') ) name = models.CharField(max_length=200) type = models.CharField(max_length=20, choices=TYPE, default="TANGIBLE") + unity = models.ForeignKey( + 'products.Unity', on_delete=models.SET_NULL, related_name='products', null=True + ) + product_code = models.CharField(max_length=200, null=True, blank=True) + folder = models.ForeignKey( + 'products.Folder', on_delete=models.CASCADE, related_name='products', null=True + ) + sub_folder = models.ForeignKey( + 'products.SubFolder', on_delete=models.SET_NULL, related_name='products', null=True, blank=True + ) def __str__(self): return self.name diff --git a/core/apps/products/serializers/folder.py b/core/apps/products/serializers/folder.py new file mode 100644 index 0000000..1011021 --- /dev/null +++ b/core/apps/products/serializers/folder.py @@ -0,0 +1,34 @@ +from rest_framework import serializers + +from core.apps.products.models.folder import Folder, SubFolder + + +class FolderSerializer(serializers.ModelSerializer): + class Meta: + model = Folder + fields = ['id', 'name'] + extra_kwargs = {'id': {'read_only': True}} + + def create(self, validated_data): + return Folder.objects.create(**validated_data) + + def update(self, instance, validated_data): + instance.name = validated_data.get('name', instance.name) + instance.save() + return instance + + +class SubFolderSerializer(serializers.ModelSerializer): + class Meta: + model = SubFolder + fields = ['id', 'name', 'folder'] + extra_kwargs = {'id': {'read_only': True}, "folder": {"write_only": True}} + + def create(self, validated_data): + return SubFolder.objects.create(**validated_data) + + def update(self, instance, validated_data): + instance.name = validated_data.get('name', instance.name) + instance.folder = validated_data.get('folder', instance.folder) + instance.save() + return instance \ No newline at end of file diff --git a/core/apps/products/urls.py b/core/apps/products/urls.py index b4a2ac0..a63f30a 100644 --- a/core/apps/products/urls.py +++ b/core/apps/products/urls.py @@ -2,6 +2,7 @@ from django.urls import path, include from core.apps.products.views import product as product_views from core.apps.products.views import unity as unity_views +from core.apps.products.views import folder as folder_views urlpatterns = [ @@ -15,4 +16,20 @@ urlpatterns = [ path('list/', unity_views.UnityListApiView.as_view()), ] )), + path('folder/', include( + [ + path('create/', folder_views.FolderCreateApiView.as_view()), + path('list/', folder_views.FolderListApiView.as_view()), + path('/update/', folder_views.FolderUdateApiView.as_view()), + path('/delete/', folder_views.FolderDeleteApiView.as_view()), + path('sub_folder/', include( + [ + path('create/', folder_views.SubFolderCreateApiView.as_view()), + path('/list/', folder_views.SubFolderListByFolderIdApiView.as_view()), + path('/delete/', folder_views.SubFolderDeleteApiView.as_view()), + path('/update/', folder_views.SubFolderUpdateApiView.as_view()), + ] + )), + ] + )), ] \ No newline at end of file diff --git a/core/apps/products/views/folder.py b/core/apps/products/views/folder.py new file mode 100644 index 0000000..fda128c --- /dev/null +++ b/core/apps/products/views/folder.py @@ -0,0 +1,76 @@ +from django.shortcuts import get_object_or_404 + +from rest_framework.response import Response +from rest_framework import generics, views + +from core.apps.products.serializers.folder import FolderSerializer, SubFolderSerializer +from core.apps.products.models.folder import Folder, SubFolder +from core.apps.accounts.permissions.permissions import HasRolePermission + + +class FolderCreateApiView(generics.CreateAPIView): + serializer_class = FolderSerializer + queryset = Folder.objects.all() + permission_classes = [HasRolePermission] + required_permissions = ['product_folder'] + + +class FolderListApiView(generics.ListAPIView): + serializer_class = FolderSerializer + queryset = Folder.objects.all() + permission_classes = [HasRolePermission] + required_permissions = ['product_folder'] + pagination_class = None + + +class FolderUdateApiView(generics.UpdateAPIView): + serializer_class = FolderSerializer + queryset = Folder.objects.all() + permission_classes = [HasRolePermission] + required_permissions = ['product_folder'] + lookup_field = 'id' + + + +class FolderDeleteApiView(generics.DestroyAPIView): + serializer_class = FolderSerializer + queryset = Folder.objects.all() + permission_classes = [HasRolePermission] + required_permissions = ['product_folder'] + lookup_field = 'id' + + +class SubFolderCreateApiView(generics.CreateAPIView): + serializer_class = SubFolderSerializer + queryset = SubFolder.objects.all() + permission_classes = [HasRolePermission] + required_permissions = ['product_folder'] + lookup_field = 'id' + + +class SubFolderListByFolderIdApiView(views.APIView): + permission_classes = [HasRolePermission] + required_permissions = ['product_folder'] + + def get(self, request, folder_id): + folder = get_object_or_404(Folder, id=folder_id) + sub_folders = SubFolder.objects.filter(folder=folder) + serializer = SubFolderSerializer(sub_folders, many=True) + return Response(serializer.data, status=200) + + +class SubFolderUpdateApiView(generics.UpdateAPIView): + serializer_class = SubFolderSerializer + queryset = SubFolder.objects.all() + lookup_field = 'id' + permission_classes = [HasRolePermission] + required_permissions = ['product_folder'] + + +class SubFolderDeleteApiView(generics.DestroyAPIView): + serializer_class = SubFolderSerializer + queryset = SubFolder.objects.all() + lookup_field = 'id' + permission_classes = [HasRolePermission] + required_permissions = ['product_folder'] + diff --git a/resources/logs/django.log.2025-08-13 b/resources/logs/django.log.2025-08-13 new file mode 100644 index 0000000..4bf7194 --- /dev/null +++ b/resources/logs/django.log.2025-08-13 @@ -0,0 +1 @@ +WARNING 2025-08-13 09:59:06,248 log Forbidden: /api/v1/accounts/permission/list/