Backend/Python

[인스타그램 클론] 단일 페이지에 모델 폼 추가하기

비비빅B 2020. 6. 17. 22:30

  • 인스타그램 클론 코딩의 일환으로 회원가입 페이지를 만들던 중
  • 회원가입을 완료하고 로그인하면 오류가 뜸
  • 회원가입한 유저의 프로필 이미지와 닉네임이 없어서 오류 발생

nav와 댓글, 게시물 헤더에 쓰이는 profile_photo


첫번째 시도

Model에서 profile_photo 디폴트 설정

class Profile(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE)	# 장고 유저모델 사용
    nickname = models.CharField(max_length=20)	# 회원가입 페이지에서 값을 받을 것임
    profile_photo = models.ImageField(
        upload_to="profile/", blank=True, default="default/person.png")	# 기본으로 익명 그림 받음
    slug = models.SlugField(blank=True, null=True)	# save함수 오버라이드로 소문자 nickname를 자동으로 설정

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.nickname)
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        return resolve_url("accounts:profile", self.slug)

 

  • Profile 폼이 save되야 값이 비더라도 들어가는 것이었음
  • 따라서 회원가입 페이지는 User 폼밖에 없기 때문에 Profile은 여전히 빈 데이터 상태로 오류뜸

두번째 시도

View에서 데이터 가공


class UserCreate(CreateView):
    template_name = "registration/signup.html"
    form_class = CreateUserForm
    success_url = reverse_lazy('accounts:create_user_done')
    
    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        if self.request.POST:
            ctx['user_form'] = CreateUserForm(self.request.POST)
            ctx['profile_form'] = ProfileForm(self.request.POST)
        else:
            ctx['user_form'] = CreateUserForm()
            ctx['profile_form'] = ProfileForm()

        return ctx
  • 모델은 User로 유지하고, get_context_data 오버라이드 함
  • POST와 GET 분리해서 폼 2개 데이터로 받음
    def form_valid(self, form):
        ctx = self.get_context_data()
        profile_form = ctx["profile_form"]

        if profile_form.is_valid() and form.is_valid():	# form은 CreateView에서 form_class로 지정한 폼
            profile = profile_form.save(commit=False)
            user = form.save(commit=False)
            profile.user = user		# 1:1관계 연결

            form.save()
            profile_form.save()

            return redirect(self.success_url)	# 설정한 success_url로 리다이렉트
  • DB에 저장하기 전에 commit 잠시 멈추고
  • 같은 페이지에 있지만 별개의 폼이므로 장고는 누구 Profile 데이턴지 모름
  • 1:1관계인 User 객체와 연결 후 DB에 저장

HTML

<form method="post" class="signup_form" enctype="multipart/form-data">
	{% csrf_token %}
    <!-- User.email input -->
	{% render_field user_form.email class="signup_email" placeholder="이메일 주소" %}
    
	<!-- User.Profile.nickname input -->
	{% render_field profile_form.nickname class="signup_nickname" placeholder="닉네임" %}
    
	<!-- User.username input -->
	{% render_field user_form.username class="signup_username" placeholder="아이디" %}
    
	<!-- User.password input -->
	{% render_field user_form.password1 class="signup_pw" id="signup_pw1" placeholder="비밀번호" %}
	{% render_field user_form.password2 class="signup_pw" id="signup_pw2" placeholder="비밀번호 확인" %}

	<button type="submit" class="signup_btn">가입</button>
</form>
  • Profile 모델의 닉네임만 받아서 저장
  • PrifileForm이 저장되므로 '첫번째 시도'에서 디폴트로 설정한 기본 이미지가 자동으로 설정될 것임

  • 가입 후 리스트페이지도 잘 나오고 nickname과 profile_photo도 잘 들어가 있음

결론

  • get_context_data로 GET POST 데이터를 받고 보냄
  • form_valid로 DB 저장 전 폼 데이터 수정 가능

 

https://stackoverflow.com/questions/12573992/multiple-forms-and-formsets-in-createview

 

Multiple Forms and Formsets in CreateView

I have 2 models, Father and Son. I have a page to register Father. On the same page I have a formset to register Son. On page has a button "more" to add another Father and their respective Son on...

stackoverflow.com