계기

(Django를 사용한지 얼마되지 않았지만) 지금까지 템플릿에 Form을 만들어서 서버에서 데이터를 받아서 DB에 저장하는 식으로 해왔다

하지만 파일 업로드에 알아보는 중 이러한 문구를 읽게 되었다

클라이언트가 이미지라고 말한다고 해서 파일을 서버에 허용하는 것은, 아무것도 훔치지 않겠다고 말한 낯선 사람에게 집 열쇠를 맡기는 것과 같다

😨😰😱

충격을 받고 이것저것 찾아보니 파일 업로드에서 보안이 취약할 수 있다는 것을 알게되었다. 어찌보면 당연한 일이다... 난 프론트엔드만 믿었는데... 프론트는 언제나 해킹 당할 수 있다고 생각해야겠다

Django Form

그래서 장고 서버에서 Form의 형식을 어떻게 제한할까에 대해 찾아보니, 장고에서는 Form 을 설계해서 입력받고 출력할 수 있다는 것을 알게되었다

기본적인 Form에 대한 방법도 있지만, 어차피 나는 만들어 놓은 Model로 저장할 것이니 Model과 연결하는 방법을 정리하겠다

Form 선언

models.py

from django.db import models

class NameModel(models.Model):
  your_name = forms.CharField(label='Your name', max_length=100)

먼저 App 폴더에 forms.py라는 파일을 만들자

forms.py

from django import forms

from .models import NameModel

class NameForm(forms.ModelForm):
  class Meta:
    model = NameModel
    fields = ['your_name']  # your_name 속성만 사용한다는 뜻이다

아주 간단한 이름을 저장하는 폼을 작성했다

폼안에 입력받을 데이터형식(text, file, integer ...)은 forms.Field 로 선언하면 된다. 필수 인자가 있는 Field 도 있으니 문서를 참조하자 - Field 참조

Form 처리

이제 중요한 과정인 Form 을 입력받고 출력하는 과정이다
(깔끔하게 한 코드로 정리하겠다)

def get_name(request):
    # POST로 폼데이터를 입력받을 때
    if request.method == 'POST':
        form = NameForm(request.POST)
        # 유효성 검사
        if form.is_valid():
            # 유효성 검사 성공
            form.save()  # 저장해야 모델로 저장된다
            return HttpResponseRedirect('/Success/')

    # Get으로 받을 경우, 빈 Form을 템플릿에 출력한다
    else:
        form = NameForm()  # 빈 Form 생성
    return render(request, 'name.html', {'form': form})

먼저 Get으로 입력받으면 빈 Form을 생성해서 render 함수에 넣어 보낸다

template.html

<form action="/your-name/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit">
</form>

이렇게 템플릿에서 form 태그안에 넣어주기만 하면, 작성한 폼 형식대로 <input> 태그가 생긴다

그리고 같은 URL의 POST로 들어오면 데이터를 저장 할 수 있다

Instance for Foreign Key (issue solution)

나의 프로젝트에서 Model에 User라는 속성이 request.user(지금 로그인중인 계정)로 받는 외래키이자 기본키이다

하지만 폼에 이런 계정을 입력받을 수 없어서 일단 폼을 동작시켜보니 기본키가 입력이 안되서 에러가 발생한다

해결하기위에 시간을 투자하며 알아보니 Form에 Model Instance를 인자를 넣을 수 있었다

Instance ?
이미 만들어진 Model Instance를 POST로 들어오는 데이터와 함께 Form에 넣어주면 같이 모델에 저장된다

models.py

class Pet(models.Model):
    def __str__(self):
        return self.pet_name
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    pet_name = models.CharField(max_length=50)

지금 owner 속성이 request.user 를 받게하려고 한다
forms.py

from django import forms
from .models import Pet

class PetForm(models.ModelForm):
  class Meta:
    model = pet
    fields = ['pet_name']

폼의 입력은 pet_name 만 받게 한다

views.py

from .models import Pet
from .forms import PetForm

def registerPet(request):
  if request.method = 'POST':
    pet = Pet.objects.create(owner=request.user)
    form = PetForm(request.POST, instance=pet)
    if form.is_vailed():
      form.save()
      return redirect('/success/')
  else :
    form = PetForm()
    return render(request, 'template.html', {'form':form})

위처럼 owner를 미리 넣어서 pet 인스턴스를 미리 생성해주고, 폼에 같이 넣어준다면 유효성 검사를 통과하게 된다