인프런 커뮤니티 질문&답변

김제하님의 프로필 이미지

작성한 질문수

파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트

two form on a page 관련 질문

해결된 질문

작성

·

366

·

수정됨

0

안녕하세요!

한 페이지당 2개의 form을 사용해서 페이지를 만들려고 합니다.

2개의 form을 만든 사례가 구글링을 해도 많이 보이지 않아서 직접 View class를 상속받아서만 만들어봤습니다.

그래서 view를 다음과 같이 만들었지만 에러가 발생되어 구글링을 하거나 print문으로 찍어봐도 어디서 해결할지를 몰라서 여쭤봅니다.

Traceback (most recent call last):

File "/Users/jehakim/.pyenv/versions/assignment/lib/python3.10/site-packages/django/core/handlers/exception.py", line 55, in inner

response = get_response(request)

File "/Users/jehakim/.pyenv/versions/assignment/lib/python3.10/site-packages/django/utils/deprecation.py", line 136, in call

response = self.process_response(request, response)

File "/Users/jehakim/.pyenv/versions/assignment/lib/python3.10/site-packages/django/middleware/clickjacking.py", line 34, in process_response

response.headers["X-Frame-Options"] = self.get_xframe_options_value(

AttributeError: 'dict' object has no attribute 'headers'

위 에러는 django middleware 단에서 발생했습니다.

views.py

한 화면에 버튼이 2개가 있고, 이 버튼에 따라 전송되는 form이 다른 상황입니다. 이런 상황일 때 버튼을 구분하는 방법을 구글링해보니 button tag에 name 속성으로 구분하면 된다고 하여

"refusal-btn" 과 "approval-btn" 으로 구분했습니다.

FBV로 먼저 만든 후 코드가 너무 지저분해 보여서 CBV로 바꿨습니다.

views.py

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import FormView, ListView, View
from django.views.generic.base import ContextMixin
from django.contrib.auth import login, logout
from django.urls import reverse_lazy
from django.utils import timezone
from django.db.models import Q

from config.settings.base import COUNTS_PER_PAGE
from accounts.utils import (
    authorization_filter_on_employee_list,
    authorization_filter_on_signup_list,
)
from accounts.forms import (
    ResignationForm,
    EmployeeForm,
    SignUpForm,
    LoginForm,
    UserForm,
)
from accounts.models import User, Employee


@method_decorator(login_required(login_url=reverse_lazy("login")), name="get")
class SignupUserDetailView(View, ContextMixin):
    user_object = None
    user_form = None
    employee_form = None
    context = dict()

    def post(self, request, **kwargs):
        self.user_form = UserForm(request.POST, instance=self.user_object)

        if self.user_form.is_valid():
            state = self.user_form.cleaned_data.get("state")
            self.user_object.state = state

            if "refusal-btn" in request.POST:
                reason_for_refusal = self.user_form.cleaned_data.get(
                    "reason_for_refusal"
                )
                rejected_at = timezone.now()

                self.user_object.reason_for_refusal = reason_for_refusal
                self.user_object.rejected_at = rejected_at

                self.user_object.save()
            else: # approval-btn 일 때 
                self.employee_form = EmployeeForm(request.POST)
                if self.employee_form.is_valid():
                    cleaned_data = self.employee_form.cleaned_data
                    grade = self.employee_form.data.get("grade")
                    authorizations = {
                        "authorization_grade": grade,
                        "signup_approval_authorization": cleaned_data.get(
                            "signup_approval_authorization"
                        ),
                        "list_read_authorization": cleaned_data.get(
                            "list_read_authorization"
                        ),
                        "update_authorization": cleaned_data.get(
                            "update_authorization"
                        ),
                        "resign_authorization": cleaned_data.get(
                            "resign_authorization"
                        ),
                    }
                    employee = Employee.objects.create(
                        user_id=self.user_object.id, **authorizations
                    )

                    employee.save()
                    self.user_object.save()
            return render(request, "detail.html", self.get_context_data())
        return render(request, "detail.html", self.get_context_data())

    def get(self, request, user_id):
        self.user_object = get_object_or_404(User, pk=user_id)
        self.user_form = UserForm(instance=self.user_object)
        self.employee_form = EmployeeForm()
        return render(request, "detail.html", self.get_context_data())

    def get_context_data(self, **kwargs):
        self.context["user_form"] = self.user_form
        self.context["employee_form"] = self.employee_form
        self.context["signup_list"] = True
        return self.context

제일 하단에 있는 signup_list 는 하나의 템플릿 .html을 사용하는데 두 개의 view에서 이를 공유하기 때문에 이를 구분하고자 만든 flag 변수입니다.

제가 알기로는 render 함수는 HttpResponse를 반환하는데 그러면 header가 당연히 있지 않나? 라는 생각이라서 부딪혔습니다.

제 생각에는 기존에 다른 View 종류들을 상속받으면 해결해주는 거를 순수히 View만 상속받아서 어느 부분을 놓친 것 같은데, 이러면 어느 클래스를 상속받으면 될지, 아니면 직접 뭔가를 해야할지 모르겠습니다.

읽어주셔서 감사합니다.

advice 해주시면 감사하겠습니다 ㅠㅠ

답변 1

0

이진석님의 프로필 이미지
이진석
지식공유자

안녕하세요.

아래와 같은 오류가 발생하는 데요.

File "/Users/jehakim/.pyenv/versions/assignment/lib/python3.10/site-packages/django/middleware/clickjacking.py", line 34, in process_response

response.headers["X-Frame-Options"] = self.get_xframe_options_value(

AttributeError: 'dict' object has no attribute 'headers'

dict 객체에는 headers 속성이 없다고 합니다. dict은 사전으로서 headers 속성이 없습니다.

미들웨어에서는 응답객체는 HttpResponse 타입임을 가정하고 동작하거든요.
response는 HttpResponse 객체여야 하는 데, dict 객체라서 발생하는 오류입니다.

HttpResponse 객체가 반환될 것이라고 생각하지만, 이 코드 혹은 다른 곳에서 사전 객체가 반환되고 있을 수 있습니다.

--

View 코드만 봐서는 정확히 파악은 안 되지만. 파이참이나 VSCode에서 디버거(debugger) 물리셔서, django/middleware/clickjacking.py 라인 34에 브레이크 포인트 거시고, 콜스택과 response 값을 확인해보시면
어느 부분에서 사전이 반환되고 있는 지, 파악이 가능하실 것으로 보여집니다.

--

참고로 context = dict()는 클래스 변수입니다. 인스턴스 변수 초기화 코드가 아닙니다.
get_context_data 메서드 내에서 새로운 사전 객체를 만들어서 반환하시는 것으로 충분합니다.

화이팅입니다.