티스토리 뷰

Python/* Django

19. ModelForm

포꾼 2017. 12. 20. 11:09

ModelForm (Form Class를 상속)

- Django Form Base

- 지정된 Model로부터 필드정보를 읽어들여, form fields 를 세팅

변경 전

def min_length_3_validator(value):

    if len(value) < 3:

        raise forms.ValidationError('3글자 이상 입력해주세요.')


class PostForm(forms.Form):

title = forms.CharField()

content = forms.CharField(widget=form.Textarea)


# ModelForm.save 인터페이스를 흉내내어 구현

def save(self, commit=True):

post = Post(**self.cleaned_data)

if commit:

post.save()

return post

변경 후

class PostForm(forms.ModelForm):

    class Meta:

        model = Post

        fields = '__all__' # 전체 필드 지정. 혹은 list로 읽어올 필드명 지정 

- 내부적으로 model instance를 유지

- 유효성 검증에 통과한 값들로, 지정 model instance로의 저장 (save)을 지원 (Create or Update)


문제점 : validator를 설정하지 못함, validator를 모델에 정의하는게 좋다.

# dojo/models.py

def min_length_3_validator(value):

    if len(value) < 3:

        raise forms.ValidationError('3글자 이상 입력해주세요.')


class Post(models.Model):

    title = models.CharField(max_length=100, validators=[min_length_3_validator])


form vs modelform

from django import forms

from .models import Post

class PostForm(forms.Form):

title = forms.CharField()

content = forms.CharField(widget=forms.Textarea)

# 생성되는 Form Field는 PostForm과 거의 동일

class PostModelForm(forms.ModelForm):

class Meta:

model = Post

fields = ['title', 'content'] 

ModelForm.save(commit=True) #src

• Form의 cleaned_data를 Model Instance 생성에 사용하고, 그 Instance를 리턴

• commit=True : model instance의 save() 를 호출

• form.save() != instance.save()

• commit=False

• instance.save() 함수 호출을 지연시키고자할 때 사용


ModelForm.save(commit=True) 

from django import forms

from .models import Post


class PostForm(forms.Form):

    title = forms.CharField()

    content = forms.CharField(widget=forms.Textarea)


    def save(self, commit=True):

        post = Post(**self.cleaned_data)

        if commit:

           post.save()

        return post 



ModelForm을 통해 새 포스팅 저장하기

from django.db import models

from django.urls import reverse


class Post(models.Model):

    title = models.CharField()

    content = models.TextField()

    created_at = models.DateTimeField(auto_now_add=True)

    updated_at = models.DateTimeField(auto_now=True)


    def get_absolute_url(self):

        return reverse('myapp:post_detail', args=[self.id])


# dojo/forms.py

from django import forms

from .models import Post


class PostModelForm(forms.ModelForm):

    class Meta:

        model = Post

        fields = ['title', 'content']


ModelForm을 통해 새 포스팅 저장하기

from django.shortcuts import get_object_or_404, render

from .forms import PostModelForm

from .models import Post


def post_new(request):

    if request.method == 'POST':

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

        if form.is_valid():

            post = form.save()

        return redirect(post)

    else:

        form = PostModelForm()

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


<!-- myapp/templates/myapp/post_form.html -->

<table>

    <form action="" method="post">

        {% csrf_token %}

        {{ form.as_table }}

        <input type="submit" />

    </form>

</table> 


ModelForm.save(commit=False) 예제


from django.db import models


class Comment(models.Model):

    author = models.CharField(max_length=20)

    message = models.TextField()

    ip = models.CharField(max_length=15) # 필드 추가


# dojo/forms.py

class CommentForm(forms.ModelForm):

    class Meta:

    model = Comment

    fields = ['author', 'message'] # ip필드는 유저로부터 입력받지 않고, 프로그램으로 채워넣을 것이기에 제외

# dojo/views.py

def comment_new(request):

    if request.method == 'POST':

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

        if form.is_valid():

            comment = form.save(commit=False)

            comment.ip = request.META['REMOTE_ADDR'] # IP를 기록하고

            comment.save() # save()

       return redirect('/')

    else:

        form = CommentForm()

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

템플릿은 다른 Form 템플릿과 동일


Form에서는 유저에게 입력받을 필드만 기재를 하여야함, 나머지는 views에서 처리


기존

def post_new(request):
if request.method == 'POST':
# POST 요청일 때
form = PostForm(request.POST, request.FILES)
if form.is_valid(): # validator check
post = form.save()
post.ip = request.META['REMOTE_ADDR']
post.save()

return redirect('/dojo/') 

> save가 이중으로 진행되기때문에, commit=False를 통해 저장하지않고, 별로 save함수를 호출하여 저장한다.

if form.is_valid(): # validator check
post = form.save(commit=False)
post.ip = request.META['REMOTE_ADDR']
post.


# post_new 함수 총 코드

def post_new(request):
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
post = form.save()
return redirect(post) # get_absolute.url() => post detail
else:
form = PostForm()
return render(request, 'blog/post_form.html',{'form':form,})


# url 설정 blog/urls.py

url(r'^new/$', views.post_new, name='post_new'),


# html 글목록 페이지에 글쓰기 기능 추가 blog/templates/blog/post_list.html

<a href="{% url "blog:post_new" %}" class='btn btn-primary pull-right'>New Post</a>


# 수정 가능하도록 blog앱까지 등록

def post_edit(reqeust, id):
post = get_object_or_404(Post, id=id)
if reqeust.method == 'POST':
form = PostForm(reqeust.POST, reqeust.FILES, instance=post)
if form.is_valid():
post = form.save()
return redirect(post)
else:
form = PostForm(instance=post)
return render(reqeust, 'blog/post_form.html',{'form':form,})


<a href='{% url 'blog:post_edit' post.id %}' class='btn btn-default'>수정</a>


[당부의 말] Form을 끝까지 작성

'''

request.POST 데이터가 form clean함수를 통해 변경될 수도 있다..

'''

class CommentForm(forms.Form):

    def clean_message(self):

        return self.cleaned_data.get('message', '').strip() # 좌우 공백제거

# ---

form = CommentForm(request.POST) 

if form.is_valid():

    # request.POST : 폼 인스턴스 초기 데이터

    message = request.POST['message'] # request.POST를 통한 접근 : BAD !!!

    comment = Comment(message=message)

    comment.save()

    return redirect(post)


[적극 추천]

form = CommentForm(request.POST) 

if form.is_valid():

    # form.cleaned_data : 폼 인스턴스 내에서 clean함수를 통해 변환되었을 수도 있을 데이터

    message = form.cleaned_data['message'] # form.cleaned_data를 통한 접근 : GOOD !!!

    comment = Comment(message=message)

    comment.save()

    return redirect(post)



ModelForm을 활용한 Model Instance 수정 뷰

'''

ModelForm을 활용한 post_new뷰와 비교하여,

ModelForm 인스턴스 생성 시에 instance 인자로서 Model Instance를 지정해주는 차이 뿐.

'''

# myapp/views.py

def post_edit(request, id):

    post = get_object_or_404(Post, id=id)  # 모델 인스턴스 획득

    if request.method == 'POST':

        form = PostModelForm(request.POST, request.FILES, instance=post)

        if form.is_valid():

            post = form.save()

            return redirect(post)

    else:

        form = PostModelForm(instance=post)

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




"""본 내용은 AskDjango VOD, "장고 차근차근 시작하기" 강의내용을 참고하여 작성했습니다.(https://nomade.kr/)"""



반응형

'Python > * Django' 카테고리의 다른 글

21. Messages Framework  (0) 2017.12.28
20. Form Template Custom Render  (0) 2017.12.27
18. Form  (0) 2017.12.19
17. HttpRequest and HttpResponse  (0) 2017.12.18
16. CSRF(Cross-Site Request Forgery)  (0) 2017.12.15
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함