Skip to content

Commit c129200

Browse files
committed
Merge remote-tracking branch 'origin/dev' into dev
2 parents e3b29fb + 0bd344e commit c129200

14 files changed

Lines changed: 256 additions & 27 deletions
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 4.1.2 on 2022-11-17 13:58
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("projects", "0005_alter_project_collaborators_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="project",
15+
name="name",
16+
field=models.CharField(blank=True, max_length=256, null=True),
17+
),
18+
migrations.AlterField(
19+
model_name="project",
20+
name="step",
21+
field=models.PositiveSmallIntegerField(
22+
blank=True,
23+
choices=[
24+
(0, "Идея"),
25+
(1, "Прототип"),
26+
(2, "MVP(Минимально жизнеспособный продукт)"),
27+
(3, "Первые продажи"),
28+
(4, "Масштабирование"),
29+
],
30+
null=True,
31+
),
32+
),
33+
]

projects/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ class Project(models.Model):
3030
datetime_updated: A DateTimeField indicating date of update.
3131
"""
3232

33-
name = models.CharField(max_length=256, null=False)
33+
name = models.CharField(max_length=256, null=True, blank=True)
3434
description = models.TextField(blank=True)
3535
short_description = models.TextField(blank=True)
3636
region = models.CharField(max_length=256, blank=True)
37-
step = models.PositiveSmallIntegerField(choices=VERBOSE_STEPS, null=False)
37+
step = models.PositiveSmallIntegerField(choices=VERBOSE_STEPS, null=True, blank=True)
3838

3939
industry = models.ForeignKey(
4040
Industry,

projects/serializers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class Meta:
1414
"title",
1515
"status",
1616
]
17+
ref_name = "Projects"
1718

1819

1920
class ProjectAchievementListSerializer(serializers.ModelSerializer):
@@ -168,3 +169,4 @@ class Meta:
168169
"status",
169170
"projects",
170171
]
172+
ref_name = "Projects"

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: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
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+
]
15+
ref_name = "Users"
516

617

718
class MemberSerializer(serializers.ModelSerializer):
@@ -45,6 +56,7 @@ class UserDetailSerializer(serializers.ModelSerializer):
4556
investor = InvestorSerializer(required=False)
4657
expert = ExpertSerializer(required=False)
4758
mentor = MentorSerializer(required=False)
59+
achievements = AchievementListSerializer(required=False, many=True)
4860

4961
class Meta:
5062
model = CustomUser
@@ -55,6 +67,7 @@ class Meta:
5567
"first_name",
5668
"last_name",
5769
"patronymic",
70+
"birthday",
5871
"speciality",
5972
"avatar",
6073
"city",
@@ -63,6 +76,7 @@ class Meta:
6376
"investor",
6477
"expert",
6578
"mentor",
79+
"achievements",
6680
]
6781

6882
def update(self, instance, validated_data):
@@ -90,9 +104,9 @@ def update(self, instance, validated_data):
90104
# maybe it's better to write ALLOWED_UPDATABLE_FIELDS = ["first_name", "last_name", ...]
91105
IMMUTABLE_FIELDS = ("email", "user_type", "is_active", "password")
92106
USER_TYPE_FIELDS = ("member", "investor", "expert", "mentor")
93-
107+
RELATED_FIELDS = ("achievements",)
94108
for attr, value in validated_data.items():
95-
if attr in IMMUTABLE_FIELDS + USER_TYPE_FIELDS:
109+
if attr in IMMUTABLE_FIELDS + USER_TYPE_FIELDS + RELATED_FIELDS:
96110
continue
97111
setattr(instance, attr, value)
98112

@@ -113,18 +127,32 @@ class Meta:
113127
model = CustomUser
114128
fields = [
115129
"id",
116-
"user_type",
117130
"email",
131+
"user_type",
118132
"first_name",
119133
"last_name",
120134
"patronymic",
121135
"avatar",
136+
"speciality",
137+
"birthday",
122138
"is_active",
123139
"password",
124140
]
125141
extra_kwargs = {"password": {"write_only": True}}
126142

127143

144+
class AchievementDetailSerializer(serializers.ModelSerializer):
145+
class Meta:
146+
model = UserAchievement
147+
fields = [
148+
"id",
149+
"title",
150+
"status",
151+
"user",
152+
]
153+
ref_name = "Users"
154+
155+
128156
class EmailSerializer(serializers.Serializer):
129157
email = serializers.EmailField()
130158

users/urls.py

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

33
from users.views import (
4+
AchievementDetail,
5+
AchievementList,
46
CurrentUser,
57
EmailResetPassword,
68
ResetPassword,
79
SpecialistsList,
8-
UserAdditionalRoles,
10+
UserAdditionalRolesView,
911
UserDetail,
1012
UserList,
11-
UserTypes,
13+
UserTypesView,
1214
VerifyEmail,
1315
)
1416

@@ -19,11 +21,13 @@
1921
"specialists/", SpecialistsList.as_view()
2022
), # this url actually returns mentors, experts and investors
2123
path("users/", UserList.as_view()),
22-
path("users/roles/", UserAdditionalRoles.as_view()),
23-
path("users/types/", UserTypes.as_view()),
24+
path("users/roles/", UserAdditionalRolesView.as_view()),
25+
path("users/types/", UserTypesView.as_view()),
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(),

0 commit comments

Comments
 (0)