Skip to content

Commit 10191ea

Browse files
committed
Добавлена модель стандартной аватарки проекта, в случае если проект создан без автарки будет выбрана случайная из стандартных
1 parent c8109b7 commit 10191ea

4 files changed

Lines changed: 100 additions & 31 deletions

File tree

projects/admin.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from projects.models import (
44
Achievement,
55
Collaborator,
6+
DefaultProjectAvatar,
67
DefaultProjectCover,
78
Project,
89
ProjectLink,
@@ -141,3 +142,8 @@ class DefaultProjectCoverAdmin(admin.ModelAdmin):
141142
"datetime_created",
142143
"datetime_updated",
143144
)
145+
146+
147+
@admin.register(DefaultProjectAvatar)
148+
class DefaultProjectAvatarAdmin(admin.ModelAdmin):
149+
list_display = ("id", "image", "datetime_created")
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Generated by Django 4.2.11 on 2025-07-11 11:40
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("files", "0007_auto_20230929_1727"),
11+
("projects", "0026_collaborator_specialization"),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name="defaultprojectcover",
17+
name="datetime_created",
18+
field=models.DateTimeField(auto_now_add=True),
19+
),
20+
migrations.AlterField(
21+
model_name="defaultprojectcover",
22+
name="datetime_updated",
23+
field=models.DateTimeField(auto_now=True),
24+
),
25+
migrations.AlterField(
26+
model_name="defaultprojectcover",
27+
name="image",
28+
field=models.ForeignKey(
29+
blank=True,
30+
null=True,
31+
on_delete=django.db.models.deletion.CASCADE,
32+
to="files.userfile",
33+
),
34+
),
35+
migrations.CreateModel(
36+
name="DefaultProjectAvatar",
37+
fields=[
38+
(
39+
"id",
40+
models.BigAutoField(
41+
auto_created=True,
42+
primary_key=True,
43+
serialize=False,
44+
verbose_name="ID",
45+
),
46+
),
47+
("datetime_created", models.DateTimeField(auto_now_add=True)),
48+
("datetime_updated", models.DateTimeField(auto_now=True)),
49+
(
50+
"image",
51+
models.ForeignKey(
52+
blank=True,
53+
null=True,
54+
on_delete=django.db.models.deletion.CASCADE,
55+
to="files.userfile",
56+
),
57+
),
58+
],
59+
options={
60+
"verbose_name": "Аватарка проекта",
61+
"verbose_name_plural": "Аватарки проектов",
62+
},
63+
),
64+
]

projects/models.py

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,54 +16,48 @@
1616
User = get_user_model()
1717

1818

19-
class DefaultProjectCover(models.Model):
19+
class AbstractDefaultProjectImage(models.Model):
2020
"""
21-
Default cover model for projects, is chosen randomly at project creation
22-
23-
Attributes:
24-
image: A ForeignKey referencing the image of the cover.
25-
datetime_created: A DateTimeField indicating date of creation.
26-
datetime_updated: A DateTimeField indicating date of update.
21+
Абстрактная модель для хранения изображений проекта по умолчанию.
2722
"""
2823

2924
image = models.ForeignKey(
3025
UserFile,
3126
on_delete=models.CASCADE,
32-
related_name="default_covers",
3327
null=True,
3428
blank=True,
3529
)
30+
datetime_created = models.DateTimeField(auto_now_add=True)
31+
datetime_updated = models.DateTimeField(auto_now=True)
3632

37-
datetime_created = models.DateTimeField(
38-
verbose_name="Дата создания",
39-
null=False,
40-
auto_now_add=True,
41-
)
42-
datetime_updated = models.DateTimeField(
43-
verbose_name="Дата изменения",
44-
null=False,
45-
auto_now=True,
46-
)
33+
class Meta:
34+
abstract = True
4735

4836
@classmethod
49-
def get_random_file(cls):
50-
# FIXME: this is not efficient, but for ~10 default covers it should be ok
51-
return cls.objects.order_by("?").first().image
37+
def get_random_file(cls) -> Optional[UserFile]:
38+
if not cls.objects.exists():
39+
return None
40+
obj = cls.objects.order_by("?").first()
41+
return obj.image if obj and obj.image else None
5242

5343
@classmethod
54-
def get_random_file_link(cls):
55-
# FIXME: this is not efficient, but for ~10 default covers it should be ok
56-
return (
57-
cls.objects.order_by("?").first().image.link
58-
if cls.objects.order_by("?").first().image
59-
else None
60-
)
44+
def get_random_file_link(cls) -> Optional[str]:
45+
file = cls.get_random_file()
46+
return file.link if file else None
6147

48+
49+
class DefaultProjectCover(AbstractDefaultProjectImage):
6250
class Meta:
6351
verbose_name = "Обложка проекта"
6452
verbose_name_plural = "Обложки проектов"
6553

6654

55+
class DefaultProjectAvatar(AbstractDefaultProjectImage):
56+
class Meta:
57+
verbose_name = "Аватарка проекта"
58+
verbose_name_plural = "Аватарки проектов"
59+
60+
6761
class Project(models.Model):
6862
"""
6963
Project model
@@ -193,9 +187,13 @@ def __str__(self):
193187
return f"Project<{self.id}> - {self.name}"
194188

195189
def save(self, *args, **kwargs):
196-
"""Set random cover image if `cover_image_address` blank."""
197-
if self.cover_image_address is None:
190+
"""Set random cover and avatar images if not provided."""
191+
if not self.cover_image_address:
198192
self.cover_image_address = DefaultProjectCover.get_random_file_link()
193+
194+
if not self.image_address:
195+
self.image_address = DefaultProjectAvatar.get_random_file_link()
196+
199197
super().save(*args, **kwargs)
200198

201199
class Meta:

projects/validators.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
def validate_project(data):
55
if not data.get("draft"):
66
error = {}
7+
allowed_blank = {"image_address"}
78
for key, value in data.items():
8-
if value == "" or value is None:
9+
if (value == "" or value is None) and key not in allowed_blank:
910
error[key] = "This field is required"
1011
if error:
1112
raise ValidationError(error)

0 commit comments

Comments
 (0)