Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

service_layer 부분 drf와 관련하여 문제가 있어요! #3

Open
happysmileboy opened this issue Jan 16, 2022 · 1 comment
Open

Comments

@happysmileboy
Copy link

def service_layer(service_class, dto_class=None):
"""
add service layer to Serializer
"""
def wrapper(cls):
original_init = cls.__init__
def __init__(self, *args, **kwargs):
original_init(self, *args, **kwargs)
assert (
self.context.get("service") is not None
), f"{self.__class__.__name__} should retrieve service context from view."
service = self.context["service"]
assert isinstance(
service, service_class
), f"{self.__class__.__name__} - The service injected from view and the service declared in the decorator are not the same.({service} - {service_class})"
setattr(self, "service", service)
dto = service.dto
if dto_class:
assert isinstance(
dto, dto_class
), f"{self.__class__.__name__} - The dto injected from view and the dto declared in the decorator are not matched.({service.dto} - {dto})"
setattr(self, "dto", dto)
cls.__init__ = __init__
return cls

안녕하세요. 한국어로 이슈 남기는 게 참 익숙하진 않지만 해당 부분에서 drf 부분으로 문제가 있어서 이슈 남깁니다.

어떤 SomeSerializer가 있을때

@service_layer
class SomeSerializer(serializer.ModelSerializer):

가 있고
serializer = SomeSerializer(objs, many=True)라고 했을 때
service_layer에서 setattr(dto~~) 하는 부분이 작동을 하지 않을 거에요
이유는  drf의 BaseSerializer class의 __new__함수를 보면 알 수 있는데, 

class BaseSerializer(Field):
    ---- 생략 ----
    def __init__(self, instance=None, data=empty, **kwargs):
   ----- 생략 ------

    def __new__(cls, *args, **kwargs):
        # We override this method in order to automatically create
        # `ListSerializer` classes instead when `many=True` is set.
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        return super().__new__(cls, *args, **kwargs)

저기처럼 many true일 경우에는 super().new가 실행되지 않고
cls.many_init을 실행하기 때문입니다.

해당 문제 해결하시려면 init 뿐만 아니라, __new__의 return에도 setattr처리해주셔야해요!

제가 코드를 잘짜는 사람은 아니지만 아래와 같이 고치시면 좋을 것 같아요.

def service_layer(service_class, dto_class=None):
    """
    add service layer to Serializer
    """

    def wrapper(cls):
        original_init = cls.__init__
        original_new = cls.__new__

        def __init__(self, *args, **kwargs):
   --- 생략 ---
   
        def __new__(�_cls, *args, **kwargs):
            instance = original_new(_cls, *args, **kwargs)
            if not hasattr(instance, 'dto'):
               dto = ~~
                setattr(instance, 'dto', dto)
            return instance
        cls.__init__ = __init__
        cls.__new__ = __new__
        return cls
    return wrapper
@happysmileboy happysmileboy changed the title 해당 부분 drf와 관련하여 문제가 있어요! service_layer 부분 drf와 관련하여 문제가 있어요! Jan 16, 2022
@qu3vipon
Copy link
Owner

qu3vipon commented Jan 20, 2022

@happysmileboy 안녕하세요. 먼저 새로운 내용을 알게 해주셔서 감사합니다 😄.
Many=True인 경우 별도의 메소드로 instantiate 되는지 모르고 있었네요.

다만 __new__를 오버라이드 하지 않고 새롭게 List API를 검증하는 테스트가 추가해보았는데 정상적으로 동작되고 있는걸로 확인됩니다.
(왜 문제없이 돌아가는지는 아직 파악하지 못했네요😢 .)

혹시 Many=True에서 에러를 발생하는 경우에 대해서 직접 확인해보신 테스트가 있다면 알려주실 수 있을까요? 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants