Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 25 additions & 20 deletions apps/account/admin.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
from django.contrib import admin
from django.contrib.auth.models import Group

# from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from apps.account.forms import UserChangeForm, UserCreationForm
from apps.account.models import Role, User, UserPreferences, UserProfile


class UserProfileInline(admin.StackedInline):
model = UserProfile
verbose_name = "Profile"
can_delete = False
extra = 0
exclude = ["deleted_at"]

def has_add_permission(self, request, obj=None):
return False

def has_change_permission(self, request, obj=None):
return False

def has_delete_permission(self, request, obj=None):
return False


@admin.register(User)
class UserAdmin(admin.ModelAdmin):
# The forms to add and change user instances
add_form = UserCreationForm
form = UserChangeForm
model = User

list_display = ["email", "role"]
list_filter = ["role"]
fieldsets = [
(None, {"fields": ["email", "password"]}),
(
"Permissions",
{"fields": ["state", "role"]},
),
("Important dates", {"fields": ["created_at", "updated_at", "deleted_at"]}),
]

inlines = [UserProfileInline]
list_display = ["email", "role", "state", "is_profile_set"]
readonly_fields = ["created_at", "updated_at", "last_login", "role", "date_joined"]
exclude = ["deleted_at", "user_permissions", "groups"]
list_filter = ["role", "is_profile_set"]
search_fields = ["email"]
ordering = ["-created_at"]
filter_horizontal = []
add_fieldsets = [
(
None,
Expand All @@ -41,14 +52,8 @@ class UserAdmin(admin.ModelAdmin):
)
]

search_fields = ["email"]
ordering = ["-created_at"]
readonly_fields = ["created_at", "updated_at"]
filter_horizontal = []


admin.site.register(Role)
admin.site.register(UserProfile)
admin.site.register(UserPreferences)

# since we're not using Django's built-in permissions,
Expand Down
18 changes: 18 additions & 0 deletions apps/account/migrations/0002_alter_user_is_profile_set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.5 on 2025-07-02 10:36

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('account', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='user',
name='is_profile_set',
field=models.BooleanField(default=False),
),
]
2 changes: 1 addition & 1 deletion apps/account/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class User(AbstractUser, AbstractBaseModel):
Role, null=True, blank=True, on_delete=models.RESTRICT, related_name="+"
)

is_profile_set = models.BooleanField(default=True)
is_profile_set = models.BooleanField(default=False)

state = models.ForeignKey(
DataLookup,
Expand Down
19 changes: 1 addition & 18 deletions apps/account/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,21 +115,7 @@ def save(self, **kwargs):
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = [
"first_name",
"last_name",
"phone",
"avatar",
"address",
"created_at",
"updated_at",
]

def create(self, validated_data):
user = self.context["request"].user
validated_data["user"] = user

return super().create(validated_data)
fields = ["first_name", "last_name", "phone", "avatar", "address"]

def to_representation(self, instance):
return UserProfileResponseSerializer(instance, self.context).to_representation(
Expand All @@ -138,8 +124,6 @@ def to_representation(self, instance):


class UserProfileResponseSerializer(serializers.ModelSerializer):
user = UserSerializer()

class Meta:
model = UserProfile
fields = [
Expand All @@ -149,7 +133,6 @@ class Meta:
"phone",
"avatar",
"address",
"user",
"created_at",
"updated_at",
]
14 changes: 13 additions & 1 deletion apps/account/services.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from uuid import UUID
from django.core.cache import cache
from django.shortcuts import get_object_or_404
from apps.account.models import Role
from apps.account.models import Role, UserProfile
from apps.core.exceptions import NotFoundException


class RoleService:
Expand All @@ -16,3 +17,14 @@ def get_cached_role(id: UUID):
cache.set(key, result, timeout=3600)

return result


class UserProfileService:
@staticmethod
def get_user_profile(user) -> UserProfile:
try:
return UserProfile.objects.select_related("user").get(user=user)
except UserProfile.DoesNotExist:
raise NotFoundException(
"Your profile is not not set. Please complete your profile."
)
4 changes: 2 additions & 2 deletions apps/account/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
from apps.account.views import (
RoleViewSet,
UserViewSet,
USerProfileViewSet,
# USerProfileViewSet,
PasswordChangeViewSet,
)

router = DefaultRouter()

router.register("roles", RoleViewSet, basename="roles")
router.register("users", UserViewSet, "users")
router.register("profile", USerProfileViewSet, "company-profile")
# router.register("profile", USerProfileViewSet, "company-profile")
router.register("change-password", PasswordChangeViewSet, "change-password")

urlpatterns = router.urls
66 changes: 40 additions & 26 deletions apps/account/views.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,67 @@
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework import viewsets, status
from rest_framework import viewsets, mixins, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth import get_user_model
from apps.account.models import Role, UserProfile
from drf_spectacular.utils import extend_schema
from apps.account.services import UserProfileService
from apps.account.models import Role
from apps.account.serializers import (
PasswordChangeSerializer,
RoleSerializer,
UserProfileResponseSerializer,
UserSerializer,
UserProfileSerializer,
)

from apps.core.views import AbstractModelViewSet


User = get_user_model()


class RoleViewSet(AbstractModelViewSet):
class RoleViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [AllowAny]
http_method_names = ["get"]
queryset = Role.objects.all()
serializer_class = RoleSerializer


class UserViewSet(AbstractModelViewSet):
class UserViewSet(
mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [AllowAny]
serializer_class = UserSerializer
queryset = User.objects.select_related("role", "state").all()

@extend_schema(
request=UserProfileSerializer, responses=UserProfileResponseSerializer
)
@action(methods=["get", "post", "patch"], detail=False, url_path="me")
def profile(self, request, *args, **kwargs):
user = request.user

if request.method == "GET":
profile = UserProfileService.get_user_profile(user)
serializer = UserProfileSerializer(profile)
return Response(serializer.data)

if request.method == "POST":
serializer = UserProfileSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(user=user)
user.is_profile_set = True
user.save()
return Response(serializer.data, status=status.HTTP_200_OK)

elif request.method == "PATCH":
profile = UserProfileService.get_user_profile(user)
serializer = UserProfileSerializer(profile, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)


class PasswordChangeViewSet(viewsets.ViewSet):
permission_classes = [IsAuthenticated]
Expand All @@ -50,22 +83,3 @@ def create(self, request):
},
status=status.HTTP_200_OK,
)


class USerProfileViewSet(AbstractModelViewSet):
permission_classes = [IsAuthenticated]
http_method_names = ["get", "post", "patch"]
serializer_class = UserProfileSerializer

def get_queryset(self):
return UserProfile.objects.filter(user=self.request.user)

# @property
# def access_policy(self):
# return self.permission_classes[0]

# def get_queryset(self):
# return self.access_policy.scope_queryset(
# request=self.request,
# queryset=UserProfile.objects.all()
# )
6 changes: 6 additions & 0 deletions apps/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ class ServiceBaseException(APIException):
default_code = "TICKETING_ERROR"


class NotFoundException(APIException):
status_code = 404
default_detail = "Not found"
default_code = "NOT_FOUND"


class SerializationError(APIException):
status_code = 500
default_detail = "Object Serialization Error"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.