해당 API에 대한 내용이 길어 쪼개서 보시죠.
class CreatePartsRequestViewSet(generics.CreateAPIView):
"""
부품 요청 등록
---
"""
serializer_class = CreatePartsRequestSerialize
def get_permissions(self):
created_type = self.request.data.get("created_type", None)
if created_type == RequestType.SELFCLAIM:
return [permissions.AllowAny()]
return [permissions.IsAuthenticated()]
generics.CreateAPIView
가 GenericAPIView
를 상속받기 때문에 속성인 serializer_class
를 가져다 사용 할 수 있다. 해당 변수에 시리얼라이저
를 지정해줌으로써 유효성검사, 직렬화 및 역직렬화 기능을 사용할 수 있다. 지정하지 않을 경우 get_serializer_class()
메서드를 오버라이드해서 사용해야한다.
DRF를 통해 request
를 받을 때 request.data
를 통해 프론트에서 보낸 데이터에 접근한다. 위 코드를 통해 프론트에서 보낸 데이터의 key
중 created_type
에 접근해 값을 얻어 오는데 값이 없을 경우 None
으로 지정한다.
DRF에서의 request immutable 객체인데 프론트에서 보낸 값을 바꿀 수 있다???
해당 api 호출자가 RequestType
객체에 정의되어 있는 SELFCLAIM
변수 값과 일치하면 인증 처리를 거치지 않고 그렇지 않을 경우 인증처리 시행한다.
get_permissions
함수는 CreateAPIView
가 가지고 있는 메서드이다. 아래를 보면 알 수 있겠지만 리스트를 반환하는데 이를 오버라이드해서 사용하고 있다. 오버라이드인 이유는 CreateAPIView가 가지고 있는 메서드는 아래인데 메서드와 다르게 생겼기 때문이다.
"""
원함수(오버라이드 전)
"""
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]
"""
사용자함수(오버라이드 후)
"""
def get_permissions(self):
created_type = self.request.data.get("created_type", None)
if created_type == RequestType.SELFCLAIM:
return [permissions.AllowAny()]
return [permissions.IsAuthenticated()]
permissions.IsAuthenticated
를 통해 permissions.IsAuthenticated 객체를 인스턴스화 해서 리스트 안에 담아 리턴하였다. IsAuthenticated
의 형태다. 반환값을 통해 해당 api 요청자가 존재해야 하고 인증된 요청자여야 한다.class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
Authentication
유저가 인증되었는지 아닌지에 대해 식별
Permissions
각 요청에 대한 허용 또는 거부
Throttling
일정 기간 동안에 허용 할 최대 요청 수
Django에선 인증 및 권한에 대해 위의 3가지 사항으로 처리하고 있다. 위 코드는 각 요청에 IsAuthenticated
에 해당하며 인증된 요청에 한해서 해당 뷰 호출을 허용한다(로그인이 되어 있어야만 접근 허용)
: 현재 요청에 대한 허용/거부를 결정, APIView 단위로 지정이 가능하다.
AllowAny
(디폴트 전역 설정) : 인증 여부에 상관없이 뷰 호출을 허용
IsAuthenticated
: 인증된 요청에 한해서 뷰 호출 허용 (로그인이 되어있어야만 접근 허용)
IsAdminUser
: Staff 인증 요청에 한해서 뷰 호출 허용
IsAuthenticatedOrReadOnly
: 비인증 요청에게는 읽기 권한만 허용 (로그인이 되어 있지않아도 조회는 가능)
DjangoModelPermissons
: 인증된 요청에 한하여 뷰 호출 허용, 추가로 장고 모델단위 Permissions 체크
DjangoModelPermissionsOrAnonReadOnly
: DjangoModelPermissions와 유사, 비인증 요청에게는 읽기만 허용
DjangoObjectPermissons
: 비인증 요청은 거부, 인증된 요청은 Object에 대한 권한 체크 수행
APIView
를 상속받기 때문에 permission_classes
변수에 list
형태로 위에서 적용하고 싶은 객체를 선택해서 할당해주면된다. 또는 위에서처럼 CreateAPIView
가지고 있는 메서드인 get_permissions
함수를 오버라이드해서 커스터마이징 해서 사용한다.
CreateAPIView
내부에 있는 post
메서드를 오버라이딩 하여 작성하였다.
"""
원함수
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
"""
사용자함수
"""
@transaction.atomic
@swagger_auto_schema(
operation_summary="summary",
operation_description="content",
responses={status.HTTP_200_OK: PartsRequestWithMatchSerializer},
)
def post(self, request, *args, **kwargs):
data = request.data
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
created_type = data.get("created_type", None)
parts_agent_id = None
if created_type == RequestType.SELFCLAIM:
parts_req_manager = PartsRequestManage()
server_env = settings.SERVER_ENVIROMENT
member = parts_req_manager.get_selfclaim_created_by(server_env=server_env)
parts_agent_id = parts_req_manager.get_selfclaim_agent_parts(
server_env=server_env
).agent_id
else:
member = request.user
serializer.validated_data["created_by"] = member
serializer.validated_data["created_by_name"] = member.name
parts_request = serializer.save()
if "agent_parts" in data:
parts_agent_id = data["agent_parts"]["id"]
if created_type == RequestType.SELFCLAIM:
parts_req_manager = PartsRequestManage()
server_env = settings.SERVER_ENVIROMENT
member = parts_req_manager.get_selfclaim_created_by(server_env=server_env)
parts_agent_id = parts_req_manager.get_selfclaim_agent_parts(
server_env=server_env
).agent_id
else:
member = request.user
serializer.validated_data["created_by"] = member
serializer.validated_data["created_by_name"] = member.name
parts_request = serializer.save()
PartsRequestManage
해당 객체는 접수된 요청 사항과 관련한 여러개의 메서드를 갖고 있다. 부품사 매칭상태변환
공업사 매칭상태변환
부품 배송완료시 설정
부품 요청업체변경
등등 즉 접수된 요청과 관련한 매니징을 하고 싶을 때 해당 객체를 불러와 사용한다.
get_selfclaim_created_by
메서드와 get_selfclaim_agent_parts
메서드는 셀프클레임이 들어왔을 때 local, dev, prod 환경에 따라 요청한 서비스에 대해 요청자의 member 모델
과 부품사의 아이디
를 지정해주는 작업이다.
member 모델
의 경우 관리자 아이디
를 가지고 오고 부품사
의 경우 해당하는 부품사를 찾아 부품사 아이디
를 가지고 온다.
셀프 클레임이 아닌 요청에 대해선 그냥 요청자의 정보를 담은 member
모델을 반환한다.
이후 시리얼라이저
에서 필드에 접근해 값을 할당하는 과정을 거친다. 그 전에 이 serializer
를 불러들이는 코드를 볼 필요가 있다.
serializer
를 들고 올 때 아래와 같이 들고온다. 여기서 get_serializer_class
와 get_serializer
의 차이를 알면 유용하다.
serializer = self.get_serializer(data=data)
전자
는 해당 메서드를 가진 객체 내부에 serializer_class
변수에 값이 할당되어 있을 때 serializer_class
를 반환한다.
def get_serializer_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`.
You may want to override this if you need to provide different
serializations depending on the incoming request.
(Eg. admins get full serialization, others get basic serialization)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
후자
는 해당 객체가 serializer_class
에 시리얼라이저
가 할당되어 있다는 전제하에 해당 매개변수 kwargs
에 get_serializer_context
의 딕셔너리 형태의 반환 값을 붙인 kwargs
를 serializer_class
객체의 인자로 넣어 인스턴스화 한 결과를 반환한다.
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs.setdefault('context', self.get_serializer_context())
return serializer_class(*args, **kwargs)
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
serializer_class에 CreatePartsRequestSerialize
를 지정한다.
프론트에서 보낸 데이터에 get_serializer
메서드를 통해 key
값을 context
로 value
를 get_serializer_context
메서드 반환값으로 한 딕셔너리 형태의 데이터를 추가한 kwargs
(딕셔너리 형태의 데이터)를 serializer_class인 CreatePartsRequestSerialize
에 인자로 전달한 인스턴스를 반환한다.
is_valid
메서드를 통해 유효성 검사를 할 때 raise_exception=True
를 is_valid 뒤에 추가해 주면 raise된 에러를 클라이언트에 전달한다.
- 사용자가 요청한 데이터에 접근해서 직렬화 시킨 후 유효성 검사를 실시한다.
- 사용자가 요청한 요청건에 관련한 값을 가져온다. [ REQUEST, CANCELED, GIVEUP, SEND_ESTIMATE, CONFIRM_ORDER, COMPLETE_DELIVERY, VEHICLE_RELEASE ] 중에서
- 사용자의 요청이 셀프클레임인 경우 필요한 로직을 수행하고 셀프클레임이 아닌 경우 요청한 사용자의 정보를 가져와 member 변수에 담는다.
- 유효성 검사를 실시한 시리얼라이저의 필드에 접근해 사용자의 정보와 사용자의 이름을 각각의 변수에 할당한 후 저장한다.
- 사용자가 요청한 정보에
agent_parts
필드가 있으면 해당 값의 아이디를 추출해 해당 변수에 담는다.
serializer 객체 접근하는 법
request.user 의 정체
# 매칭 생성
requestPartsMatch = RequestPartsMatch()
match = requestPartsMatch.requestPartsMatch(
partsRequest=parts_request, member=member, parts_agent_id=parts_agent_id
)
notiLogic = PartsRequestNotiLogic()
notiLogic.publishMatchSuccess(match=match)
if created_type == RequestType.SELFCLAIM:
booking_date = parts_request.booking_date.strftime("%Y년%m월%d일%H시%M분")
data = {
"name": parts_request.booking_owner_name,
"phone": parts_request.booking_owner_phone,
"date": booking_date[:11],
"time": booking_date[11:],
"car_number": parts_request.car_number,
"agent_name": parts_request.req_agent.name,
"agent_location": parts_request.req_agent.agent_carcenter.address,
}
coolsms_helper = CoolSmsHelper.reserve_complete(data)
logging.info(coolsms_helper)
send_not_confirm_match.apply_async(args=[match.id], countdown=60 * 30)
parts_req_serializer = PartsRequestWithMatchSerializer(parts_request)
return Response(parts_req_serializer.data, status=status.HTTP_200_OK)
RequestPartsMatch
객체는 서비스 접수 후 (보험사, 공업사, 부품사) 안에서 3자 또는 2자간 매칭이 되었을 때
필요한 로직들을 수행하는 메서드를 들고있는 객체이다. 메서드의 경우 필요한 값을 해당 객체에 할당하는 작업이다.
해당 객체의 인스턴스를 통해 매칭에 필요한 메서드를 수행하고 PartsBrokMatch
객체를 반환한다. 해당 객체는 접수받은 요청과 관련해서 전반적인 데이터를 제공하는 모델
이다.
PartsRequestNotiLogic
객체는 서비스 접수 후 3자 또는 2자가 연결되었을 때 서비스에 참여한 당사자에게 메세지를 보내는 메서드를 갖고 있는 객체이다.
publishMatchSuccess
메서드가 매개변수로 PartsBrokMatch
객체를 전달 받기에 아래 requestPartsMatch
메서드 반환 값의 데이터 타입은 PartsBrokMatch
이다.
match = requestPartsMatch.requestPartsMatch(
partsRequest=parts_request, member=member,
parts_agent_id=parts_agent_id
)
notiLogic = PartsRequestNotiLogic()
notiLogic.publishMatchSuccess(match=match)
CoolSmsHelper 객체의 경우 아래의 외부 모듈을 들고와서 외부 api를 사용한다.
from sdk.api.message import Message
from coolsms.src.lib import message
reserve_complete
해당 메서드는 해당 api를 이용하기 위해 data를 규격에 맞게 변환해 주는 메서드이고 데이터를 담은 객체를 반환해서 서버에 로그가 찍히도록 하는 역할을 수행한다.
이로서 서버에 로그가 찍히게 하는 것이 중요하다는 것을 알 수 있다.
coolsms_helper = CoolSmsHelper.reserve_complete(data)
logging.info(coolsms_helper)
RabbitMQ와 Celery를 활용하여 만일 주문확정이 30분 동안 되지 않았을 경우 관리자에게 요청이 되지 않았다고 메세지가 보내지고 서버에 주문 건이 확정되지 않았다고 로그가 기록된다.
send_not_confirm_match.apply_async(args=[match.id], countdown=60 * 30)