qna blog using django - 회원가임/로그인폼, post, 글보기

38
6주차: 회원가입/로그인폼, Post, 보기 NOTE 주차별 소스는 https://github.com/studybee/webDevForBeginner 에서 다운로드받 있습니다. git 사용할 아는 분들은 git , 모르겠으면 우측 하단에 있는 Download zip file 버튼을 눌러 다운로드 받아 사용하시기 바랍니다. 1. 회원가입 Form 우리는 지금까지 Djang o 에서 기본적으로 제공하는 User 모델을 사용하고 있습니다. 또한 Djang o 에서 기본적으로 제공하는 UserCreationForm이용해 회원가입 폼을 만들었습니다. 다만 UserCreationForm에는 Username, Password1, Password2입력할 있도록 되어있습니다. 여기에서 우리는 Email 추가해보도록 하겠습니다. 어렵게 만들지 말고 최대한 Djang o 에서 제공 하는 것을 토대로 만드는 것으로 하겠습니다. 그리고 Djang o 에서 이미 만들어 놓은 좋은 기능을 그대로 사용하는것이 좋습니다. 앞으로 forms.py에서 다루게 내용은 Djang o 에서 만들어 놓은 Class 메소드와 속성을 재정 의해서 사용할 것입니다. 이를 Overriding 이라고 합니다. 객체지향의 중요한 특징 하나이지요. 더불어 Overriding Overloading 헷갈리시면 안됩니다. Overloading 이번 스터디에 안나오 인터넷에서 찾아보시길 권해드립니다. 1. forms.py 사용자에게 특정값을 입력받기 위해선 필수적으로 HTMLForm 태그를 사용한다고 했습니다. 사용자가 입력한 값을 검증 등의 작업이 있습니다. 이를 간편하게 해결해줄 있는 것이 바로 Djang o Form이라는 기능입니다. 우리는 Djang o 에서 이미 만들어 제공하는 (User 모델, UserCreationForm) 최대한 이용하도 하겠습니다. 그러기 위해서 우선 Djang o 에서 제공하는 UserCreationForm상속받아야 합니 . 아래와 같이 따라 실행해보겠습니다.

Upload: kwangyoun-jung

Post on 22-Jan-2017

1.442 views

Category:

Internet


3 download

TRANSCRIPT

Page 1: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

6주차: 회원가입/로그인폼, Post, 글보기

NOT E 매 주차별 소스는 https://g ithub.com/studybee/webDevForBeginner 에서 다운로드받을 수 있습니다. g it를 사용할 줄 아는 분들은 git를, 잘 모르겠으면 우측 하단에 있는Download zip file 버튼을 눌러 다운로드 받아 사용하시기 바랍니다.

1. 회원가입 Form

우리는 지금까지 Django에서 기본적으로 제공하는 User 모델을 사용하고 있습니다. 또한 Django에서 기본적으로 제공하는 UserCreationForm을 이용해 회원가입 폼을 만들었습니다. 다만UserCreationForm에는 Username, Password1, Password2만 입력할 수 있도록 되어있습니다.

여기에서 우리는 Email을 추가해보도록 하겠습니다. 어렵게 만들지 말고 최대한 Django에서 제공하는 것을 토대로 만드는 것으로 하겠습니다. 그리고 Django에서 이미 만들어 놓은 좋은 기능을그대로 사용하는것이 좋습니다.

앞으로 f orms.py에서 다루게 될 내용은 Django에서 만들어 놓은 Class의 메소드와 속성을 재정의해서 사용할 것입니다. 이를 Overriding이라고 합니다. 객체지향의 중요한 특징 중 하나이지요.더불어 Overriding과 Overloading을 헷갈리시면 안됩니다. Overloading은 이번 스터디에 안나오니 인터넷에서 한 번 찾아보시길 권해드립니다.

1. forms.py

사용자에게 특정값을 입력받기 위해선 필수적으로 HTML의 Form 태그를 사용한다고 했습니다.이 때 사용자가 입력한 값을 검증 등의 작업이 더 있습니다. 이를 간편하게 해결해줄 수 있는 것이바로 Django의 Form이라는 기능입니다.

우리는 Django에서 이미 만들어 제공하는 것(User 모델, UserCreationForm)을 최대한 이용하도록 하겠습니다. 그러기 위해서 우선 Django에서 제공하는 UserCreationForm을 상속받아야 합니다. 아래와 같이 따라 실행해보겠습니다.

Page 2: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

1) Editor > forms.py 파일 신규 생성

qna 앱 안에 forms.py를 새로 만듭니다. 그리고 앞으로 이 곳에 Form 관련된 내용을 입력하도록 하겠습니다. 그리고 후에 views.py에서 forms.py에서 만든 폼 클래스를 불러와 사용하도록 하겠습니다.

2) Django의 form을 사용하겠다고 선언

Django의 f orm 기능을 사용할 것이므로 당연히 먼저 django 패키지안의 f orms 모듈을 사용하겠다고 말해줘야 합니다. 아래와 같이 작성해봅시다.

from django import forms

3) Django의 UserCreationForm을 사용하겠다고 선언

우리는 회원가입 양식(Form)을 역시 Django에 미리 만들어 놓은 것을 사용하려고 합니다. 사용자가 Username 등을 적을 때 30자까지 제한을 둔다던가 등의 몇 가지 제약조건이 이미 설정되어 있는데 이것을 이용하면 빠르게 만들 수 있겠지요? 그래서 이것을 사용해보고자 합니다. 위에서 작성한 다음 줄에 아래와 같이 작성해 봅시다.

from django.contrib.auth.forms import UserCreationForm

4) Django의 User 모델을 사용하겠다고 선언

우리는 Django에서 제공하는 User 모델을 사용하도록 하겠습니다. 위에서 작성한 다음 줄에 아래와 같이 작성해 봅시다.

from django.contrib.auth.models import User

5) 새로운 폼 클래스 만들기

class SignupForm(UserCreationForm):

위에서 사용할 것들을 다 불러왔습니다. 이제부터는 본격적으로 폼을 만들 차례입니다. 그런데 우리는 UserCreationForm를 상속받고 있지요. UserCreationForm이 Django의 f orm 두 가지 중 하

Page 3: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

나인 ModelForm으로 만들어져 있습니다. 따라서 우리도 ModelForm 형식에 따라 아래와 같이 만들아 보겠습니다.

6) Meta 클래스 설정하기

class SignupForm(UserCreationForm): class Meta: model = User fields = ("username", "email", "password1", "password2",)

ModelForm의 경우는 위와 같이 항상 class Meta를 정의해줘야 합니다. 대문자로 시작하는Meta입니다. 대문자로 시작하지 않을 경우 에러가 나니 유의하시기 바랍니다. SignupForm에 대한 기술서라고 이해하면 편합니다. 이 부분은 Django가 아니라 Python을 더 자세히 공부해야하는 부분입니다. fields 부분에서는 작성한 필드만큼 화면에 보여지게 되어있습니다. 만약 모든필드를 다 보여주고 싶다면 필드명을 적는 대신에 '__all__'라고 작성하면 됩니다.

그 안에 우리가 사용하게 될 모델인 User를 설정해주고, 우리가 사용할 필드를 fields를 통해서설정해줍니다.

7) Email 필드 추가하기

class SignupForm(UserCreationForm): email = forms.EmailField( required=True, widget=forms.EmailInput( attrs={ 'class': 'form-control', 'placeholder': 'Email', 'required': 'True', } ) )

class Meta: model = User fields = ("username", "email", "password1", "password2",)

위와 같이 이번에는 새롭게 추가할 필드인 Email 필드를 선언해줍니다. 이 부분은 생전 처음보쥬?아래에서 설명해 드리겠습니다.

Form의 구조

Page 4: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

Form의 구조는 크게 아래와 같습니다.

1. 폼 필드(Form Field Class)2. 위젯 필드(Widget Field Class)

1번의 폼 필드는 데이터베이스의 필드 구조와 같다고 생각하시면 됩니다. 2번의 위젯 필드는 화면에 보여지는 입력 양식을 뜻합니다. 위 2개를 헷갈리면 안됩니다. 그리고 중요한 것은 1번이나 2번이나 모두 클래스라는 것입니다. 함수가 아니라 클래스이기 때문에 대문자로 시작합니다.

email = forms.EmailField( required=True, widget=forms.EmailInput( attrs={ 'class': 'form-control', 'placeholder': 'Email', 'required': 'True', } ))

위의 소스에서 forms.EmailField가 1번에 해당하는 폼 필드입니다. 그리고forms.EmailInput이 위젯 필드입니다.

Field Arguments

그런데 forms.EmailField 클래스의 괄호 안에 여러가지가 들어가 있습니다. 이를 FieldArgument라고 하는데 여기에 들어가는 Argument는 아래와 같습니다. 이는 공식 문서의 내용을정리해서 가져왔습니다.

Argument 명 내용 Def ault

required 필수 입력 True

label 필드의 Human-f riendly 라벨 필드명

initial 필드의 최초값 없음

widget HTML로 문서의 표현 부분 각 폼 필드마다 다름

help_text 필드의 도움말 없음

error_messages 필드가 에러가 났을 때의 메시지 에러마다 다름

validators 제약 조건 설정 없음

Page 5: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

localize 필드 데이터의 지역화 없음

Argument 명 내용 Def ault

위 인자 중 우리는 두 가지만 사용했습니다. - required, widget. required는 써주지 않아도 디폴트로 True이지만 한 번 사용하고 싶어서 써봤습니다.ㅎㅎㅎ

문제는 Widget입니다. 위에서 설명드린대로 이는 HTML로 표현하는 부분입니다. 만약 Username을 입력받고 싶다면 폼 필드가 CharField 이고, Widget 필드는 TextInput입니다. 하지만Password를 입력바도 싶다면 폼 필드가 CharField이고, Widget 필드는 PasswordInput이됩니다. 데이터베이스에는 똑같이 스트링으로 저장되지만 화면상에 나오는 필드는 비밀번호 형식이어야 하기 때문입니다. 그렇다면 먼저 폼 필드에는 무엇이 있는지 보겠습니다.

Built-in Field Classes

폼 필드명 Def ault Widget Field Model Field

BooleanField CheckboxInput BooleanField

CharField TextInput CharField

DecimalField NumberInput DecimalField

EmailField EmailInput EmailField

공식 문서에 들어가보면 위 4개 보다 더 많다는 것을 알 수 있습니다. 그런데 좀 헷갈리지요. 폼 필드, 위젯 필드, 모델 필드. 폼 필드와 위젯 필드는 짝꿍입니다. 폼 필드 가는 곳에 위젯 필드가 항상같이 있습니다. 폼 필드는 사용자로 부터 값을 입력받을 수 있도록 설정하는 클래스 인데 그 안에HTML로는 어떤 걸 쓰겠냐 라고 물어볼 때 쓰는게 바로 위젯 필드입니다.

그리고 우리가 사용하는 ModelForm은 Model과도 연결이 되어있는데 그 이유는 사용자에게 값을입력받고 바로 데이터베이스에 저장할 수 있게 하기 위해서입니다. 따라서 모델 필드와도 연결되어 있습니다.

위 사항들은 공식 문서에 자세히 나와있습니다.

Widget

그렇다면 위젯 필드에는 무엇이 있을까요!?

위젯 필드 HT ML

TextInput <input type="text" ...>

Page 6: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

NumberInput <input type="number" ...>

URLInput <input type="url" ...>

EmailInput <input type="email" ...>

PasswordInput <input type="password" ...>

Textarea <extarea> ... </textarea>

위젯 필드 HT ML

앞에서도 말씀드렸지만 위젯 필드는 HTML로 보여지는 부분을 담당합니다. 따라서 HTML 코드를만들어주는데 위 테이블에는 각 필드마다 어떻게 표현되는지 나와있습니다. 더 많은 내용은 공식문서를 참고하시기 바랍니다.

attrs 속성

우리는 attrs라는 argument로 EmailField의 속성을 줄 수 있습니다. 이는 HTML에 보여지는 태그의 속성을 정의하는 것입니다.

widget=forms.EmailInput( attrs={ 'class': 'form-control', 'placeholder': 'Email', 'required': 'True', })

EmailField안의 attrs라는 Argument로 HTML의 속성을 아래와 같이 정의해주었습니다.

1. class: f orm-control2. placeholder: Email3. required: True

위 속성을 HTML로 변환하면 아래와 같이 변환됩니다.

<input type="email" class="form-control" placeholder="Email" required="True"/>

위 세 가지 속성이 이렇게 HTML의 속성으로 정의가 되는 것입니다. 그 중 1번의 form-control은 Bootstrap에서 Inputbox를 예쁘게 바꿔주는 CSS 클래스입니다.

Page 7: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

8) 다른 필드 추가하기

이렇게 우리는 위에서 email 필드를 추가해보면서 Form에 대해 본격적으로 다뤄보았습니다.email 필드를 제외한 남은 필드는 Username, Password1, Password2가 남았습니다. 그런데 이세 개의 필드는 생각해보면 UserCreationForm에 이미 있습니다. 그러므로 새롭게 추가할 필요는없습니다.

다만, 우리는 UserCreatoinForm에 있는 Username, Password1, Password2에 없던 어떤 속성을 새로 추가하고자 합니다. 바로 Bootstrap을 적용시키는 부분입니다.

우선 Django 패키지의 UserCreationForm을 찾아 Username, Password1, Password2 필드를만든 부분을 그대로 복사해서 붙여넣겠습니다.

class SignupForm(UserCreationForm): email = forms.EmailField( required=True, widget=forms.EmailInput( attrs={ 'class': 'form-control', 'placeholder': 'Email', 'required': 'True', } ) )

username = forms.RegexField(label=_("Username"), max_length=30, regex=r'̂ [\w.@+-]+$', help_text=_("Required. 30 characters or fewer. Letters, digits and " "@/./+/-/_ only."), error_messages={ 'invalid': _("This value may contain only letters, numbers and " "@/./+/-/_ characters.")}) password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput) password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput, help_text=_("Enter the same password as above, for verification."))

class Meta: model = User

Page 8: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

fields = ("username", "email", "password1", "password2",)

처음에 복사를 하면 위와 같을 것입니다. 여기에서 블로그를 만들면서 하지 않을 작업은 바로 국제화 라는 것입니다. 지역별로 다른 언어로 표기하는 것인데요. 그 부분이 바로 Underscore(_) 부분입니다. 즉 Username의 label 다음에 나와있는 label=_("Username") 부분에서 밑줄과 괄호를 삭제하겠습니다. 그러면 label="Username" 이렇게 될 것입니다.

마찬가지로 밑줄이 되어있는 문자열 값은 모두 위 label과 같은 방법으로 밑줄을 제거하겠습니다.밑줄을 제거한 소스는 아래와 같습니다.

class SignupForm(UserCreationForm): email = forms.EmailField( required=True, widget=forms.EmailField( attrs={ 'class': 'form-control', 'placeholder': 'email', 'required': 'true', } ) )

username = forms.RegexField(label="Username", max_length=30, regex=r'̂ [\w.@+-]+$', help_text="Required. 30 characters or fewer. Letters, digits and " "@/./+/-/_ only.", error_messages={ 'invalid': "This value may contain only letters, numbers and " "@/./+/-/_ characters."}) password1 = forms.CharField(label="Password", widget=forms.PasswordInput) password2 = forms.CharField(label="Password confirmation", widget=forms.PasswordInput, help_text="Enter the same password as above, for verification.")

class Meta: model = User fields = ("username", "email", "password1", "password2",)

9) 위젯에 HTML 속성(attrs) 추가하기

Page 9: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

이 소스에서 Bootstrap을 적용해보도록 하곘습니다. Bootstrap 중에서도 Inputbox에 적용시킬Class는 form-control이라는 것입니다. 이 부분은 Bootstrap의 공식 문서를 참고하시기 바랍니다. Class라는 것은 HTML의 태그안에 들어갈 속성 부분이기 때문에 Widget 필드에 속성(attrs)값으로 입력합니다. 아래와 같이 작성하겠습니다.

class SignupForm(UserCreationForm): email = forms.EmailField( required=True, widget=forms.EmailField( attrs={ 'class': 'form-control', 'placeholder': 'email', 'required': 'true', } ) )

username = forms.RegexField(label="Username", max_length=30, regex=r'̂ [\w.@+-]+$', help_text="Required. 30 characters or fewer. Letters, digits and " "@/./+/-/_ only.", error_messages={ 'invalid': "This value may contain only letters, numbers and " "@/./+/-/_ characters."}, widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': 'Username', 'required': 'true', }) password1 = forms.CharField(label="Password", widget=forms.PasswordInput(attrs={ 'class': 'form-control', 'placeholder': 'Password', 'required': 'true', })) password2 = forms.CharField(label="Password confirmation", widget=forms.PasswordInput(attrs={ 'class': 'form-control', 'placeholder': 'Password confirmation', 'required': 'true', })), help_text="Enter the same password as above, for

Page 10: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

verification.")

class Meta: model = User fields = ("username", "email", "password1", "password2",)

email 필드를 작성했던 것과 마찬가지로 widget에 attrs으로 속성값을 설정한 것을 알 수 있습니다.

2. views.py

1) 소스 수정

기존 소스는 아래와 같이 되어있을 것입니다. 이는 UserCreationForm을 사용하는 것입니다.

from django.shortcuts import renderfrom django.http import HttpResponseRedirectfrom django.core.urlresolvers import reversefrom django.contrib.auth.forms import UserCreationForm

def signup(request): """signup to register users """ if request.method == "POST": userform = UserCreationForm(request.POST) if userform.is_valid(): userform.save()

return HttpResponseRedirect( reverse("signup_ok") ) elif request.method == "GET": userform = UserCreationForm()

return render(request, "registration/signup.html", { "userform": userform, })

위와 같이 기존에 UserCreationForm을 사용할 수 있도록 만들어놓은 signup 함수를 아래와 같이우리가 새로 만든 SignupForm을 사용할 수 있도록 수정하겠습니다.

Page 11: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

from django.shortcuts import renderfrom django.http import HttpResponseRedirectfrom django.core.urlresolvers import reversefrom qna.forms import SignupForm

def signup(request): """signup to register users """ if request.method == "POST": signupform = SignupForm(request.POST) if signupform.is_valid(): user = signupform.save(commit=False) user.email = signupform.cleaned_data['email'] user.save()

return HttpResponseRedirect( reverse("signup_ok") ) elif request.method == "GET": signupform = SignupForm()

return render(request, "registration/signup.html", { "signupform": signupform, })

2) 달라진 점

달라진 것이라곤 아래 두 가지밖에 없습니다.

1. UserCreationForm -> SignupForm2. is_valid()(유효성 검사) 이후 email 추가로 저장하기

위 2번, 즉 is_valid() 유효성 검사 이후에 하는 행동들은 아래와 같습니다.

signupf orm.cleaned_ data[‘email’ ]

1. self 클래스로부터 인스턴스를 만들 때 해당 인스턴스 이름이 뭔지는 모르겠지만 우선self로 부르자! 라는 뜻을 가지고 있습니다. 즉 만들어진 인스턴스를 가리킵니다.

2. cleaned_data Form은 is_valid() 함수를 통해 사용자가 입력한 값이 문제가 있는지 없는지 체크를

Page 12: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

합니다. 그리고 체크가 없을 경우 cleaned_data라는 사전 객체를 만들어 사용자가입력한 값을 이 사전 객체에 넣어줍니다. Validation을 통과했기 때문에 깨끗한 데이터, 즉 cleaned data라 cleaned_data라는 이름으로 만든 것 같습니다.user.email = cleaned_data['email'] : 우리가 사용자로부터 받아온 여러값 중에 email에 해당하는 값을 위에서 만든 user객체의 email 필드에 할당하겠다 라는 뜻입니다.

이 부분이 조금 어려웠을 것 같습니다. 이 부분은 밑에 views에서 한 번 더 할 것이니 한 번 더 보면서 학습하면 좋을 거 같습니다.

save()

user = signupform.save(commit=False)user.email = signupform.cleaned_data['email']user.save()

처음에 나오는 save() 함수의 commit=False라는 것은 Database에 아직 저장하지 말라는뜻입니다. 일반적으로 Database에 저장하는 것은 Commit을 해야 그 때서라 비로소 Database에저장이 되기 때문입니다. 그런데 왜 아직 저장하지 말라고 했을까요? 이렇게 한 이유는 추가로 속성값을 저장시킬 게 있기 때문입니다. 바로 Email이지요. 그래서 아래 Email 값을 입력하고 나서다시 save() 함수를 사용해서 Database에 저장한 것입니다.

3. signup.html

1) 새로운 파일 생성

이번엔 템플릿을 바꾸도록 하겠습니다. 기존에 있던 signup.html과 signup_ok.html 내용은 더 이상 사용하지 않을 것이기 때문에 지우고 다시 시작하겠습니다.

templates > registration 폴더에 signup.html을 새로 생성해서 about.html에 있는 내용을 그대로 복사+붙여넣기 하도록 하겠습니다. 마찬가지로 signup_ok.html도 똑같이만들겠습니다.

2) 코딩

새로 생성한 signup.html의 {% block content %} 와 {% endblock %} 사이에 아래내용을 입력하도록 하겠습니다. 아래의 내용은 우리가 다운받은 clean-blog 템플릿의contact.html에 있던 폼 내용을 복사하여 우리 상황에 맞게 수정한 것입니다.

Page 13: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

<!-- Sign Up Form --><form id="signup" class="form-horizontal" method="post" action="{% url 'signup' %}"> {% csrf_token %}

<!-- Username input--> <div class="row control-group"> <div class="form-group col-xs-12 floating-label-form-group controls"> {{ signupform.username.label_tag }} {{ signupform.username }} <span class="field-error"> {{ signupform.username.errors|striptags }} </span> </div> </div>

<!-- Email input--> <div class="row control-group"> <div class="form-group col-xs-12 floating-label-form-group controls"> {{ signupform.email.label_tag }} {{ signupform.email }} <span class="field-error"> {{ signupform.email.errors|striptags }} </span> </div> </div>

<!-- Password1 input--> <div class="row control-group"> <div class="form-group col-xs-12 floating-label-form-group controls"> {{ signupform.password1.label_tag }} {{ signupform.password1 }} <span class="field-error"> {{ signupform.password1.errors|striptags }} </span> </div> </div>

<!-- Password2 input--> <div class="row control-group"> <div class="form-group col-xs-12 floating-label-form-

Page 14: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

group controls"> {{ signupform.password2.label_tag }} {{ signupform.password2 }} <span class="field-error"> {{ signupform.password2.errors|striptags }} </span> </div> </div>

<br>

<!-- Button --> <div class="row"> <div class="form-group col-xs-12"> <button id="signup" name="signup" class="btn btn-block btn-default" type="submit">Sign Up</button> <hr> <center> Or <a href="{% url 'login_url' %}"><u>log in</u></a> if you have an account. </center> </div> </div>

</form>

여기선 아래 세 가지를 살펴보겠습니다.

{{ signupf orm.username.label_ tag }}

views.py에서 signupf orm이라는 이름으로 보낸 signupform 기억하시죠? 이 객체에는 사용자가 입력할 수 있도록 username, email, password1, password2를 만들어 놓았었습니다. 그리고 여기서는 username을 먼저 보여주는데 Django의 f orm은 항상 세 가지로 짝지어 있습니다.

1. Label2. 폼태그(에. <input type="text" ... /> )3. 에러 메시지

이렇게 세 가지를 작성하는 것인데, 첫번째로 label을 프린트하는 것입니다. {{signupform.username.label }} 이렇게만 해도 되는데 label_tag 라고 한 이유는 단순히label만 적으면 HTML 태그의 <ul>태그가 붙여져 나와서 그 태그를 없애라는 의미의 기능입니다.

{{ signupf orm.username }}

Page 15: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

이제 위에서 말한 세 가지 중 두 번째 인 폼태그입니다. 이 부분이 바로 사용자가 입력할 수 있도록폼태그를 생성하는 부분입니다. 아무것도 입력하지 않고 바로 필드명만 적어주면 됩니다.

{{ signupf orm.username.errors|striptags }}

사용자가 특정 값을 입력하고 나서 값에 에러가 있을 때 (is_valid()에서 False가 나왔을 때) 해당 에러 메시지를 뿌려줍니다. 여기서 특이한게 있습니다. 바로 errors 다음에 붙은 파이프라인(|)입니다.

이는 템플릿 필터라고 부릅니다. 템플릿 필터에는 여러가지가 있는데 여기서는 striptags라는 것을 사용했습니다. errors도 label과 마찬가지로 자동으로 <ul>를 붙여서 보여줍니다. 이 태그를제거하는 필터가 바로 striptags입니다.

4. 테스트

이렇게 템플릿까지 작성하면 회원가입 기능은 완성된 것입니다. 웹서버를 실행시켜 한 번 테스트해보세요.

2. 로그인 Form

우리는 지난 시간에 Django에서 기본적으로 제공하는 로그인 양식인 AuthenticationForm을 사용했었는데 기억안나시쥬?ㅠㅠ 괜찮습니다. 다시 만들어보아요.

1. forms.py

우리는 기존에 만들어져있는 AuthenticationForm을 조금 수정해서 사용하도록 하겠습니다.

1) AuthenticationForm 사용하겠다고 선언하기

forms.py 파일을 열어 아래와 같이 내용을 추가합니다.

from django.contrib.auth.forms import UserCreationForm, AuthenticationForm

Page 16: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

2) LoginForm 클래스 생성

class LoginForm(AuthenticationForm):

3) 클래스 내용 추가하기

우리는 기본적으로 제공하는 것에 조금 수정해서 사용할 것입니다. 따라서 우선 수정할 부분만 복사해오겠습니다. django.contrib.auth.f orms 파일에 가서 AuthenticationForm 클래스를 찾습니다. 그리고 로그인에 필요한 username, password를 복사해 옵니다. 아래와 같이요.

class LoginForm(AuthenticationForm): username = forms.CharField(max_length=254) password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)

여기에서 우리는 Bootstrap 속성을 입힐 것이므로 아래와 같이 widget을 이용하도록 합니다.

username = forms.CharField( max_length=254, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': 'Username', 'required': 'True', } ) ) password = forms.CharField( widget=forms.PasswordInput( attrs={ 'class': 'form-control', 'placeholder': 'Password', 'required': 'True', } ) )

더 이상 할 게 없습니다. 이 게 끝입니다. 사실 할 게 많긴 한데요. 예를 들어 username이 틀렸을 경우에 올바르게 입력하라고 메시지를 뿌려준다던가 해당 username과 password를 비교한다던가등의 기능은 상속받아온 AuthenticationForm에 이미 만들어져 있고 우리는 상속받아왔기 때문

Page 17: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

에 그 기능을 그대로 사용할 수 있는 것입니다.

2. views.py

view 함수 역시 Django에서 이미 만들어놓은 함수를 사용하도록 하겠습니다. 경로는django.contrib.auth.views.login입니다. 우리는 따로 만들 것이 없으므로 이 함수를 그대로 URL에서 설정하도록 하겠습니다.

3. urls.py

1) LoginForm 사용하겠다고 선언하기

먼저 프로젝트 아래에 프로젝트 이름과 똑같은 이름을 가진 디렉토리 즉, 시작 패키지 디렉토리 안에 있는 urls.py 파일을 열어 맨 위 어느쯤에 아래와 같이 작성하여 우리가 만든LoginForm 을 사용하겠다고 선언하겠습니다. 이를 유식한 말로 Namespace를 설정한다고 합니다.

from qna.forms import LoginForm

2) url pattern 설정하기

urlpattern 변수를 보면 많은 url() 함수가 있지요. 이들 사이에 아래와 같이 작성하도록 하겠습니다.

url(r'̂ login/$', 'django.contrib.auth.views.login', { 'authentication_form': LoginForm }, name='login_url'), url(r'̂ logout/$', 'django.contrib.auth.views.logout', { 'next_page': '/login/', }, name='logout_url'),

login

호스트명/login으로 접속했을 경우 django에서 이미 만들어 놓은 login함수로 이동을 합니다. 함수로 전달해주는 인자값은 authentication_form 이라는 이름에 LoginForm 객체를전달해줍니다. 즉 로그인 양식으로 우리가 만든 LoginForm을 사용하겠다고 말한 것입니다. 그리

Page 18: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

고 이 url pattern의 이름을 login_url이라는 이름으로 설정합니다.

logout

호스트명/logout으로 접속했을 경우 django에서 이미 만들어 놓은 logout함수로 이동을 합니다. 함수로 전달해주는 인자값은 next_page이라는 이름에 /login/ 라는 값을 전달해줍니다. 즉 로그아웃하고 나서 이동하는 페이지를 설정한 것입니다. 그리고 이 url pattern의 이름을logout_url이라는 이름으로 설정합니다.

urls.py 전체 소스

만들어진 전체 소스는 아래와 같습니다.

from django.conf.urls import patterns, include, urlfrom django.contrib import adminfrom django.views.generic import TemplateViewfrom qna.forms import LoginForm

urlpatterns = patterns('', url(r'̂ admin/', include(admin.site.urls)), url(r'̂ signup/$', 'qna.views.signup', name='signup'), url(r'̂ signup_ok/$', TemplateView.as_view( template_name='registration/signup_ok.html' ), name='signup_ok'),

url(r'̂ $', 'qna.views.home', name='home'), url(r'̂ about/$', 'qna.views.about', name='about'), url(r'̂ q/$', 'qna.views.question', name='question'),

url(r'̂ login/$', 'django.contrib.auth.views.login', { 'authentication_form': LoginForm }, name='login_url'), url(r'̂ logout/$', 'django.contrib.auth.views.logout', { 'next_page': '/login/', }, name='logout_url'),)

4. login.html

이제는 로그인을 할 수 있도록 템플릿을 만들어 보겠습니다.django.contrib.auth.views.login이라는 함수는 우리가 만든 LoginForm 양식을

Page 19: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

form 이라는 이름으로 렌더링하여 템플릿 파일에 넘겨줍니다. 즉 login.html에서는form 이라는 객체를 사용하여 username과 password 양식을 만들어낼 수 있습니다.

이번에도 마찬가지로 login.html 파일을 열어 {% block content %} 와 {% endblock%} 사이에 아래와 같이 입력하겠습니다.

<!-- Sign In Form --><form id="signin" class="form-horizontal" method="post" action="{% url 'login_url' %}"> {% csrf_token %}

<!-- Username input--> <div class="row control-group"> <div class="form-group col-xs-12 floating-label-form-group controls"> {{ form.username.label_tag }} {{ form.username }} </div> </div>

<!-- Password input--> <div class="row control-group"> <div class="form-group col-xs-12 floating-label-form-group controls"> {{ form.password.label_tag }} {{ form.password }} <span class="field-error"> {{ form.non_field_errors|striptags }} </span> </div> </div>

<br>

<!-- Sign in Button --> <div class="row"> <div class="form-group col-xs-12"> <input type="hidden" name="next" value="{{ next }}"> <button id="signin" name="signin" class="btn btn-block btn-default" type="submit">Log In</button> <hr> <center> Or <a href="{% url 'signup' %}"><u>sign up</u></a>

Page 20: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

if you don't have any account. </center> </div> </div>

</form>

위에서 회원가입과 할 때와 마찬가지로 form 객체의 username과 password 속성을 가져오는부분만 잘 보면 되겠습니다. 이 부분은 회원가입에서 설명했으므로 넘어가겠습니다.

5. 로그인 후 페이지 이동하기

로그인이 완료된 후 페이지를 이동시켜 보도록 하겠습니다. 아래와 같이 settings.py의 아무곳에서 입력하도록 하겠습니다.

LOGIN_REDIRECT_URL = '/'

즉 로그인이 완료되면 Redirect할 곳을 맨 처음 페이지로 설정해놓은 것입니다.

6. 로그아웃 버튼 만들기

이제 로그인을 했으니깐 로그인했을 때의 화면과 로그아웃했을 때의 화면은 달라져야겠지요!? 아래와 같이 base.html 파일을 열어 수정해보도록 하겠습니다.

<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav navbar-right"> <li> <a href="{% url 'home' %}">Home</a> </li> <li> <a href="{% url 'about' %}">About</a> </li> <li> <a href="{% url 'post' %}">Question</a> </li> <li> {% if request.user.is_authenticated %}

Page 21: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

<a href="{% url 'logout_url' %}">{{ request.user.username }}, Log Out</a> {% else %} <a href="{% url 'login_url' %}">Log In</a> {% endif %} </li> </ul></div>

위 소스 중에서도 아래 부분을 수정해야 합니다.

<li> {%if request.user.is_authenticated %} <a href="{% url 'logout_url' %}">{{ request.user.username }}, Log Out</a> {% else %} <a href="{% url 'login_url' %}">Log In</a> {% endif %}</li>

1) {% request.user.is_authenticated %}

웹 페이지는 request되어집니다. request(요청)될 때 웹서버는 request 객체를 전달해줍니다. 그객체에는 user 정보도 담도록 Django는 설정해 놓고 있는데요. 그 user가 로그인된 User인지 확인하는 함수가 바로 is_authenticated함수입니다. 함수는 일반적으로 괄호를 열고 닫는 것이있지만 템플릿에서는 절대로 괄호를 사용하지 않습니다.

2) 로직

로그인을 한 상태라면, username을 출력하고 로그아웃할 수 있도록 링크를 걸어줍니다. 로그아웃된 상태라면, 로그인을 할 수 있도록 링크를 걸어줍니다. 이를 위와 같이 if문으로 표현한 것입니다.

7. 로그인/로그아웃 끝

자, 이렇게 로그잇과 로그아웃은 Django에서 제공해주는 것을 이용해 금방 끝냈습니다. 아래부터는 우리가 모델부터 직접만들어 사용하도록 하겠습니다.

Page 22: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

3. Post

이번에는 새로운 글을 작성하는 것을 만들어 보겠습니다. 우리가 위에서 했던 방식과 차이는 별로없습니다만, 이번에는 Django에서 기본적으로 제공해주는 모델이 없으므로 새로운 글이 저장될수 있는 모델을 만들도록 하겠습니다. 이 모델은 데이터베이스의 테이블과 같다는 것을 잊으시면안됩니다.

1. models.py

위 MTV 패턴을 기억하시나요? 우리는 이제 글을 작성해서 담을 수 있는 그릇을 models.py에서만들 것입니다.

1) 모델 디자인

Page 23: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

사용자가 글을 작성하면 그 글이 어딘가에 저장이 됩니다. 그 어딘가를 정의하는 곳이 바로models.py입니다. 그 데이터가 어떻게 저장될지 디자인 해보도록 하겠습니다.

필드명 설명 필드타입 제약조건

user 사용자 ForeignKey 외래키

title 글제목 CharField 최대 길이(300)

content 글내용 TextField 없음

updated_at 수정 날짜 DateTimeField 자동으로 수정 시간 기록

created_at 만든 날짜 DateTimeField 자동으로 생성 시간 기록

우선 위와 같이 하도록 하겠습니다. 글의 태그는 아래에서 추가해보도록 할께요. 위에서 말한 필드타입이란 건 Django에서 설정해주는 타입이며 Database에서는 조금씩 다를 수 있습니다.

2) qna앱의 models.py

models.py을 열어 아래와 같이 작성하도록 하겠습니다.

User 모델 사용하겠다고 선언하기

from django.contrib.auth.models import User

우리는 글을 담는 그릇(테이블)에 어떤 사용자가 작성한 건지도 기록할 것입니다. 따라서 우리가현재 사용하고 있는 User 모델을 가져와야 하는 것입니다.

Question 모델 클래스 만들기

class Question(models.Model): """question when a user posts questions """ user = models.ForeignKey(User) title = models.CharField(max_length=300) content = models.TextField() updated_at = models.DateTimeField(auto_now=True) created_at = models.DateTimeField(auto_now_add=True)

Page 24: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

필드타입

모델의 경우 이렇게 필드명 = models.필드타입() 이런 식으로 선언합니다. 그리고 그 필드타입의 경우 아래와 같이 여러가지가 있습니다. 이는 공식문서를 참고하였습니다.

필드명 설명

AutoField IntegerField that automatically increments

BooleanField true/f alse

CharField string f ield

DateTimeField date and time

DecimalField f ixed-precision decimal number

EmailField valid email address

IntegerField Values f rom -214 74 8364 8 to 214 74 8364 7

SmallIntegerField Values f rom -32768 to 32767

BigIntegerField Values f rom -9223372036854 775808 to 9223372036854 775807

PositiveIntegerField Values f rom 0 to 214 74 8364 7

TextField Large text

Relationship Field

그런데 우리는 위에서 User 모델을 사용할 때 1:Many 관계를 사용했습니다. 이를 Relationshipf ield라고 부릅니다. 이런 필드는 아래와 같이 세 가지가 있습니다. 공식 문서를 참고했습니다.

1. ForeignKey2. ManyToManyField3. OneToOneField

Question 모델에 대한 속성 설정

class Question(models.Model): # 중략 class Meta: ordering = ['-updated_at']

Page 25: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

Meta Option

Python에는 메타 클래스라는 것이 있습니다. Data about data 라는 뜻입니다. 그리고 메타 클래스에는 들어가는 옵션을 Django에서 지정해주고 있습니다. 이를 Meta Option이라고 합니다. 공식 문서를 찾아보면 아래와 같은 것들이 있습니다.

1. app_label2. db_table3. ordering4 . unique_together

ordering

이중에서도 우리는 ordering이라는 것을 사용했습니다. 즉 Question 모델에 있는 데이터를 가져올 때의 정렬 순서를 미리 정해놓은 것입니다.

unicode 함수 만들기

class Question(models.Model): # 중략 def __unicode__(self): return u"{0}".format(self.title)

unicode 함수

Python에는 이상하게 생긴 함수가 있습니다. 바로 Underscore(_)가 두 개가 양 옆으로 있는 함수인데요. 이는 조금 특수한 메서드여서 특수 메서드 라고 합니다.ㅋㅋ 아래와 같은 종류가 있습니다.

객체 생성 및 파괴

특수 메소드 설명

__new__ 새 인스턴스를 생성하기 위해 호출되는 클래스 메서드

__init__ 새 인스턴스를 초기화하기 위해 호출된다.

__del__ 인스턴스가 파괴될 때 호출된다.

객체의 문자열 표현

특수 메소드 설명

__repr__ 공식적인 문자열

1

2

Page 26: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

__str__ 더 간결하며 사용자에게 정보를 제공하는 문자열 반환, 비공식적인 문자열

__unicode__ 유니코드 문자열을 반환

특수 메소드 설명

우리는 unicode 함수를 사용했는데 해당 클래스의 인스턴스를 출력(print)할 때 이 객체의 내용을출력할 수 있도록 해 놓은 것입니다.

String f ormatting

u"{0}".format(self.title)

인스턴의 내용을 출력하는데 f ormat 함수를 사용했습니다. 이는 치환자라고도 합니다. Python문법이다. 좌측의 값중 0번째 인덱스 값을 f ormat함수의 0번째 인자값으로 치환해준다는 뜻입니다.

자세한 건 공식 문서를 참고하시기 바랍니다.

완성된 소스

from django.db import modelsfrom django.contrib.auth.models import User

class Question(models.Model): """question when a user posts questions """ user = models.ForeignKey(User) title = models.CharField(max_length=300) content = models.TextField() updated_at = models.DateTimeField(auto_now=True) created_at = models.DateTimeField(auto_now_add=True)

class Meta: ordering = ['-updated_at']

def __unicode__(self): return u"{0}".format(self.title)

3) makemigrations & migrate

Page 27: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

models.py를 수정하면 항상 해야하는 것이 바로 마이그레이션입니다. 즉 데이터베이스 디자인이 수정되었기 때문에 이를 실제로 데이터베이스에 반영하는 작업인 것입니다. 아래처럼 실행을하시기 바랍니다.

$ python manage.py makemigrations

위와 같이 실행하여 Create model Question이란 메시지가 뜨면 성공한 것입니다.

$ python manage.py migrate

위와 같이 실행하여 Applying qna.0001_...... OK라고 뜨면 성공한 것입니다.

2. forms.py

모델을 만들었으니 사용자가 글을 쓸 수 있도록 폼을 만들어보도록 하겠습니다. 근데 사실 적을 게별로 없죠!? 글제목과 글내용만 적도록 해주면 됩니다. 회원가입할 때 공부했던 ModelForm을 사용해서 만들도록 하겠습니다.

그런데 우리는 글내용을 적을 때 특수한 에디터를 설치해보도록 하겠습니다. 그냥 HTML의Textarea로는 멋지게 만들 수 없죠. 그래서 오픈소스로 되어있는 에디터를 설치해서 이용하도록하겠습니다.

1) django-summernote 설치

Form을 작성하기 전에 먼저 사용하게 될 오픈소스인 django-summernote를 설치하도록 하겠습니다. Github에서 제공하는 오픈소스입니다. 사용방법이 여기에 다 적혀있기 때문에 여기에 나와있는대로 하시면 됩니다. 자세한 설치법은 Github에 설치법을 보면서 하겠습니다.

Markdown을 사용하고 싶은 분들에게

최근에는 Markdown editor도 많이 사용하고 있습니다. 글 쓰기에 Markdown 만큼 좋은 것도 없지요. 여러 오픈소스가 있겠지만 Django로 만들어진 Markdown은 django-markdown 같은 것이있습니다. 이 것도 오픈소스이며 Github에 있는 설명에 따라 설치를 하고 사용하시면 됩니다.

2) Summernote와 Question 모델 사용하겠다고 선언하기

forms.py을 열어서 아래와 같이 작성합니다.

Page 28: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

from django_summernote.widgets import SummernoteWidgetfrom qna.models import Question

우리가 만들 PostForm은 Question 모델을 기본으로 하기 때문에 Question까지 불러왔습니다.

3) PostForm 클래스 만들기

아래와 같이 작성합니다.

class PostForm(forms.ModelForm):

django의 f orms에서 제공하는 ModelForm을 상속받아 만들겠다는 뜻입니다.

4) Meta class 설정하기

class PostForm(forms.ModelForm): class Meta: model = Question fields = ('title', 'content', )

Question 모델을 사용할 거고 title, content 필드를 사용할 것이라고 설정하는 부분입니다.

5) 필드 타입 설정하기

아래와 같이 title 필드와 content 필드에 대해 설정해줍니다.

title = forms.CharField( required=True, max_length=300, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': 'Title', } ) )content = forms.CharField( widget=SummernoteWidget(

Page 29: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

attrs={ 'placeholder': 'Content', } ))

여기에서 content 부분을 눈여겨 봐야합니다. 우리는 이전에 설치한 Summernote를 사용할 것이기 때문에 Widget으로 SummernoteWidget을 선언한 부분을 볼 수 있습니다.

이렇게 작성하면 forms.py은 완성된 것입니다.

3. views.py

이제 사용자가 작성한 글을 받아와 저장해야하는 일이 남았습니다. 이는 회원가입할 때와 거의 똑같은 형식이라 어렵지 않습니다.

1) PostForm 사용하겠다고 선언하기

from qna.forms import SignupForm, PostForm

2) post 함수 선언하기

def post(request): """ posting questions """

3) POST와 GET 방식으로 분기 처리

if request.method == "POST":

elif request.method == "GET":

return render(request, "post.html", { "postform":postform,})

Page 30: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

4) GET방식에서 Form 객체 그냥 생성하기

if request.method == "POST":

elif request.method == "GET": postform = PostForm()return render(request, "post.html", { "postform":postform,})

5) POST방식에서 사용자가 입력한 값 가져와 Form 객체 생성하기

if request.method == "POST": postform = PostForm(request.POST)elif request.method == "GET": postform = PostForm()return render(request, "post.html", { "postform":postform,})

6) Validation 체크하기

if request.method == "POST": postform = PostForm(request.POST) if postform.is_valid():elif request.method == "GET": postform = PostForm()return render(request, "post.html", { "postform":postform,})

7) Validation 처리가 완료되면 Redirect하기

if request.method == "POST": postform = PostForm(request.POST)

Page 31: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

if postform.is_valid():

return HttpResponseRedirect(reverse('home'))elif request.method == "GET": postform = PostForm()return render(request, "post.html", { "postform":postform,})

8) 데이터베이스에 저장하기

if request.method == "POST": postform = PostForm(request.POST) if postform.is_valid(): post = postform.save(commit=False) post.user = request.user post.save()

return HttpResponseRedirect(reverse('home'))elif request.method == "GET": postform = PostForm()return render(request, "post.html", { "postform":postform,})

여기서 유심히 봐야할 것이 request.user입니다. 사용자는 웹 페이지를 요청 즉, request합니다. 이 때 request 객체가 전달이 됩니다. Django에서는 특히 이 request 객체에 user를 저장해놓고 있습니다. 그래서 우리는 request.user를 통해 사용자 정보를 얻을 수 있습니다. 만약 사용자가 없다면 즉, 로그인을 하지 않았다면 어떻게 될까요? 익명(Anonymous)처리 됩니다.

이렇게 해서 views.py는 완성되었습니다. 하지만 가만히 생각해보면 글을 쓰는 사람은 익명으로 하면 안될 거 같습니다. 로그인을 필수적으로 할 수 있도록 안내해야할 것 같습니다. 이 때 사용하는 것이 바로 아래에 나와있습니다.

login_required decorator

python에는 decorator(장식자)라는 것이 있습니다. 함수를 인자값으로 하는 함수입니다. 그래서특정 함수위에 사용하게 됩니다. 아래에 있는 함수를 인자값으로 받기 위해서 말이지요. 아래와 같이 작성해 보겠습니다.

from django.contrib.auth.decorators import login_required

Page 32: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

#중략

@login_requireddef post(request): # 생략

이렇게 decorator는 함수 위에 쓰며 @장식자명 으로 사용합니다. 자세한 것은 공식 문서를 참고하시기 바랍니다.

여기서는 login_required라는 decorator를 사용했는데 이는 아래에 있는 함수를 사용하기위해서는 먼저 로그인을 하라는 뜻입니다. 참 간편하죠!?

LOGIN_ URL / LOGOUT _ URL 설정하기

단, 주의할 것이 있습니다. login_required 장식자의 경우 기본 Login 주소가accounts/login으로 되어있습니다. 그런데 우리는 이 주소로 하지 않았기 때문에 주소를 바꿔줘야합니다. 아래와 같이 settings.py에 내용을 추가하도록 하겠습니다.

LOGIN_URL = '/login/'LOGOUT_URL = '/logout/'

4. post.html

templates 디렉토리에 post.html명으로 새로운 파일을 만듭니다. 그리고 about.html 파일의 내용을 복사해서 붙여넣기 합니다. 가장 기본이 되는 레이아웃이라서 복사 붙여넣기 한 것입니다. 그리고 {% block content %} 와 {% endblock %} 사이에 아래와 같이 작성합니다.

<!-- post form --><form id="postform" class="form-horizontal" method="post" action="{% url 'post' %}"> {% csrf_token %}

<!-- Title input--> <div class="row control-group"> <div class="form-group col-xs-12 floating-label-form-group controls"> {{ postform.title.label_tag }} {{ postform.title }} <span class="field-error">

Page 33: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

{{ postform.title.errors|striptags }} </span> </div> </div>

<!-- Content input--> <div class="row control-group"> <div class="form-group col-xs-12 floating-label-form-group controls"> {{ postform.content.label_tag }} {{ postform.content|safe }} <span class="field-error"> {{ postform.content.errors|striptags }} </span> </div> </div>

<br>

<!-- Post Button --> <div class="row"> <div class="form-group col-xs-12"> <button id="post" name="post" class="btn btn-block btn-default" type="submit">Post</button> </div> </div>

</form>

위에 회원가입했을 때와 비슷합니다.

1. {{ postform.title.label_tag }}2. {{ postform.title }}3. {{ postform.title.errors|striptags }}

위의 형식이 그대로 사용되고 있습니다.

5. urls.py

urls.py 파일을 열어 아래의 내용을 추가합니다.

url(r'̂ post/$', 'qna.views.post', name='post'),

Page 34: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

4. 작성한 글 보기우리는 위에서 글을 작성하는 것을 구현해보았습니다. 이제는 그 작성한 글을 볼 수 있는 페이지를만들어 보겠습니다. 잘 생각해보면 이 부분은 딱 하나만 필요합니다. 원하는 데이터를 가져오는 것이지요.

1. models.py

위에서 말씀드렸지요. 원하는 데이터를 가져오는 것만 필요하다고요! ㅋㅋ 그렇기 때문에 데이터를 담는 그릇인 모델 부분은 여기에서 필요가 없습니다.

2. forms.py

위에서 말씀드렸지요. 원하는 데이터를 가져오는 부분만 필요하다고요!^^ f orms는 사용자에게입력을 받을 때에만 사용하는 것이기 때문에 이 부분에서는 f orms도 필요없습니다.

3. urls.py

URL 패턴 디자인을 해보겠습니다. 즉 어떤 경로로 들어갔을 때 해당 글을 볼 수 있을까요. 아래와같이 urls.py에 작성해보도록 하겠습니다.

url(r'̂ question/(?P<question_id>[0-9]+)/$', 'question', name='view_question'),

r’^question/(?P[0-9]+)/$’

url 함수의 첫 번째 인자값은 정규 표현식입니다. 먼저 볼 것은 question/ 다음에 나오는 (?P[0-9]+)/ 입니다. 정규 표현식에서 만약 (?P<question_id> ... )와 같은 형태가 있다면이는 파라미터 즉, 인자값을 의미합니다. 이는 해당 URL로 접속했을 때 실행하게 될 함수에 보내지

Page 35: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

는 값을 의미합니다. 영문자 P 다음에 나오는 괄호 <> 안이 파라미터 이름을 뜻합니다. 우리는 여기서 파라미터 이름을 question_id라고 정하겠습니다.

이제는 파라미터 값이 어떤 것인지 살펴보겠습니다. <> 다음에 나오는 값을 살펴보겠습니다.[0-9]+ 입니다. 처음에 나오는 [0-9]는 숫자 0부터 9까지를 의미합니다. 그 다음에 나오는+ 는 숫자 0부터 9까지 2자 이상 파라미터 값일 수 있다는 의미입니다.

즉, 호스트명/question_id가 0부터 9까지 1자 이상 인 URL로 요청했을 때 뷰 함수로 이동하라는 뜻입니다.

그 뷰 함수가 바로 qna.views.question 입니다. 이 URL 패턴의 이름은 view_question입니다.

4. views.py

다음은 위에서 설정한 함수인 question을 views.py에서 만들어 보겠습니다. 아래를 따라해보도록 하겠습니다.

question 함수 선언하기

아래와 같이 함수를 선언합니다.

def question(request, question_id=0): """ viewing the question """

우리는 위에서 특정 URL로 접속을 할 때 question_id라는 이름으로 파라미터값을 전달하도록했습니다. 그렇기 때문에 그 파라미터값 즉, 인자값을 받을 수 있도록 question_id=0이라고작성한 것입니다. 원래는 question_id만 입력해도 되는데 0을 할당한 이유는 0을 def ault로 하라는 것입니다.

question_id가 0일 때

그런데 생각해보면 question_id가 0일 때는 없을 것입니다. 왜냐면 여기서 말하는question_id는 Django에서 autoincrement되는 글 번호인데 항상 1이상이기 때문이지요. 그렇기 때문에 악의적인 뜻을 가진 사용자가 URL에 호스트명/question/0이라고 입력했을 경우를 대비해 0이 들어오면 첫 화면으로 가게끔 설정하도록 하겠습니다.

Page 36: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

def question(request, question_id=0): """ viewing the question """

if int(question_id) == 0: HttpResponseRedirect(reverse('home'))

get_object_or_404() 함수 사용하겠다고 선언하기

from django.shortcuts import render, get_object_or_404

우리는 이제부터 데이터를 불러오도록 할 것입니다. 데이터를 불러올 때 get_object_or_404함수를 사용할 것입니다. 이 함수는 데이터를 불러오는데 데이터가 있으면 상관은 없지만 만약 없다면 4 04 에러 메시지를 보여주라는 함수입니다. 4 04 에러 메시지는 해당 페이지를 못 찾았을 경우 나오는 메시지입니다. 위 함수는 공식 문서를 참고하세요.

get_object_or_404() 함수 사용해서 데이터 가져오기

이제는 데이터를 가져와보겠습니다. 이 함수 사용법은 아래와 같습니다.

get_object_or_404(모델명, 필드명=값)

여기에서 필드명=값 은 찾을 값을 의미합니다. 그리고 우리가 찾을 데이터는 Question 모델 안에 있는 데이터입니다. 따라서 모델명에 Question을 써야하는데 이 모델을 사용하겠다고 선언을 안했네요? 선언부터 하겠습니다. 맨 위에다 작성하겠습니다.

from qna.models import Question

그리고 아래와 같이 작성하겠습니다.

def question(request, question_id=0): """ viewing the question """

if int(question_id) == 0: HttpResponseRedirect(reverse('home'))

Page 37: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

q = get_object_or_404(Question, id=question_id)

get_object_or_404() 함수를 사용해서 가져온 데이터는 q라는 객체에 담는 과정인 것입니다.

question.html로 렌더링해서 보내기

자 이제 마지막 단계입니다. 위에서 만든 q객체를 question.html 템플릿 파일로 렌더링해서보내주도록 하겠습니다.

def question(request, question_id=0): """ viewing the question """

if int(question_id) == 0: HttpResponseRedirect(reverse('home'))

q = get_object_or_404(Question, id=question_id)

return render(request, 'question.html', { 'question': q, })

render()함수를 이용해서 return 해주고 있지요. 다만 q라는 객체를 렌더링할 때 템플릿해서는 question이라는 이름으로 사용하게끔 설정해주었습니다.

question.html

위에서 q 값을 렌더링해서 보내줄 때 question이라는 이름으로 보내주었습니다. 따라서question.html 파일을 열어 이 question을 사용해서 내용을 출력해주도록 하겠습니다. 아래와 같이 내용을 작성하되 {% block content %} 와 {% endblock %} 사이에 작성하도록 하겠습니다.

<!-- title --><h1> {{ question.title }}</h1>

Page 38: QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

<hr><!-- content --><p> {{ question.content|safe }}</p>

렌더링되어 전달 된 question이라는 객체에는 views.py에서 작업했던 q객체의 값을 가지고 있습니다. 따라서 q객체에 있던 데이터를 불러오는 것입니다.

근데 마지막에 {{ question.content|safe }} 에서 |safe는 뭘까요? 이건 django-summernote 라는 패키지를 설치했는데 그 패키지 사용법을 보면 꼭 이렇게 써주라고 해서 쓴 것일 뿐입니다.^^;

5. 다음 시간에는1. 7주차에서 Front-end 부분을 완성시키겠습니다.2. 8주차에서 Home, Tag, Comment, Popularity를 완성시키고 마지막으로 실서버에

Deploy해보겠습니다.

Written by initialkommit@Study-Bee.

1. 인사이트 출판사, 파이썬 완벽 가이드, p65에서 객체의 생성과 파괴 및 객체의 문자열 표현의 일부를 참조했음 �

2. 블로그 http://pinocc.tistory.com/168를 참고해서 작성했습니다. �