DevYoon

[Django] Form 본문

Web/Django

[Django] Form

gimewn 2022. 4. 7. 02:06

Django Form

1️⃣ Django의 Form을 사용하는 이유 ➡️ 유효성 검증

  • 유효성 검증 : 사용자가 입력한 데이터를 검증하는 것
  • HTML의 form, input 태그를 사용해서 직접 사용자로부터 데이터를 받으면 유효성 검증을 코드로 모두 구현해야 함
  • Django의 Form은 이러한 작업과 반복 코드를 줄여줌
  • 외부의 악의적 공격 및 데이터 손상에 대한 중요한 방어 수단

2️⃣ Django가 처리해주는 form에 관련된 작업 3가지

  • 렌더링을 위한 데이터 준비 및 재구성
  • 데이터에 대한 HTML Forms 생성
  • 클라이언트로부터 받은 데이터 수신 및 처리

3️⃣ Django Forms

3️⃣-1️⃣ forms 선언하기

  1. 앱 안에 forms.py 파일 생성

  2. from django import forms 
    class ArticleForm(forms.Form): 
    title = forms.CharField(max_length=10) 
    content = forms.CharField()
  3. Model 선언과 유사, 같은 필드타입(CharField) 사용

  4. forms 라이브러리에서 파생된 Form 클래스 상속받음

3️⃣-2️⃣ forms 사용하기

  1. views.py 수정

  2. from .forms import ArticleForm 
    def new(request): 
    form = ArticleForm() 
    context = { 'form': form, } 
    return render(request, 'articles/new.html', context)
  3. template 수정

    {{ form.as_p }}
  4. ➡️ form 안의 title, content 등의 내용을 {{ form }}으로 대체할 수 있음

*️⃣ Form rendering options

➡️ label & input 태그 쌍에 대한 출력 옵션

  1. as_p() : 각 필드가 단락(p 태그)로 감싸져서 렌더링 됨
  2. as_ul() : 각 필드가 목록 항목(li 태그)로 감싸져서 렌더링 되며, ul 태그는 직접 작성해야 함
  3. as_table() : 각 필드가 테이블(tr 태그)로 감싸져서 렌더링 되며, table 태그는 직접 작성해야 함

*️⃣ Django의 HTML input 요소 표현 방법

  1. Form fields

    • input에 대한 유효성 검사 로직을 처리하여 템플릿에서 직접 사용됨
  2. Widgets

    • 웹 페이지의 HTML input 요소 렌더링

    • GET/POST 딕셔너리에서 데이터 추출

    • 위젯은 반드시 Form fields에서 할당 됨

    • 💥주의

      • Form Fields와 혼동 X
      • Form Fields는 input 유효성 검사를 처리
      • 위젯은 웹페이지에서 input element의 단순 raw한 렌더링 처리
      content = forms.CharField(widget=forms.Textarea)
      FRUITS_A = 'ap' # 서버에 넘어가는 것
      FRUITS_B = 'or'
      FRUITS_C = 'gr'
      FRUITS_CHOICES = [
          (FRUITS_A, '사과'), # 사과 - 사용자에게 보여주는 것
          (FRUITS_B, '오렌지'),
          (FRUITS_C, '포도'),
      ]
      content = forms.ChoiceField(choices=FRUITS_CHOICES, widget=forms.Select())

4️⃣ ModelForm

  • Model에 정의한 필드를 유저로부터 입력받기 위해 Form에서 Model필드를 재정의하는 행위가 중복될 수 있음
  • Model을 통해 Form Class를 만들 수 있는 ModelForm이라는 일종의 도우미를 제공
  • 일반 Form Class와 같은 방식(객체 생성)으로 view에서 사용 가능

4️⃣-1️⃣ ModelForm 선언하기

  1. forms.py 안에 코드 작성

  2. from .models import Article 
    class ArticleForm(forms.ModelForm): 
    class Meta: 
    model = Article 
    fields = '__all__' 
    exclude = ('title',) # 출력에서 제외할 요소 지정
  3. forms 라이브러리에서 파생된 ModelForm 클래스를 상속받음

  4. 정의한 클래스 안에 Meta 클래스 선언 후 어떤 모델을 기반으로 Form을 작성할 것인지 지정해줌

  5. 💥 주의 : fields와 exclude는 동시에 쓰일 수 없음!

*️⃣ Meta Class

  • Model의 정보를 작성하는 곳
  • ModelForm 사용 시 사용할 모델이 있어야 하는데, 이를 Meta Class가 구성
  • field 정보를 Form에 적용

[참고] Inner Class (Nested Class)

  • 클래스 내에 선언된 다른 클래스
  • 관련 클래스를 함께 그룹화하여 가독성 및 프로그램 유지 관리를 지원
  • 외부에서 내부 클래스에 접근할 수 없으므로 코드의 복잡성을 줄일 수 있음

4️⃣-2️⃣ view 수정

def create(request):
    form = ArticleForm(request.POST)
    if form.is_valid():
        article = form.save()
        return redirect('articles:detail', article.pk)
    return redirect('articles:new')

*️⃣ is_valid() method

  • 유효성 검사를 실행하고, 데이터가 유효한지 Boolean으로 반환

*️⃣ save() method

  • Form에 바인딩 된 데이터에서 데이터베이스 객체를 만들고 저장
  • ModelForm의 하위 클래스는 기존 모델의 인스턴스를 키워드 인자 instance 로 받아들일 수 있음
    • instance가 제공되면 save()는 해당 인스턴스를 수정
    • instance가 제공되지 않으면 save()는 지정된 모델의 새 인스턴스를 생성
  • Form의 유효성이 확인되지 않은 경우, save()를 호출해 form.errors를 통해 에러 확인 가능

4️⃣-3️⃣ view 수정2

# 같은 주소로 왔을 때
def create(request):
    if request.method == 'POST': # GET인지 POST인지 확인
        # create
        form = ArticleForm(request.POST)
        # 유효성 검사
        if form.is_valid():
            article = form.save()
            return redirect('articles:detail', article.pk)
    else:
        # new
        form = ArticleForm()
    context = {
        'form': form,
    }
    return render(request, 'articles/create.html', context)

4️⃣-4️⃣ 위젯 활용

  • 방법 1

    class ArticleForm(forms.ModelForm): 
        class Meta: 
            model = Article 
            fields = '__all__' 
            widgets = { 'title' : forms.TextInput(attrs={ 'class':'my-title', 'placeholder': '입력하세요', 'maxlength': 10,}, ) }
  • 방법 2 (권장)

    class ArticleForm(forms.ModelForm): 
        title = forms.CharField( label='title', widget=forms.TextInput(
                attrs={
                        'class':'my-title form-control',  
                        'placeholder':'Enter the title',} ), )
  • 💥 Form Class는 어느 위치에 두어도 상관 없지만, 되도록 앱/forms.py에 작성하는 것이 일반적

5️⃣ Form과 ModelForm 비교

Form

  • 어떤 Model에 저장해야 하는지 알 수 없으므로 유효성 검사 이후 cleaned_data 딕셔너리 생성
  • cleaned_data 딕셔너리에서 데이터를 가져온 후 .save() 호출
  • Model에 연관되지 않은 데이터를 받을 때 사용

ModelForm

  • Django가 해당 model에서 양식에 필요한 대부분의 정보를 이미 정의
  • 어떤 레코드를 만들어야 할지 알고 있음 ➡️ 바로 .save() 호출 가능

✔️ 사용자의 정보를 입력 받음 ➡️ 저장 ➡️ Form과 DB가 밀접 ➡️ ModelForm

✔️ 사용자의 정보를 입력 받아 별도 처리 필요 ➡️ DB와 연관 x ➡️ Form

6️⃣ 수동으로 Form 작성하기

6️⃣-1️⃣ Rendering fields manually

<form action="{% url 'articles:create' %}" method="POST">
    {% csrf_token %}
    <div>
      {{form.title.errors}}
      {{form.title.label_tag}}
      {{form.title}}
    </div>
    <div>
      {{form.content.errors}}
      {{form.content.label_tag}}
      {{form.content}}
    </div>
    <br>
    <input type="submit" value='CREATE'>
  </form>

6️⃣-2️⃣ Looping over the form's fields

<form action="{% url 'articles:create' %}" method="POST">
    {% csrf_token %}
    {% for field in form %}
        {{ field.errors }}
        {{ field.label_tag }}
        {{ field }}
       {% endfor %}
    <input type="submit" value='CREATE'>
  </form>

6️⃣-3️⃣ Bootstrap 사용

  1. Bootstrap Form의 핵심 class ➡️ forms.py의 widget에 작성

  2. 에러 메시지를 Bootstrap alert 컴포넌트

  3. {% for field in form %} 
        {% if field.errors %} 
            {% for error in field.errors %} 
                <div class="alert alert-warning" role="alert"> {{ error|escape }} </div>         {% endfor %}  
        {% endif %} 
        <div class="form-group"> {{ field.label_tag }} {{ field }} </div> 
    {% endfor %}

6️⃣-4️⃣ Django-bootstrap v5

  • form class에 bootstrap을 적용시켜주는 라이브러리

  • 설치

    $ pip install django-bootstrap-v5

  • 등록

    • settings.py ➡️ INSTALLED_APPS ➡️ 'bootstrap5'
  • 적용

    {% load bootstrap5 %} 
    {% bootstrap_css %} 
    {% bootstrap_javascript %} 
    {% bootstrap_form form layout='horizontal'%}