Repository: vas3k/vas3k.club

Issue:
Баг: При попытке добавления экспертизы в Профиль ошибка Server Error (500)
https://vas3k.club/profile/expertise/add/

Это если попытаться добавить Экспертизу с таким же именем каким уже была добавленна, хотел для теста попробовать такую же добавить, но с другим уровнем экспертизы. Получил ошибку Server Error (500).


Discussion:
Срабатывает duplicated key в БД. Нужно ловить эксепшен. Оставлю для новичков

Resolving patch:
diff --git a/common/data/expertise.py b/common/data/expertise.py
--- a/common/data/expertise.py
+++ b/common/data/expertise.py
@@ -1,3 +1,5 @@
+from itertools import chain
+
 EXPERTISE = [
     ("Хард-скиллы", [
         ("frontend", "Фронтенд"),
@@ -47,3 +49,6 @@
         ("csharp", "C#"),
     ])
 ]
+
+
+EXPERTISE_FLAT_MAP = dict(chain.from_iterable(items for _, items in EXPERTISE))
diff --git a/users/forms/profile.py b/users/forms/profile.py
--- a/users/forms/profile.py
+++ b/users/forms/profile.py
@@ -110,7 +110,15 @@ def clean(self):
         super().clean()
         custom_expertise = self.cleaned_data.get("expertise_custom")
         if custom_expertise:
-            self.cleaned_data["expertise"] = custom_expertise
+            self.cleaned_data["expertise"] = UserExpertise.make_custom_expertise_slug(custom_expertise)
 
         if not self.cleaned_data["expertise"]:
             raise ValidationError("Name is required")
+
+    def save(self, commit=True):
+        instance = super().save(commit=commit)
+        custom_expertise = self.cleaned_data.get("expertise_custom")
+        if custom_expertise:
+            instance.name = custom_expertise
+        return instance
+
diff --git a/users/models/expertise.py b/users/models/expertise.py
--- a/users/models/expertise.py
+++ b/users/models/expertise.py
@@ -4,14 +4,16 @@
 from slugify import slugify
 
 from common.data.colors import COOL_COLORS
-from common.data.expertise import EXPERTISE
+from common.data.expertise import EXPERTISE_FLAT_MAP
 from users.models.user import User
 
 
 class UserExpertise(models.Model):
+    EXPERTISE_SLUG_LENGTH = 32
+
     id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
     user = models.ForeignKey(User, related_name="expertise", on_delete=models.CASCADE)
-    expertise = models.CharField(max_length=32, null=False, db_index=True)
+    expertise = models.CharField(max_length=EXPERTISE_SLUG_LENGTH, null=False, db_index=True)
     name = models.CharField(max_length=64, null=False)
     value = models.IntegerField(default=0, null=False)
     created_at = models.DateTimeField(auto_now_add=True)
@@ -22,16 +24,19 @@ class Meta:
         ordering = ["created_at"]
 
     def save(self, *args, **kwargs):
-        pre_defined_expertise = dict(sum([e[1] for e in EXPERTISE], []))  # flatten nested items
 
         if not self.name:
-            self.name = pre_defined_expertise.get(self.expertise) or self.expertise
+            self.name = EXPERTISE_FLAT_MAP.get(self.expertise) or self.expertise
 
-        if self.expertise not in pre_defined_expertise:
-            self.expertise = slugify(self.expertise.lower())[:32]
+        if self.expertise not in EXPERTISE_FLAT_MAP:
+            self.expertise = self.make_custom_expertise_slug(self.expertise)
 
         return super().save(*args, **kwargs)
 
     @property
     def color(self):
         return COOL_COLORS[hash(self.name) % len(COOL_COLORS)]
+
+    @classmethod
+    def make_custom_expertise_slug(cls, expertise_name: str):
+        return slugify(expertise_name.lower())[:cls.EXPERTISE_SLUG_LENGTH]


Test patch: