brunch

매거진 백엔드

You can make anything
by writing

C.S.Lewis

by 내가 사는 세상 Jan 13. 2024

Django - Form

깔끔한 서류양식, 유효성 검사

목차

0. 시작하며

1. 기본형태

    1.1. Form

    1.2. ModelForm

2. 기본기능

    2.1. 유효성 검사

3. Form 사용할 때, 중간에 데이터 추가하고 DB에 저장하기




0. 시작하며


 고객은 웹사이트를 돌아다니다가, 자신의 데이터를 서버에 전송시킬 때가 있다. '아이디, 비밀번호를 전송해서 로그인하기', '상품 종류, 수량, 카드번호를 전송하여 결제 요청하기' 등 그 종류는 다양하다. 데이터를 적고 서버에 전달하는 것은 '포스트잍에 장볼거리를 적어 심부름시키기', '결제 서류를 적어 상사한테 보고하기'와 비슷하다. 이때 전자보단 후자가 좋다. 전달할 정보가 명확하고 그 방식이 더 깔끔하기 때문이다.


 웹도 마찬가지다. 데이터를 서버로 아무때나 하나씩 툭툭 던지는 것은 별로다. 한장의 깔끔한 양식에 데이터를 정리해서 전송하는게 좋다. 그러면 서버에서 데이터를 처리하기도 수월하고, 보안적으로도 관리하기 쉬울 것이다. 이 때 사용되는 양식을 Form이라고 부른다.




1. 기본형태


1.1. Form


# forms.py

from django import forms


class MyForm(forms.Form):

    name = forms.CharField(label='이름', max_length=100)

    email = forms.EmailField(label='이메일')

    message = forms.CharField(label='메시지', widget=forms.Textarea)




# views.py

from django.shortcuts import render

from .forms import MyForm


def my_view(request):

    if request.method == 'POST':

        form = MyForm(request.POST)

        if form.is_valid():

            name = form.cleaned_data['name']

            email = form.cleaned_data['email']

            message = form.cleaned_data['message']

            print(f'이름: {name}, 이메일: {email}, 메시지: {message}')

    else:

        form = MyForm()

    return render(request, 'my_template.html', {'form': form})




<!-- my_template.html -->

<form method="post" action="{% url 'my_view' %}" enctype='multipart/form-data'>

  {% csrf_token %}

  {{ form.as_p }}

  <button type="submit">제출</button>

</form>


cf) post를 사용해 이미지도 넘겨줘야 한다면 enctype='multipart/form-data' 추가(여기선 필요 X)



1.2. ModelForm


 고객한테 작성하라고 넘겨줄 Form(서류 양식)에 들어가는 내용은 DB의 필드(Model의 멤버변수)와 비슷할 때가 많다. 일반적으로 서버는 DB에 저장할 필요가 있는 정보만을 요구하기 때문이다. 회원가입을 생각해보자. DB에는 아이디, 비밀번호 필드 2개만 필요하다. 고객의 몸무게나 오른손의 손톱 길이 같은 쌩뚱맞는 정보는 필요하지 않다. DB에서 필요한 정보(Model의 멤버변수)만을 Form(서류양식)에서 필요로 한다면, 이 둘을 연동시켜서 개발자의 짐을 덜어줄 필요가 있다. 그렇게 탄생하게 ModelForm이다.


# forms.py

from django import forms

from .models import MyModel


class MyModelForm(forms.ModelForm):

    class Meta:

        model = MyModel

        fields = '__all__'




# views.py

from django.shortcuts import render, redirect

from .forms import MyModelForm


def create_my_model(request):

    if request.method == 'POST':

        form = MyModelForm(request.POST, request.FILES)

        if form.is_valid():

            instance= form.save()       # form에 입력된 정보를 통해 DB에 해당 정보 저장(레코드 추가)

            return redirect('/')  # 성공 페이지로 리다이렉트

    else:

        form = MyModelForm()


    # 폼이 유효하지 않은 경우 또는 GET 요청인 경우 해당 페이지를 렌더링

    return render(request, 'myapp/create_my_model.html', {'form': form})




<!-- create_my_model.html -->

<form method="post" action="{% url 'create_my_model' %}">

  {% csrf_token %}

  {{ form.as_p }}

  <button type="submit">저장</button>

</form>




2. 기본기능


2.1. 유효성 검사


#forms.py

class PostForm(forms.ModelForm):

    class Meta:

        model = Post

        fields = ['title', 'content']


    def clean_title(self):

        title = self.cleaned_data.get('title')

        if len(title) < 3:

            raise forms.ValidationError("제목은 최소 3글자 이상이어야 합니다.")

        return title


    def clean(self):

            cleaned_data = super().clean()

            content = cleaned_data.get('content')

            if len(content) < 10:

                raise forms.ValidationError("내용은 최소 10글자 이상이어야 합니다.")


지정한 필드들에 대해서만 유효성 검사 진행

: model의 모든 필드를 검사하는 것이 아닌, 화이트리스트로 forms.py에 지정한 필드만 검사

지정한 필드들 전체에 관련된 유효성 검사는 clean()

지정한 필드들 중 하나를 꼭 찝어서 유효성 검사는 clean_필드명()




# views.py

def create_post(request):

    if request.method == 'POST':

        form = PostForm(request.POST)

        if form.is_valid():

            title = form.cleaned_data['title']

            content = form.cleaned_data['content']

            # 여기에서 검증된 데이터를 활용하여 원하는 작업 수행 가능

            # 예를 들어, 데이터베이스에 저장하거나 다른 비즈니스 로직 수행

            form.save()

            return redirect('post_list')  # 적절한 URL로 리디렉션

    else:

        form = PostForm()

    return render(request, 'create_post.html', {'form': form})


forms.is_valid()로 유효성 검사 진행

유효성 검사를 통과한 값들은 .cleaned_data에 딕셔너리 형태로 담김



cf) Django DRF와의 비교 https://brunch.co.kr/@i-intheworld/631

Django의        Form과 관련된       clean은

Django DRF의 Serializer와 관련된 validate와 비슷




3. Form 사용할 때, 중간에 데이터 추가하고 DB에 저장하기


 최종적으로 DB에 데이터가 저장되려면 instance.save()가 호출되어야 한다. 이 과정은 보통 form.save()를 하면서 이루어진다. 이와 관련된 Django 내부 코드를 살펴보자. 기본적으로는 commit=True로 설정해서 DB에 instace를 저장하는 것까지 한번에 실행하는 것을 볼 수 있다. 즉 form.save()에서 commit 옵션을 설정하지 않으면 DB에 바로 데이터가 저장되는 것이다.


https://github.com/django/django/blob/main/django/forms/models.py#L535

def save(self, commit=True):
    """
    Save this form's self.instance object if commit=True. Otherwise, add
    a save_m2m() method to the form which can be called after the instance
    is saved manually at a later time. Return the model instance.
    """
    if self.errors:
        raise ValueError(
            "The %s could not be %s because the data didn't validate."
            % (
                self.instance._meta.object_name,
                "created" if self.instance._state.adding else "changed",
            )
        )
    if commit:
        # If committing, save the instance and the m2m data immediately.
        self.instance.save()
        self._save_m2m()
    else:
        # If not committing, add a method to the form to allow deferred
        # saving of m2m data.
        self.save_m2m = self._save_m2m
    return self.instance



 하지만 이런 상황이 문제를 일으킬 때가 있다. 어느 유저가 인스타에 포스팅을 올릴 때를 생각해보자. 유저는 사진과 코멘트만을 입력하고 저장 버튼을 누른다. form에서 사진과 코멘트만을 입력받는 것이다. 하지만 모든 포스팅에는 유저 정보도 필요하다. 포스팅을 누가 올린 것인지 구분 가능해야하기 때문이다. 그래서 form.save()를 한 뒤(사진과 코멘트를 저장하여 instance 생성), 곧바로 DB에 저장하면 안된다. 잠깐 멈추어선 뒤, 계정 정보를 instance에 추가하고, DB에 저장해야한다. 이 때 commit=False를 통해 한방에 DB에까지 저장되는 흐름을 정지시킬 수 있다.


from django.shortcuts import render, redirect

from .forms import YourModelForm


def your_view(request):

    if request.method == 'POST':

        form = YourModelForm(request.POST)

        if form.is_valid():

            instance = form.save(commit=False)  #잠깐 정지!

            instance.user = request.user                # 유저 정보 추가!

            instance.save()                                        # 이제야 비로소 DB에 저장!

            return redirect(instance) #redirect 자세한 설명 https://brunch.co.kr/@i-intheworld/683

    else:

        form = YourModelForm()


    return render(request, 'myapp/create_my_model.html', {'form': form})





keyword
매거진의 이전글 Pycharm, Django 디버깅
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari