@@ -1,18 +1,20 @@
|
|||||||
import os
|
import os
|
||||||
|
from django.utils.deconstruct import deconstructible
|
||||||
|
|
||||||
def dynamic_upload_to(prefix, field_name_func):
|
@deconstructible
|
||||||
def wrapper(instance, filename):
|
class DynamicUploadTo:
|
||||||
|
def __init__(self, prefix, field_name):
|
||||||
|
self.prefix = prefix
|
||||||
|
self.field_name = field_name
|
||||||
|
|
||||||
|
def __call__(self, instance, filename):
|
||||||
ext = filename.split('.')[-1]
|
ext = filename.split('.')[-1]
|
||||||
field_name = field_name_func(instance)
|
fname = self.field_name(instance) if callable(self.field_name) else self.field_name
|
||||||
|
if self.prefix == "user":
|
||||||
if prefix == 'user':
|
filename = f"{instance.nickname}-{fname}.{ext}"
|
||||||
filename = f'{instance.nickname}-{field_name}.{ext}'
|
|
||||||
else:
|
else:
|
||||||
filename = f'{instance.id}-{field_name}.{ext}'
|
filename = f"{instance.id}-{fname}.{ext}"
|
||||||
|
return os.path.join(self.prefix, filename)
|
||||||
return os.path.join(prefix, filename)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
def file_delete(obj, field):
|
def file_delete(obj, field):
|
||||||
getattr(obj, field).delete(save=False)
|
getattr(obj, field).delete(save=False)
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ from django.conf import settings
|
|||||||
def connect_colio_mongo():
|
def connect_colio_mongo():
|
||||||
mongoengine.connect(
|
mongoengine.connect(
|
||||||
db=settings.MONGODB_NAME,
|
db=settings.MONGODB_NAME,
|
||||||
host=settings.MONGODB_URI
|
host=settings.MONGODB_URI,
|
||||||
|
tz_aware=True,
|
||||||
)
|
)
|
||||||
@@ -167,7 +167,7 @@ TIME_ZONE = 'Asia/Seoul'
|
|||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
USE_TZ = False
|
USE_TZ = True
|
||||||
|
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = '/media/'
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ from django.conf import settings
|
|||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
# path('admin/', admin.site.urls),
|
||||||
path('api/user/', include('users.urls')),
|
path('api/user/', include('users.urls')),
|
||||||
path('api/code/', include('codes.urls')),
|
path('api/code/', include('codes.urls')),
|
||||||
path('api/portfolio/', include('portfolios.urls')),
|
path('api/portfolio/', include('portfolios.urls')),
|
||||||
path('api/project/', include('projects.urls')),
|
path('api/project/', include('projects.urls')),
|
||||||
path('api/notification/', include('notifications.urls')),
|
path('api/notification/', include('notifications.urls')),
|
||||||
|
path('api/nocodetool/', include('nocodetools.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:
|
||||||
|
|||||||
@@ -1,14 +1,25 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
import mongoengine as me
|
import mongoengine as me
|
||||||
|
|
||||||
|
|
||||||
class Element(me.EmbeddedDocument):
|
class Element(me.EmbeddedDocument):
|
||||||
|
element_id = me.StringField()
|
||||||
element_type = me.StringField()
|
element_type = me.StringField()
|
||||||
content = me.StringField()
|
content = me.StringField()
|
||||||
css = me.DictField()
|
css = me.DictField()
|
||||||
|
|
||||||
class Page(me.Document):
|
class Page(me.EmbeddedDocument):
|
||||||
cut = me.IntField()
|
cut = me.IntField()
|
||||||
elements = me.ListField(me.EmbeddedDocumentField(Element))
|
elements = me.ListField(me.EmbeddedDocumentField(Element))
|
||||||
created_at = me.DateTimeField()
|
|
||||||
updated_at = me.DateTimeField()
|
class Code(me.Document):
|
||||||
|
pages = me.ListField(me.EmbeddedDocumentField(Page))
|
||||||
|
created_at = me.DateTimeField(default=timezone.now)
|
||||||
|
updated_at = me.DateTimeField(default=timezone.now)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.updated_at = timezone.now()
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
26
nocodetools/permissions.py
Normal file
26
nocodetools/permissions.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# permissions.py
|
||||||
|
from rest_framework.permissions import BasePermission
|
||||||
|
from projects.models import Project, ProjectTeamList
|
||||||
|
from portfolios.models import Portfolio
|
||||||
|
|
||||||
|
UNSAFE_REQUEST = ["POST", "PUT", "PATCH", "DELETE"]
|
||||||
|
|
||||||
|
class IsOwnerOrMemberInCreateAndUpdateAndDelete(BasePermission):
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
if request.method not in UNSAFE_REQUEST:
|
||||||
|
return True
|
||||||
|
|
||||||
|
related_type = request.query_params.get("type")
|
||||||
|
related_id = request.query_params.get("id")
|
||||||
|
|
||||||
|
if not related_type or not related_id:
|
||||||
|
return False
|
||||||
|
|
||||||
|
user = request.user
|
||||||
|
|
||||||
|
if related_type == "project":
|
||||||
|
return ProjectTeamList.objects.filter(project=related_id, user=user).exists()
|
||||||
|
elif related_type == "portfolio":
|
||||||
|
return Portfolio.objects.filter(id=related_id, owner=user).exists()
|
||||||
|
else:
|
||||||
|
return False
|
||||||
67
nocodetools/serializers.py
Normal file
67
nocodetools/serializers.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from .models import Code, Page, Element
|
||||||
|
|
||||||
|
# from datetime import datetime, timezone
|
||||||
|
|
||||||
|
from django.utils import timezone
|
||||||
|
# from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
# KST = ZoneInfo("Asia/Seoul")
|
||||||
|
|
||||||
|
class ElementSerializer(serializers.Serializer):
|
||||||
|
element_id = serializers.CharField(required=False)
|
||||||
|
element_type = serializers.CharField()
|
||||||
|
content = serializers.CharField(allow_blank=True)
|
||||||
|
css = serializers.DictField()
|
||||||
|
|
||||||
|
class PageSerializer(serializers.Serializer):
|
||||||
|
cut = serializers.IntegerField()
|
||||||
|
elements = ElementSerializer(many=True)
|
||||||
|
|
||||||
|
class CodeSerializer(serializers.Serializer):
|
||||||
|
id = serializers.SerializerMethodField()
|
||||||
|
pages = PageSerializer(many=True, required=False)
|
||||||
|
created_at = serializers.SerializerMethodField()
|
||||||
|
updated_at = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def get_created_at(self, obj):
|
||||||
|
return timezone.localtime(obj.created_at).isoformat(timespec="seconds")
|
||||||
|
|
||||||
|
def get_updated_at(self, obj):
|
||||||
|
return timezone.localtime(obj.updated_at).isoformat(timespec="seconds")
|
||||||
|
|
||||||
|
def get_id(self, obj):
|
||||||
|
return str(obj.id)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
pages_data = validated_data.pop('pages')
|
||||||
|
pages = [
|
||||||
|
Page(
|
||||||
|
cut=page['cut'],
|
||||||
|
elements=[Element(**el) for el in page['elements']]
|
||||||
|
) for page in pages_data
|
||||||
|
]
|
||||||
|
code = Code(pages=pages, **validated_data)
|
||||||
|
code.save()
|
||||||
|
return code
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
update_pages_data = validated_data.get('pages', [])
|
||||||
|
existing = {p.cut: p for p in instance.pages}
|
||||||
|
|
||||||
|
for page in update_pages_data:
|
||||||
|
cut = page.get('cut')
|
||||||
|
if cut and cut in existing:
|
||||||
|
page_obj = existing[cut]
|
||||||
|
page_obj.elements = [Element(**el) for el in page["elements"]]
|
||||||
|
|
||||||
|
else:
|
||||||
|
instance.pages.append(
|
||||||
|
Page(
|
||||||
|
cut=cut,
|
||||||
|
elements=[Element(**el) for el in page["elements"]]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
29
nocodetools/services.py
Normal file
29
nocodetools/services.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from .models import *
|
||||||
|
from .serializers import *
|
||||||
|
|
||||||
|
from projects.models import Project
|
||||||
|
from portfolios.models import Portfolio
|
||||||
|
|
||||||
|
from projects.serializers import ProjectNocodetoolSerializer
|
||||||
|
from portfolios.serializers import PortfolioNocodetoolSerializer
|
||||||
|
|
||||||
|
NOCODETOOL_MODEL_MAP = {
|
||||||
|
'project': Project,
|
||||||
|
'portfolio': Portfolio,
|
||||||
|
}
|
||||||
|
|
||||||
|
NOCODETOOL_SERIALIZER_MAP = {
|
||||||
|
'project': ProjectNocodetoolSerializer,
|
||||||
|
'portfolio': PortfolioNocodetoolSerializer,
|
||||||
|
}
|
||||||
|
|
||||||
|
class NocodetoolObjectMapService:
|
||||||
|
@staticmethod
|
||||||
|
def mapping_model_instance(related_type: str, related_id: str):
|
||||||
|
object_model = NOCODETOOL_MODEL_MAP.get(related_type)
|
||||||
|
if not object_model:
|
||||||
|
return None
|
||||||
|
return object_model.objects.filter(id=related_id).first()
|
||||||
|
|
||||||
|
def mapping_model_serializer(related_type: str):
|
||||||
|
return NOCODETOOL_SERIALIZER_MAP.get(related_type, None)
|
||||||
11
nocodetools/urls.py
Normal file
11
nocodetools/urls.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from .views import *
|
||||||
|
|
||||||
|
|
||||||
|
app_name = 'nocodetools'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', NoCodeToolAPIView.as_view()),
|
||||||
|
path('working/', NocodeToolWorkingAPIView.as_view()),
|
||||||
|
]
|
||||||
@@ -1 +1,163 @@
|
|||||||
# views.py
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import status, mixins, viewsets
|
||||||
|
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||||
|
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
from .models import Code, Page, Element
|
||||||
|
from .serializers import CodeSerializer
|
||||||
|
from .permissions import IsOwnerOrMemberInCreateAndUpdateAndDelete
|
||||||
|
from .services import NocodetoolObjectMapService
|
||||||
|
|
||||||
|
from users.models import User
|
||||||
|
from portfolios.models import Portfolio
|
||||||
|
from projects.models import Project
|
||||||
|
|
||||||
|
from bson import ObjectId
|
||||||
|
|
||||||
|
|
||||||
|
class NoCodeToolAPIView(APIView):
|
||||||
|
permission_classes = [IsAuthenticated, IsOwnerOrMemberInCreateAndUpdateAndDelete]
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
related_type = request.query_params.get("type")
|
||||||
|
related_id = request.query_params.get("id")
|
||||||
|
code_id = None
|
||||||
|
if obj := NocodetoolObjectMapService.mapping_model_instance(related_type, related_id):
|
||||||
|
code_id = ObjectId(obj.code_id)
|
||||||
|
|
||||||
|
if not code_id:
|
||||||
|
return Response({"message": "Not validated type or no object"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
obj_serializer = NocodetoolObjectMapService.mapping_model_serializer(related_type)
|
||||||
|
|
||||||
|
code = Code.objects.get(id=code_id)
|
||||||
|
return Response({
|
||||||
|
"obj_info": obj_serializer(obj).data,
|
||||||
|
"codes": CodeSerializer(code).data
|
||||||
|
}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def post(self, request):
|
||||||
|
related_type = request.query_params.get("type")
|
||||||
|
related_id = request.query_params.get("id")
|
||||||
|
obj = NocodetoolObjectMapService.mapping_model_instance(related_type, related_id)
|
||||||
|
if not obj:
|
||||||
|
return Response({"message": "No object"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if not obj.now_worker:
|
||||||
|
return Response({"message": "start edit first"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if obj.now_worker != request.user.nickname:
|
||||||
|
return Response({"message": f"{obj.now_worker} is working now"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
data = request.data.copy()
|
||||||
|
thumbnail_file = data.pop("thumbnail", None)
|
||||||
|
|
||||||
|
obj_serializer = NocodetoolObjectMapService.mapping_model_serializer(related_type)
|
||||||
|
|
||||||
|
serializer = CodeSerializer(data=request.data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
code = serializer.save()
|
||||||
|
obj.code_id = str(code.id)
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
if thumbnail_file:
|
||||||
|
obj.thumbnail = thumbnail_file
|
||||||
|
obj.save(update_fields=["thumbnail"])
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
"obj_info": obj_serializer(obj).data,
|
||||||
|
"codes": CodeSerializer(code).data
|
||||||
|
}, status=status.HTTP_200_OK)
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def patch(self, request):
|
||||||
|
related_type = request.query_params.get("type")
|
||||||
|
related_id = request.query_params.get("id")
|
||||||
|
obj = NocodetoolObjectMapService.mapping_model_instance(related_type, related_id)
|
||||||
|
if not obj:
|
||||||
|
return Response({"message": "No object"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if not obj.now_worker:
|
||||||
|
return Response({"message": "start edit first"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if obj.now_worker != request.user.nickname:
|
||||||
|
return Response({"message": f"{obj.now_worker} is working now"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
code = Code.objects.filter(id=ObjectId(obj.code_id)).first()
|
||||||
|
if not code:
|
||||||
|
return Response({'message': 'No code object'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
data = request.data.copy()
|
||||||
|
thumbnail_file = data.pop("thumbnail", None)
|
||||||
|
|
||||||
|
obj_serializer = NocodetoolObjectMapService.mapping_model_serializer(related_type)
|
||||||
|
serializer = CodeSerializer(code, data=data, partial=True)
|
||||||
|
if serializer.is_valid():
|
||||||
|
updated_code = serializer.save()
|
||||||
|
|
||||||
|
if thumbnail_file:
|
||||||
|
obj.thumbnail = thumbnail_file
|
||||||
|
obj.save(update_fields=["thumbnail"])
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
"obj_info": obj_serializer(obj).data,
|
||||||
|
"codes": CodeSerializer(updated_code).data
|
||||||
|
}, status=status.HTTP_200_OK)
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def delete(self, request):
|
||||||
|
related_type = request.query_params.get("type")
|
||||||
|
related_id = request.query_params.get("id")
|
||||||
|
obj = NocodetoolObjectMapService.mapping_model_instance(related_type, related_id)
|
||||||
|
if not obj:
|
||||||
|
return Response({"message": "No object"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if not obj.now_worker:
|
||||||
|
return Response({"message": "start edit first"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if obj.now_worker != request.user.nickname:
|
||||||
|
return Response({"message": f"{obj.now_worker} is working now"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
code = Code.objects.filter(id=ObjectId(obj.code_id)).first()
|
||||||
|
if not code:
|
||||||
|
return Response({'message': 'No code object'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
code.delete()
|
||||||
|
obj.code_id = None
|
||||||
|
obj.thumbnail = None
|
||||||
|
obj.save()
|
||||||
|
return Response({"message": "delete code success"}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
class NocodeToolWorkingAPIView(APIView):
|
||||||
|
permission_classes = [IsAuthenticated, IsOwnerOrMemberInCreateAndUpdateAndDelete]
|
||||||
|
@transaction.atomic
|
||||||
|
def patch(self, request): # 수정 시작 or 종료
|
||||||
|
related_type = request.query_params.get("type")
|
||||||
|
related_id = request.query_params.get("id")
|
||||||
|
action = request.query_params.get("action")
|
||||||
|
obj = NocodetoolObjectMapService.mapping_model_instance(related_type, related_id)
|
||||||
|
if not obj:
|
||||||
|
return Response({"message": "No object"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if action == "start":
|
||||||
|
if obj.now_worker:
|
||||||
|
return Response({"message": f"{obj.now_worker} is working"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
obj.now_worker = request.user.nickname
|
||||||
|
elif action == "end":
|
||||||
|
if obj.now_worker and obj.now_worker == request.user.nickname:
|
||||||
|
obj.now_worker = None
|
||||||
|
else:
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
"message": f"{obj.now_worker} is working" if obj.now_worker else "nobody working now"
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
else:
|
||||||
|
return Response({"message": "Not supported action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
return Response({"message": f"work {action}"}, status=status.HTTP_200_OK)
|
||||||
@@ -2,7 +2,7 @@ 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 common.utils.fileManager import DynamicUploadTo
|
||||||
|
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -10,6 +10,8 @@ from django.conf import settings
|
|||||||
from users.models import User
|
from users.models import User
|
||||||
|
|
||||||
|
|
||||||
|
NICKNAME_LEN = 20
|
||||||
|
|
||||||
class Portfolio(BaseModel):
|
class Portfolio(BaseModel):
|
||||||
title = models.CharField(max_length=20)
|
title = models.CharField(max_length=20)
|
||||||
category = ArrayField(models.CharField(max_length=20), default=list)
|
category = ArrayField(models.CharField(max_length=20), default=list)
|
||||||
@@ -18,8 +20,9 @@ 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=dynamic_upload_to('portfolio', lambda instance: 'thumbnail'), blank=True)
|
now_worker = models.CharField(max_length=NICKNAME_LEN, blank=True)
|
||||||
code_id = models.CharField(max_length=26, blank=True)
|
thumbnail = models.ImageField(upload_to=DynamicUploadTo("portfolio", "thumbnail"), null=True, blank=True)
|
||||||
|
code_id = models.CharField(max_length=26, null=True, 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)
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ class PortfolioListViewSerializer(serializers.ModelSerializer):
|
|||||||
model = Portfolio
|
model = Portfolio
|
||||||
fields = ['id', 'category', 'thumbnail', 'title', 'nickname', 'profile_image', 'view_count', 'like_count', 'scrap_count']
|
fields = ['id', 'category', 'thumbnail', 'title', 'nickname', 'profile_image', 'view_count', 'like_count', 'scrap_count']
|
||||||
|
|
||||||
|
class PortfolioNocodetoolSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Portfolio
|
||||||
|
fields = ['title', 'is_published', 'now_worker']
|
||||||
|
|
||||||
class PortfolioCreateSerializer(serializers.ModelSerializer):
|
class PortfolioCreateSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Portfolio
|
model = Portfolio
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ 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 common.utils.fileManager import DynamicUploadTo
|
||||||
|
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -12,6 +12,8 @@ from notifications.models import Notification
|
|||||||
from users.models import User
|
from users.models import User
|
||||||
|
|
||||||
|
|
||||||
|
NICKNAME_LEN = 20
|
||||||
|
|
||||||
class Project(BaseModel):
|
class Project(BaseModel):
|
||||||
title = models.CharField(max_length=20)
|
title = models.CharField(max_length=20)
|
||||||
is_team = models.BooleanField(default=False)
|
is_team = models.BooleanField(default=False)
|
||||||
@@ -22,8 +24,9 @@ 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=dynamic_upload_to('project', lambda instance: 'thumbnail'), blank=True)
|
now_worker = models.CharField(max_length=NICKNAME_LEN, blank=True)
|
||||||
code_id = models.CharField(max_length=26, blank=True)
|
thumbnail = models.ImageField(upload_to=DynamicUploadTo("project", "thumbnail"), null=True, blank=True)
|
||||||
|
code_id = models.CharField(max_length=26, null=True, 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)
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ class ProjectListViewSerializer(serializers.ModelSerializer):
|
|||||||
model = Project
|
model = Project
|
||||||
fields = ['id', 'category', 'thumbnail', 'title', 'view_count', 'like_count', 'scrap_count']
|
fields = ['id', 'category', 'thumbnail', 'title', 'view_count', 'like_count', 'scrap_count']
|
||||||
|
|
||||||
|
class ProjectNocodetoolSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Project
|
||||||
|
fields = ['title', 'is_published', 'now_worker']
|
||||||
|
|
||||||
class ProjectCreateSerializer(serializers.ModelSerializer):
|
class ProjectCreateSerializer(serializers.ModelSerializer):
|
||||||
members = serializers.ListField(
|
members = serializers.ListField(
|
||||||
child=serializers.CharField(), write_only=True, required=False
|
child=serializers.CharField(), write_only=True, required=False
|
||||||
|
|||||||
@@ -2,7 +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 common.utils.fileManager import DynamicUploadTo
|
||||||
|
|
||||||
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
|
||||||
@@ -52,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=dynamic_upload_to('user', lambda instance: 'profile'), blank=True)
|
profile_image = models.ImageField(upload_to=DynamicUploadTo("user", "profile"), blank=True)
|
||||||
banner_image = models.ImageField(upload_to=dynamic_upload_to('user', lambda instance: 'banner'), blank=True)
|
banner_image = models.ImageField(upload_to=DynamicUploadTo("user", "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)
|
||||||
|
|||||||
Reference in New Issue
Block a user