20
common/utils/fileManager.py
Normal file
20
common/utils/fileManager.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
def dynamic_upload_to(prefix, field_name_func):
|
||||||
|
def wrapper(instance, filename):
|
||||||
|
ext = filename.split('.')[-1]
|
||||||
|
field_name = field_name_func(instance)
|
||||||
|
|
||||||
|
if prefix == 'user':
|
||||||
|
filename = f'{instance.nickname}-{field_name}.{ext}'
|
||||||
|
else:
|
||||||
|
filename = f'{instance.id}-{field_name}.{ext}'
|
||||||
|
|
||||||
|
return os.path.join(prefix, filename)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def file_delete(obj, field):
|
||||||
|
getattr(obj, field).delete(save=False)
|
||||||
|
setattr(obj, field, None)
|
||||||
|
obj.save(update_fields=[field])
|
||||||
@@ -59,6 +59,7 @@ INSTALLED_APPS = [
|
|||||||
'rest_framework_simplejwt',
|
'rest_framework_simplejwt',
|
||||||
'rest_framework_simplejwt.token_blacklist',
|
'rest_framework_simplejwt.token_blacklist',
|
||||||
'corsheaders',
|
'corsheaders',
|
||||||
|
'storages',
|
||||||
'users',
|
'users',
|
||||||
'portfolios',
|
'portfolios',
|
||||||
'projects',
|
'projects',
|
||||||
@@ -112,6 +113,25 @@ DATABASES = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# aws s3
|
||||||
|
AWS_ACCESS_KEY_ID = env('AWS_ACCESS_KEY_ID')
|
||||||
|
AWS_SECRET_ACCESS_KEY = env('AWS_SECRET_ACCESS_KEY')
|
||||||
|
AWS_STORAGE_BUCKET_NAME = 'colio-service'
|
||||||
|
AWS_S3_REGION_NAME = 'ap-northeast-2'
|
||||||
|
|
||||||
|
AWS_S3_FILE_OVERWRITE = True # 같은 이름 파일 덮어쓰기
|
||||||
|
AWS_DEFAULT_ACL = None # 권한 제어 (None이면 기본 권한)
|
||||||
|
AWS_QUERYSTRING_AUTH = False # 서명 없는 URL 사용
|
||||||
|
|
||||||
|
# DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
|
||||||
|
|
||||||
|
STORAGES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "storages.backends.s3boto3.S3Boto3Storage",
|
||||||
|
},
|
||||||
|
"staticfiles": "storages.backends.s3boto3.S3Boto3Storage",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ urlpatterns = [
|
|||||||
path('api/notification/', include('notifications.urls')),
|
path('api/notification/', include('notifications.urls')),
|
||||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
|
||||||
if settings.DEBUG:
|
# if settings.DEBUG:
|
||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from django.db import models
|
|||||||
|
|
||||||
from common.models.baseModels import BaseModel
|
from common.models.baseModels import BaseModel
|
||||||
|
|
||||||
|
from common.utils.fileManager import dynamic_upload_to
|
||||||
|
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
@@ -16,8 +18,13 @@ class Portfolio(BaseModel):
|
|||||||
like_count = models.IntegerField(default=0)
|
like_count = models.IntegerField(default=0)
|
||||||
scrap_count = models.IntegerField(default=0)
|
scrap_count = models.IntegerField(default=0)
|
||||||
is_represent = models.BooleanField(default=False)
|
is_represent = models.BooleanField(default=False)
|
||||||
thumbnail = models.ImageField(upload_to='', blank=True)
|
thumbnail = models.ImageField(upload_to=dynamic_upload_to('portfolio', lambda instance: 'thumbnail'), blank=True)
|
||||||
code_id = models.CharField(max_length=26, blank=True)
|
code_id = models.CharField(max_length=26, blank=True)
|
||||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='owned_portfolios', to_field="id")
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='owned_portfolios', to_field="id")
|
||||||
likers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='liked_portfolios', blank=True)
|
likers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='liked_portfolios', blank=True)
|
||||||
scrappers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='scrapped_portfolios', blank=True)
|
scrappers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='scrapped_portfolios', blank=True)
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
if self.thumbnail:
|
||||||
|
self.thumbnail.delete(save=False)
|
||||||
|
super().delete(*args, **kwargs)
|
||||||
@@ -3,6 +3,8 @@ from django.db import models
|
|||||||
from common.models.baseModels import BaseModel
|
from common.models.baseModels import BaseModel
|
||||||
from common.models.choiceModels import InvitationStatus
|
from common.models.choiceModels import InvitationStatus
|
||||||
|
|
||||||
|
from common.utils.fileManager import dynamic_upload_to
|
||||||
|
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
@@ -20,12 +22,17 @@ class Project(BaseModel):
|
|||||||
like_count = models.IntegerField(default=0)
|
like_count = models.IntegerField(default=0)
|
||||||
scrap_count = models.IntegerField(default=0)
|
scrap_count = models.IntegerField(default=0)
|
||||||
is_represent = models.BooleanField(default=False)
|
is_represent = models.BooleanField(default=False)
|
||||||
thumbnail = models.ImageField(upload_to='', blank=True)
|
thumbnail = models.ImageField(upload_to=dynamic_upload_to('project', lambda instance: 'thumbnail'), blank=True)
|
||||||
code_id = models.CharField(max_length=26, blank=True)
|
code_id = models.CharField(max_length=26, blank=True)
|
||||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='owned_projects', to_field="id")
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='owned_projects', to_field="id")
|
||||||
likers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='liked_projects', blank=True)
|
likers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='liked_projects', blank=True)
|
||||||
scrappers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='scrapped_projects', blank=True)
|
scrappers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='scrapped_projects', blank=True)
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
if self.thumbnail:
|
||||||
|
self.thumbnail.delete(save=False)
|
||||||
|
super().delete(*args, **kwargs)
|
||||||
|
|
||||||
class ProjectTeamList(BaseModel):
|
class ProjectTeamList(BaseModel):
|
||||||
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='team_project_member_list', to_field='id')
|
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='team_project_member_list', to_field='id')
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='team_project_list',to_field='id')
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='team_project_list',to_field='id')
|
||||||
|
|||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@@ -2,6 +2,7 @@ from django.db import models
|
|||||||
from common.models.baseModels import BaseModel
|
from common.models.baseModels import BaseModel
|
||||||
from common.models.choiceModels import GenderChoices, CertificateCodeUseType
|
from common.models.choiceModels import GenderChoices, CertificateCodeUseType
|
||||||
from common.utils.codeManger import set_expire
|
from common.utils.codeManger import set_expire
|
||||||
|
from common.utils.fileManager import dynamic_upload_to
|
||||||
|
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
|
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
|
||||||
@@ -51,8 +52,8 @@ class User(BaseModel, AbstractBaseUser, PermissionsMixin):
|
|||||||
skills = ArrayField(models.CharField(max_length=20), default=list, blank=True)
|
skills = ArrayField(models.CharField(max_length=20), default=list, blank=True)
|
||||||
external_links = ArrayField(models.TextField(), default=list, blank=True)
|
external_links = ArrayField(models.TextField(), default=list, blank=True)
|
||||||
short_bio = models.CharField(max_length=100, blank=True)
|
short_bio = models.CharField(max_length=100, blank=True)
|
||||||
profile_image = models.ImageField(upload_to='', blank=True)
|
profile_image = models.ImageField(upload_to=dynamic_upload_to('user', lambda instance: 'profile'), blank=True)
|
||||||
banner_image = models.ImageField(upload_to='', blank=True)
|
banner_image = models.ImageField(upload_to=dynamic_upload_to('user', lambda instance: 'banner'), blank=True)
|
||||||
|
|
||||||
is_staff = models.BooleanField(default=False)
|
is_staff = models.BooleanField(default=False)
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
@@ -65,4 +66,9 @@ class User(BaseModel, AbstractBaseUser, PermissionsMixin):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.nickname
|
return self.nickname
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
if self.profile_image:
|
||||||
|
self.profile_image.delete(save=False)
|
||||||
|
if self.banner_image:
|
||||||
|
self.banner_image.delete(save=False)
|
||||||
|
super().delete(*args, **kwargs)
|
||||||
|
|||||||
@@ -24,6 +24,15 @@ class CheckUserFieldDuplicateService:
|
|||||||
return User.objects.filter(**filter_dict).exists()
|
return User.objects.filter(**filter_dict).exists()
|
||||||
|
|
||||||
|
|
||||||
|
class CheckUserFieldValueExistService:
|
||||||
|
@staticmethod
|
||||||
|
def check_exist(user: User, field) -> bool:
|
||||||
|
if not field:
|
||||||
|
return False
|
||||||
|
if getattr(user, field) == None:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
class UserToNotificationService:
|
class UserToNotificationService:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ from .models import *
|
|||||||
from .serializers import *
|
from .serializers import *
|
||||||
from .services import *
|
from .services import *
|
||||||
|
|
||||||
|
from common.utils.fileManager import file_delete
|
||||||
|
|
||||||
from projects.serializers import *
|
from projects.serializers import *
|
||||||
from portfolios.serializers import *
|
from portfolios.serializers import *
|
||||||
|
|
||||||
@@ -236,6 +238,10 @@ class MyPageProfileAPIView(APIView):
|
|||||||
if user == target_user:
|
if user == target_user:
|
||||||
serializer = UserProfileSerializer(user, request.data, partial=True)
|
serializer = UserProfileSerializer(user, request.data, partial=True)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
|
if serializer.validated_data.get('profile_image') and CheckUserFieldValueExistService.check_exist(user, 'profile_image'):
|
||||||
|
file_delete(user, 'profile_image')
|
||||||
|
if serializer.validated_data.get('banner_image') and CheckUserFieldValueExistService.check_exist(user, 'banner_image'):
|
||||||
|
file_delete(user, 'banner_image')
|
||||||
serializer.save()
|
serializer.save()
|
||||||
data = serializer.data
|
data = serializer.data
|
||||||
data['represent_portfolio_id'] = UserToPortfolioService.get_represent_portfolio(target_user)
|
data['represent_portfolio_id'] = UserToPortfolioService.get_represent_portfolio(target_user)
|
||||||
|
|||||||
Reference in New Issue
Block a user