Skip to content

Commit 22f5de3

Browse files
authored
Merge pull request #39 from PROCOLLAB-github/dev
Dev
2 parents 662e760 + f60f3d5 commit 22f5de3

8 files changed

Lines changed: 171 additions & 5 deletions

File tree

users/admin.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django.contrib import admin
22

3-
from .models import CustomUser
3+
from .models import CustomUser, UserAchievement
44

55

66
@admin.register(CustomUser)
@@ -91,3 +91,8 @@ class CustomUserAdmin(admin.ModelAdmin):
9191
def save_model(self, request, obj, form, change):
9292
obj.set_password(form.cleaned_data["password"])
9393
obj.save()
94+
95+
96+
@admin.register(UserAchievement)
97+
class UserAchievementAdmin(admin.ModelAdmin):
98+
list_display = ("id", "title", "status", "user")

users/managers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from django.contrib.auth.hashers import make_password
22
from django.contrib.auth.models import UserManager
3+
from django.db.models import Manager
34

45

56
class CustomUserManager(UserManager):
@@ -32,6 +33,7 @@ def get_users_for_detail_view(self):
3233
"member__preferred_industries",
3334
"expert__preferred_industries",
3435
"investor__preferred_industries",
36+
"achievements",
3537
)
3638
.all()
3739
)
@@ -42,3 +44,19 @@ def _create_user(self, email, password, **extra_fields):
4244
user.password = make_password(password)
4345
user.save()
4446
return user
47+
48+
49+
class UserAchievementManager(Manager):
50+
def get_achievements_for_list_view(self):
51+
return (
52+
self.get_queryset()
53+
.select_related("user")
54+
.only("id", "title", "status", "user__id")
55+
)
56+
57+
def get_achievements_for_detail_view(self):
58+
return (
59+
self.get_queryset()
60+
.select_related("user")
61+
.only("id", "title", "status", "user")
62+
)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Generated by Django 4.1.2 on 2022-11-16 16:01
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
("users", "0015_customuser_speciality"),
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name="UserAchievement",
17+
fields=[
18+
(
19+
"id",
20+
models.BigAutoField(
21+
auto_created=True,
22+
primary_key=True,
23+
serialize=False,
24+
verbose_name="ID",
25+
),
26+
),
27+
("title", models.CharField(max_length=256)),
28+
("status", models.CharField(max_length=256)),
29+
(
30+
"user",
31+
models.ForeignKey(
32+
null=True,
33+
on_delete=django.db.models.deletion.SET_NULL,
34+
related_name="achievements",
35+
to=settings.AUTH_USER_MODEL,
36+
),
37+
),
38+
],
39+
options={
40+
"verbose_name": "Достижение",
41+
"verbose_name_plural": "Достижения",
42+
},
43+
),
44+
]

users/models.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
VERBOSE_ROLE_TYPES,
1414
VERBOSE_USER_TYPES,
1515
)
16-
from users.managers import CustomUserManager
16+
from users.managers import CustomUserManager, UserAchievementManager
1717

1818

1919
def get_default_user_type():
@@ -91,6 +91,36 @@ def __str__(self):
9191
return f"User<{self.id}> - {self.first_name} {self.last_name}"
9292

9393

94+
class UserAchievement(models.Model):
95+
"""
96+
UserAchievement model
97+
98+
Attributes:
99+
title: A CharField title of the achievement.
100+
status: A CharField place or status of the achievement.
101+
user: A ForeignKey referring to the CustomUser model.
102+
"""
103+
104+
title = models.CharField(max_length=256, null=False)
105+
status = models.CharField(max_length=256, null=False)
106+
107+
user = models.ForeignKey(
108+
CustomUser,
109+
on_delete=models.SET_NULL,
110+
null=True,
111+
related_name="achievements",
112+
)
113+
114+
objects = UserAchievementManager()
115+
116+
def __str__(self):
117+
return f"UserAchievement<{self.id}>"
118+
119+
class Meta:
120+
verbose_name = "Достижение"
121+
verbose_name_plural = "Достижения"
122+
123+
94124
class AbstractUserWithRole(models.Model):
95125
"""
96126
AbstractUserWithRole abstract model

users/permissions.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from rest_framework.permissions import BasePermission, SAFE_METHODS
2+
3+
4+
class IsAchievementOwnerOrReadOnly(BasePermission):
5+
"""
6+
Allows access to update only to himself.
7+
"""
8+
9+
def has_permission(self, request, view) -> bool:
10+
if request.method in SAFE_METHODS or (
11+
request.user and request.user.id == request.data.get("user")
12+
):
13+
return True
14+
return False
15+
16+
def has_object_permission(self, request, view, obj):
17+
if request.method in SAFE_METHODS or (obj.user == request.user):
18+
return True
19+
return False

users/serializers.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
from django.forms.models import model_to_dict
22
from rest_framework import serializers
33

4-
from .models import CustomUser, Expert, Investor, Member, Mentor
4+
from .models import CustomUser, Expert, Investor, Member, Mentor, UserAchievement
5+
6+
7+
class AchievementListSerializer(serializers.ModelSerializer):
8+
class Meta:
9+
model = UserAchievement
10+
fields = [
11+
"id",
12+
"title",
13+
"status",
14+
]
515

616

717
class MemberSerializer(serializers.ModelSerializer):
@@ -45,6 +55,7 @@ class UserDetailSerializer(serializers.ModelSerializer):
4555
investor = InvestorSerializer(required=False)
4656
expert = ExpertSerializer(required=False)
4757
mentor = MentorSerializer(required=False)
58+
achievements = AchievementListSerializer(required=False, many=True)
4859

4960
class Meta:
5061
model = CustomUser
@@ -63,6 +74,7 @@ class Meta:
6374
"investor",
6475
"expert",
6576
"mentor",
77+
"achievements",
6678
]
6779

6880
def update(self, instance, validated_data):
@@ -90,12 +102,19 @@ def update(self, instance, validated_data):
90102
# maybe it's better to write ALLOWED_UPDATABLE_FIELDS = ["first_name", "last_name", ...]
91103
IMMUTABLE_FIELDS = ("email", "user_type", "is_active", "password")
92104
USER_TYPE_FIELDS = ("member", "investor", "expert", "mentor")
93-
105+
RELATED_FIELDS = ("achievements",)
94106
for attr, value in validated_data.items():
95-
if attr in IMMUTABLE_FIELDS + USER_TYPE_FIELDS:
107+
if attr in IMMUTABLE_FIELDS + USER_TYPE_FIELDS + RELATED_FIELDS:
96108
continue
97109
setattr(instance, attr, value)
98110

111+
# if "achievements" in validated_data:
112+
# instance.achievements.all().delete()
113+
# instance.achievements.clear()
114+
# for achievement in validated_data["achievements"]:
115+
# obj = UserAchievement(**achievement)
116+
# obj.save()
117+
# instance.achievements.add(obj)
99118
instance.save()
100119

101120
return instance
@@ -127,6 +146,17 @@ class Meta:
127146
extra_kwargs = {"password": {"write_only": True}}
128147

129148

149+
class AchievementDetailSerializer(serializers.ModelSerializer):
150+
class Meta:
151+
model = UserAchievement
152+
fields = [
153+
"id",
154+
"title",
155+
"status",
156+
"user",
157+
]
158+
159+
130160
class EmailSerializer(serializers.Serializer):
131161
email = serializers.EmailField()
132162

users/urls.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from django.urls import path, re_path
22

33
from users.views import (
4+
AchievementDetail,
5+
AchievementList,
46
CurrentUser,
57
EmailResetPassword,
68
ResetPassword,
@@ -24,6 +26,8 @@
2426
path("users/<int:pk>/", UserDetail.as_view()),
2527
path("users/reset-password/", EmailResetPassword.as_view()),
2628
path("users/current/", CurrentUser.as_view()),
29+
path("users/achievements/", AchievementList.as_view()),
30+
path("users/achievements/<int:pk>/", AchievementDetail.as_view()),
2731
re_path(
2832
r"^account-confirm-email/",
2933
VerifyEmail.as_view(),

users/views.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
from core.permissions import IsOwnerOrReadOnly
2525
from core.utils import Email
2626
from users.helpers import VERBOSE_ROLE_TYPES
27+
from users.models import UserAchievement
28+
from users.permissions import IsAchievementOwnerOrReadOnly
2729
from users.serializers import (
30+
AchievementDetailSerializer,
31+
AchievementListSerializer,
2832
EmailSerializer,
2933
PasswordSerializer,
3034
UserDetailSerializer,
@@ -254,3 +258,15 @@ def update(self, request, *args, **kwargs):
254258
status=status.HTTP_400_BAD_REQUEST,
255259
message="Decode error",
256260
)
261+
262+
263+
class AchievementList(ListCreateAPIView):
264+
queryset = UserAchievement.objects.get_achievements_for_list_view()
265+
serializer_class = AchievementListSerializer
266+
permission_classes = [IsAchievementOwnerOrReadOnly]
267+
268+
269+
class AchievementDetail(RetrieveUpdateDestroyAPIView):
270+
queryset = UserAchievement.objects.get_achievements_for_detail_view()
271+
serializer_class = AchievementDetailSerializer
272+
permission_classes = [IsAchievementOwnerOrReadOnly]

0 commit comments

Comments
 (0)