erp 도전

dispatch 메서드: self.get_object() , self.object

나도초딩 2023. 4. 27.

이 키워드로 검색하는 사람들은 장고 클래스뷰 하다가, 주화입마에 빠져들고 있는 사람일 것이다.

 

중간 결론

CreateView, UpdateView, FormView 등에서 모델 폼을 사용할 때, 

  • get 인 경우, get_context_data() 오버라이딩
  • post 면, form_valid()
  • 공통인 경우, dispatch() 오버

https://github.com/django/django/blob/main/django/views/generic/edit.py

 

GitHub - django/django: The Web framework for perfectionists with deadlines.

The Web framework for perfectionists with deadlines. - GitHub - django/django: The Web framework for perfectionists with deadlines.

github.com

장고 기초 문법만 하면, html 도 잘 몰라도 할 수 있다는 걸 강조한 책.. ㅠㅠ Do it 장고 예제 중,

포스트 업데이트를 하는 클래스 뷰인데,

게시글, 소위 object 인데,

get_context_data 에서는 self.object

dispatch() 에서는 self.get_object()

form_valid() 에서는 self.object 로 사용하고 있다. 아씽. 아호.... ㅠㅠ 띵 받아.

 

class PostUpdate(LoginRequiredMixin, UpdateView):
    model = Post
    fields = ['title', 'hook_text', 'content', 'head_image', 'file_upload', 'category']

    template_name = 'blog/post_update_form.html'

	# GET 일 때,
    def get_context_data(self, **kwargs):
        context = super(PostUpdate, self).get_context_data()
        if self.object.tags.exists():
          .....
          
	# url로부터 요청 받을 때, 공통
    
    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated and request.user == self.get_object().author:
         .....
    # POST 일 때, 
    def form_valid(self, form):
        response = super(PostUpdate, self).form_valid(form)
        self.object.tags.clear()
		......

장고 소스를 보면, self.object = self.get_object() 가 있는데,

이 말은 self.get_object() 가 먼저 실행되야 한다는 것이잔아.

 

  1. 모든 클래스뷰에서는 url 을 입력받으면, 클래스.as_view() 메서드로 해당 클래스의 dispatch() 를 호출한다.
  2. dispatch 는 http 메소드를 확인 후, def get(), def post() 등으로 분기해준다.
  3. 분기
    1. GET : dispatch() -> get -> get_context_data() -> get_initial()
    2. POST :dispatch() -> post -> get_initial() -> form_valid() -> get_success_url()

 

그래서, 유저가 작성자와 동일한지를 먼저 검사하기 위해, dispatch() 에 오버라이딩한 것이고,

get_object().author 를 한 이유는 .....?

dispatch() 단계에서는 self.object = self.get_object() 메서드가 실행되지 않았기 때문일거다라는 합리적인 추측.... 조금 더 찾아봤는데, 일단 확실해보인다.

 

어 그러네.... dispatch 이후, get, post 에서 수행되는거였어.

왜 내가 본 책에는 이런 설명이 없었을까... 아후.

게다가 대부분 답변들도 그냥 dispatch 소스를 보면 알 수 있다 정도로 얘기하는데, 

핵심은 클래스별, 그리고, 각 수행 조건별로 메소드 실행 단계가 다르다는 거였잔아.

 

아흐. 빡친당

장고는 쉬운게 절대 ... 네버 네버 아니다. 특히, 클래스뷰.

근데, 함수형만 쓴다 가정하면, 다른 언어나 프레임웍 대비해서 장고의 장점이 뭐냐?

순간 또 빡치는 넋두리. ㅠ

class BaseDetailView(SingleObjectMixin, View):
    """A base view for displaying a single object."""
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        context = self.get_context_data(object=self.object)
        return self.render_to_response(context)
class BaseUpdateView(ModelFormMixin, ProcessFormView):
    """
    Base view for updating an existing object.

    Using this base class requires subclassing to provide a response mixin.
    """
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super().get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super().post(request, *args, **kwargs)

모델 의 주요 메소드 호출 순서

GET POST
dispatch()
get_context_data()
get_initial()
form_valid()
get_success_url()
dispatch()
get_context_data()
get_initial()
form_valid()
get_success_url()

** dispatch() 가 공통적으로 호출되기 때문에, dispatch() 를 오버라이딩 한다.

# url로부터 요청 받을 때, 공통
    
    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated and request.user == self.get_object().author:

 

GET 메소드 요청 시 주요 메소드의 호출 순서이다.

  1. dispatch()
  2. get_context_data()
  3. get_initial()

POST 메소드 요청 시 주요 메소드의 호출 순서이다.

  1. dispatch()
  2. get_initial()
  3. form_valid()
  4. get_success_url()

여기서 중요한 것은 

POST 요청 시에는 get_context_data() 메소드가 호출되지 않는다

GET 요청 시에는 폼 데이터를 전송 받지 못했으므로 form_valid() 메소드는 호출되지 않는다.

GET, POST 요청에서 공통된 작업을 하고자 할 경우 get_initial() 메소드를 이용할 수 있다.

다만 이 메소드는 FormMixin의 메소드이기 때문에 CreateView, UpdateView, FormView 에서만 사용가능

앱 전체 뷰에서 공통으로 호출되길 원한다면 dispatch() 메소드를 오버라이딩해야 한다

 

def dispatch(self, request, *args, **kwargs):
        object = self.get_object()
        if object.author != request.user:
            messages.warning(request, '수정할 권한이 없습니다.')
            return HttpResponseRedirect('/')
            # 삭제 페이지에서 권한이 없다! 라고 띄우거나
            # detail페이지로 들어가서 삭제에 실패했습니다. 라고 띄우거나
        else:
            return super(CommentUpdate, self).dispatch(request, *args, **kwargs)

 

    def dispatch(self, request, *args, **kwargs):
        object = self.get_object()
        if object.author != request.user:
            messages.warning(request, '삭제할 권한이 없습니다.')
            return HttpResponseRedirect('/')
        else:
            return super(CommentDelete, self).dispatch(request, *args, **kwargs)

 

def dispatch(self, request, *args, **kwargs):
    object = self.get_object()
    if request.method == "POST":
        super().post(request, *args, **kwargs)
    else:   
        super().post(request, *args, **kwargs)

get_initail() 예시가 많아 보이진 않는데...CreateView를 상속해서 사용시, 폼에 디폴트값을 입력할 때 사용할 수 있다.

 

def get_initial(self):
    initial = super().get_initial()
    if 'category' in self.kwargs:
        category = self.kwargs['category']
        initial['category'] = Category.objects.filter(name=category)[0]
    return initial

댓글