SlideShare uma empresa Scribd logo
1 de 38
Baixar para ler offline
6주차: 회원가입/로그인폼, Post, 글
보기
NOTE
매 주차별 소스는 https://github.com/studybee/webDevForBeginner 에서 다운로드받
을 수 있습니다. git를 사용할 줄 아는 분들은 git 를, 잘 모르겠으면 우측 하단에 있는
Download zip file 버튼을 눌러 다운로드 받아 사용하시기 바랍니다.
1. 회원가입 Form
우리는 지금까지 Django에서 기본적으로 제공하는 User 모델을 사용하고 있습니다. 또한 Django
에서 기본적으로 제공하는 UserCreationForm을 이용해 회원가입 폼을 만들었습니다. 다만
UserCreationForm에는 Username, Password1, Password2만 입력할 수 있도록 되어있습니다.
여기에서 우리는 Email을 추가해보도록 하겠습니다. 어렵게 만들지 말고 최대한 Django에서 제공
하는 것을 토대로 만드는 것으로 하겠습니다. 그리고 Django에서 이미 만들어 놓은 좋은 기능을
그대로 사용하는것이 좋습니다.
앞으로 forms.py에서 다루게 될 내용은 Django에서 만들어 놓은 Class의 메소드와 속성을 재정
의해서 사용할 것입니다. 이를 Overriding이라고 합니다. 객체지향의 중요한 특징 중 하나이지요.
더불어 Overriding과 Overloading을 헷갈리시면 안됩니다. Overloading은 이번 스터디에 안나오
니 인터넷에서 한 번 찾아보시길 권해드립니다.
1. forms.py
사용자에게 특정값을 입력받기 위해선 필수적으로 HTML의 Form 태그를 사용한다고 했습니다.
이 때 사용자가 입력한 값을 검증 등의 작업이 더 있습니다. 이를 간편하게 해결해줄 수 있는 것이
바로 Django의 Form이라는 기능입니다.
우리는 Django에서 이미 만들어 제공하는 것(User 모델, UserCreationForm)을 최대한 이용하도
록 하겠습니다. 그러기 위해서 우선 Django에서 제공하는 UserCreationForm을 상속받아야 합니
다. 아래와 같이 따라 실행해보겠습니다.
1) Editor > forms.py 파일 신규 생성
qna 앱 안에 forms.py 를 새로 만듭니다. 그리고 앞으로 이 곳에 Form 관련된 내용을 입력하도
록 하겠습니다. 그리고 후에 views.py 에서 forms.py 에서 만든 폼 클래스를 불러와 사용하도
록 하겠습니다.
2) Django의 form을 사용하겠다고 선언
Django의 form 기능을 사용할 것이므로 당연히 먼저 django 패키지안의 forms 모듈을 사용하겠
다고 말해줘야 합니다. 아래와 같이 작성해봅시다.
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의 form 두 가지 중 하
나인 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의 구조
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 클래스의 괄호 안에 여러가지가 들어가 있습니다. 이를 Field
Argument라고 하는데 여기에 들어가는 Argument는 아래와 같습니다. 이는 공식 문서의 내용을
정리해서 가져왔습니다.
Argument 명 내용 Default
required 필수 입력 True
label 필드의 Human-friendly 라벨 필드명
initial 필드의 최초값 없음
widget HTML로 문서의 표현 부분 각 폼 필드마다 다름
help_text 필드의 도움말 없음
error_messages 필드가 에러가 났을 때의 메시지 에러마다 다름
validators 제약 조건 설정 없음
localize 필드 데이터의 지역화 없음
Argument 명 내용 Default
위 인자 중 우리는 두 가지만 사용했습니다. - required, widget. required는 써주지 않아도 디폴트
로 True이지만 한 번 사용하고 싶어서 써봤습니다.ㅎㅎㅎ
문제는 Widget입니다. 위에서 설명드린대로 이는 HTML로 표현하는 부분입니다. 만약 Username
을 입력받고 싶다면 폼 필드가 CharField 이고, Widget 필드는 TextInput 입니다. 하지만
Password를 입력바도 싶다면 폼 필드가 CharField 이고, Widget 필드는 PasswordInput 이
됩니다. 데이터베이스에는 똑같이 스트링으로 저장되지만 화면상에 나오는 필드는 비밀번호 형
식이어야 하기 때문입니다. 그렇다면 먼저 폼 필드에는 무엇이 있는지 보겠습니다.
Built-in Field Classes
폼 필드명 Default Widget Field Model Field
BooleanField CheckboxInput BooleanField
CharField TextInput CharField
DecimalField NumberInput DecimalField
EmailField EmailInput EmailField
공식 문서에 들어가보면 위 4개 보다 더 많다는 것을 알 수 있습니다. 그런데 좀 헷갈리지요. 폼 필
드, 위젯 필드, 모델 필드. 폼 필드와 위젯 필드는 짝꿍입니다. 폼 필드 가는 곳에 위젯 필드가 항상
같이 있습니다. 폼 필드는 사용자로 부터 값을 입력받을 수 있도록 설정하는 클래스 인데 그 안에
HTML로는 어떤 걸 쓰겠냐 라고 물어볼 때 쓰는게 바로 위젯 필드입니다.
그리고 우리가 사용하는 ModelForm은 Model과도 연결이 되어있는데 그 이유는 사용자에게 값을
입력받고 바로 데이터베이스에 저장할 수 있게 하기 위해서입니다. 따라서 모델 필드와도 연결되
어 있습니다.
위 사항들은 공식 문서에 자세히 나와있습니다.
Widget
그렇다면 위젯 필드에는 무엇이 있을까요!?
위젯 필드 HTML
TextInput <input type="text" ...>
NumberInput <input type="number" ...>
URLInput <input type="url" ...>
EmailInput <input type="email" ...>
PasswordInput <input type="password" ...>
Textarea <extarea> ... </textarea>
위젯 필드 HTML
앞에서도 말씀드렸지만 위젯 필드는 HTML로 보여지는 부분을 담당합니다. 따라서 HTML 코드를
만들어주는데 위 테이블에는 각 필드마다 어떻게 표현되는지 나와있습니다. 더 많은 내용은 공식
문서를 참고하시기 바랍니다.
attrs 속성
우리는 attrs라는 argument로 EmailField의 속성을 줄 수 있습니다. 이는 HTML에 보여지는 태그
의 속성을 정의하는 것입니다.
widget=forms.EmailInput(
attrs={
'class': 'form-control',
'placeholder': 'Email',
'required': 'True',
}
)
EmailField 안의 attrs라는 Argument로 HTML의 속성을 아래와 같이 정의해주었습니다.
1. class: form-control
2. placeholder: Email
3. required: True
위 속성을 HTML로 변환하면 아래와 같이 변환됩니다.
<input type="email" class="form-control" placeholder="Email"
required="True"/>
위 세 가지 속성이 이렇게 HTML의 속성으로 정의가 되는 것입니다. 그 중 1번의 form-
control 은 Bootstrap에서 Inputbox를 예쁘게 바꿔주는 CSS 클래스입니다.
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
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) 추가하기
이 소스에서 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
verification.")
class Meta:
model = User
fields = ("username", "email", "password1", "password2",)
email필드를 작성했던 것과 마찬가지로 widget에 attrs 으로 속성값을 설정한 것을 알 수 있습
니다.
2. views.py
1) 소스 수정
기존 소스는 아래와 같이 되어있을 것입니다. 이는 UserCreationForm을 사용하는 것입니다.
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from 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을 사용할 수 있도록 수정하겠습니다.
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from 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 -> SignupForm
2. is_valid()(유효성 검사) 이후 email추가로 저장하기
위 2번, 즉 is_valid() 유효성 검사 이후에 하는 행동들은 아래와 같습니다.
signupform.cleaned_data[‘email’]
1. self
클래스로부터 인스턴스를 만들 때 해당 인스턴스 이름이 뭔지는 모르겠지만 우선
self로 부르자! 라는 뜻을 가지고 있습니다. 즉 만들어진 인스턴스를 가리킵니다.
2. cleaned_data
Form은 is_valid() 함수를 통해 사용자가 입력한 값이 문제가 있는지 없는지 체크를
합니다. 그리고 체크가 없을 경우 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 에 있던 폼 내용을 복사하여 우리 상황에 맞게 수정한 것입니다.
<!-- 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-
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>
여기선 아래 세 가지를 살펴보겠습니다.
{{ signupform.username.label_tag }}
views.py 에서 signupform이라는 이름으로 보낸 signupform 기억하시죠? 이 객체에는 사
용자가 입력할 수 있도록 username, email, password1, password2를 만들어 놓았었습니다. 그
리고 여기서는 username을 먼저 보여주는데 Django의 form은 항상 세 가지로 짝지어 있습니다.
1. Label
2. 폼태그(에. <input type="text" ... /> )
3. 에러 메시지
이렇게 세 가지를 작성하는 것인데, 첫번째로 label을 프린트하는 것입니다. {{
signupform.username.label }} 이렇게만 해도 되는데 label_tag 라고 한 이유는 단순히
label 만 적으면 HTML 태그의 <ul> 태그가 붙여져 나와서 그 태그를 없애라는 의미의 기능입
니다.
{{ signupform.username }}
이제 위에서 말한 세 가지 중 두 번째 인 폼태그입니다. 이 부분이 바로 사용자가 입력할 수 있도록
폼태그를 생성하는 부분입니다. 아무것도 입력하지 않고 바로 필드명만 적어주면 됩니다.
{{ signupform.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
2) LoginForm 클래스 생성
class LoginForm(AuthenticationForm):
3) 클래스 내용 추가하기
우리는 기본적으로 제공하는 것에 조금 수정해서 사용할 것입니다. 따라서 우선 수정할 부분만 복
사해오겠습니다. django.contrib.auth.forms 파일에 가서 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에 이미 만들어져 있고 우리는 상속받아왔기 때문
에 그 기능을 그대로 사용할 수 있는 것입니다.
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을 사용하겠다고 말한 것입니다. 그리
고 이 urlpattern의 이름을 login_url 이라는 이름으로 설정합니다.
logout
호스트명/logout 으로 접속했을 경우 django에서 이미 만들어 놓은 logout 함수로 이동을 합
니다. 함수로 전달해주는 인자값은 next_page 이라는 이름에 /login/ 라는 값을 전달해줍니
다. 즉 로그아웃하고 나서 이동하는 페이지를 설정한 것입니다. 그리고 이 urlpattern의 이름을
logout_url 이라는 이름으로 설정합니다.
urls.py 전체 소스
만들어진 전체 소스는 아래와 같습니다.
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.views.generic import TemplateView
from 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 양식을
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>
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 %}
<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에서 제공해주는 것을 이용해 금방 끝냈습니다. 아래부터
는 우리가 모델부터 직접만들어 사용하도록 하겠습니다.
3. Post
이번에는 새로운 글을 작성하는 것을 만들어 보겠습니다. 우리가 위에서 했던 방식과 차이는 별로
없습니다만, 이번에는 Django에서 기본적으로 제공해주는 모델이 없으므로 새로운 글이 저장될
수 있는 모델을 만들도록 하겠습니다. 이 모델은 데이터베이스의 테이블과 같다는 것을 잊으시면
안됩니다.
1. models.py
위 MTV 패턴을 기억하시나요? 우리는 이제 글을 작성해서 담을 수 있는 그릇을 models.py 에서
만들 것입니다.
1) 모델 디자인
사용자가 글을 작성하면 그 글이 어딘가에 저장이 됩니다. 그 어딘가를 정의하는 곳이 바로
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)
필드타입
모델의 경우 이렇게 필드명 = models.필드타입() 이런 식으로 선언합니다. 그리고 그 필드타
입의 경우 아래와 같이 여러가지가 있습니다. 이는 공식문서를 참고하였습니다.
필드명 설명
AutoField IntegerField that automatically increments
BooleanField true/false
CharField string field
DateTimeField date and time
DecimalField fixed-precision decimalnumber
EmailField valid emailaddress
IntegerField Values from -2147483648 to 2147483647
SmallIntegerField Values from -32768 to 32767
BigIntegerField Values from -9223372036854775808 to 9223372036854775807
PositiveIntegerField Values from 0 to 2147483647
TextField Large text
Relationship Field
그런데 우리는 위에서 User 모델을 사용할 때 1:Many 관계를 사용했습니다. 이를 Relationship
field라고 부릅니다. 이런 필드는 아래와 같이 세 가지가 있습니다. 공식 문서를 참고했습니다.
1. ForeignKey
2. ManyToManyField
3. OneToOneField
Question 모델에 대한 속성 설정
class Question(models.Model):
# 중략
class Meta:
ordering = ['-updated_at']
Meta Option
Python에는 메타 클래스라는 것이 있습니다. Data about data 라는 뜻입니다. 그리고 메타 클래
스에는 들어가는 옵션을 Django에서 지정해주고 있습니다. 이를 Meta Option이라고 합니다. 공
식 문서를 찾아보면 아래와 같은 것들이 있습니다.
1. app_label
2. db_table
3. ordering
4. 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
__str__ 더 간결하며 사용자에게 정보를 제공하는 문자열 반환, 비공식적인 문자열
__unicode__ 유니코드 문자열을 반환
특수 메소드 설명
우리는 unicode 함수를 사용했는데 해당 클래스의 인스턴스를 출력(print)할 때 이 객체의 내용을
출력할 수 있도록 해 놓은 것입니다.
String formatting
u"{0}".format(self.title)
인스턴의 내용을 출력하는데 format 함수를 사용했습니다. 이는 치환자라고도 합니다. Python
문법이다. 좌측의 값중 0번째 인덱스 값을 format함수의 0번째 인자값으로 치환해준다는 뜻입니
다.
자세한 건 공식 문서를 참고하시기 바랍니다.
완성된 소스
from django.db import models
from 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
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 을 열어서 아래와 같이 작성합니다.
from django_summernote.widgets import SummernoteWidget
from qna.models import Question
우리가 만들 PostForm은 Question 모델을 기본으로 하기 때문에 Question까지 불러왔습니다.
3) PostForm클래스 만들기
아래와 같이 작성합니다.
class PostForm(forms.ModelForm):
django의 forms에서 제공하는 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(
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,
})
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)
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
#중략
@login_required
def 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">
{{ 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'),
4. 작성한 글 보기
우리는 위에서 글을 작성하는 것을 구현해보았습니다. 이제는 그 작성한 글을 볼 수 있는 페이지를
만들어 보겠습니다. 잘 생각해보면 이 부분은 딱 하나만 필요합니다. 원하는 데이터를 가져오는 것
이지요.
1. models.py
위에서 말씀드렸지요. 원하는 데이터를 가져오는 것만 필요하다고요! ㅋㅋ 그렇기 때문에 데이터
를 담는 그릇인 모델 부분은 여기에서 필요가 없습니다.
2. forms.py
위에서 말씀드렸지요. 원하는 데이터를 가져오는 부분만 필요하다고요!^^ forms는 사용자에게
입력을 받을 때에만 사용하는 것이기 때문에 이 부분에서는 forms도 필요없습니다.
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로 접속했을 때 실행하게 될 함수에 보내지
는 값을 의미합니다. 영문자 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 을 default
로 하라는 것입니다.
question_id가 0일 때
그런데 생각해보면 question_id 가 0일 때는 없을 것입니다. 왜냐면 여기서 말하는
question_id 는 Django에서 autoincrement되는 글 번호인데 항상 1이상이기 때문이지요. 그
렇기 때문에 악의적인 뜻을 가진 사용자가 URL에 호스트명/question/0 이라고 입력했을 경우
를 대비해 0이 들어오면 첫 화면으로 가게끔 설정하도록 하겠습니다.
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
함수를 사용할 것입니다. 이 함수는 데이터를 불러오는데 데이터가 있으면 상관은 없지만 만약 없
다면 404 에러 메시지를 보여주라는 함수입니다. 404 에러 메시지는 해당 페이지를 못 찾았을 경
우 나오는 메시지입니다. 위 함수는 공식 문서를 참고하세요.
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'))
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>
<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를 참고해서 작성했습니다.

Mais conteúdo relacionado

Mais procurados

QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃Kwangyoun Jung
 
Django를 Django답게, Django로 뉴스 사이트 만들기
Django를 Django답게, Django로 뉴스 사이트 만들기Django를 Django답게, Django로 뉴스 사이트 만들기
Django를 Django답게, Django로 뉴스 사이트 만들기Kyoung Up Jung
 
Django로 쇼핑몰 만들자
Django로 쇼핑몰 만들자Django로 쇼핑몰 만들자
Django로 쇼핑몰 만들자Kyoung Up Jung
 
회사에서 써보는 SQLAlchemy
회사에서 써보는 SQLAlchemy회사에서 써보는 SQLAlchemy
회사에서 써보는 SQLAlchemyJc Kim
 
챗봇 시작해보기
챗봇 시작해보기챗봇 시작해보기
챗봇 시작해보기성일 한
 
처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2성일 한
 
Django in Production
Django in ProductionDjango in Production
Django in ProductionHyun-woo Park
 
처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4성일 한
 
처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3성일 한
 
처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1성일 한
 
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축Youngil Cho
 
Html5&css 3장
Html5&css 3장Html5&css 3장
Html5&css 3장홍준 김
 
Python 으로 Slackbot 개발하기
Python 으로 Slackbot 개발하기Python 으로 Slackbot 개발하기
Python 으로 Slackbot 개발하기성일 한
 
파이썬 플라스크로 배우는 웹프로그래밍 #3 (ABCD)
파이썬 플라스크로 배우는 웹프로그래밍 #3 (ABCD)파이썬 플라스크로 배우는 웹프로그래밍 #3 (ABCD)
파이썬 플라스크로 배우는 웹프로그래밍 #3 (ABCD)성일 한
 
[NEXT] Android 개발 경험 프로젝트 3일차 (Database)
 [NEXT] Android 개발 경험 프로젝트 3일차 (Database) [NEXT] Android 개발 경험 프로젝트 3일차 (Database)
[NEXT] Android 개발 경험 프로젝트 3일차 (Database)YoungSu Son
 
레거시 시스템에 Django 들이밀기
레거시 시스템에 Django 들이밀기레거시 시스템에 Django 들이밀기
레거시 시스템에 Django 들이밀기Jiyong Jung
 
[하코사세미나] 한 시간 만에 배우는 Jquery
[하코사세미나] 한 시간 만에 배우는 Jquery[하코사세미나] 한 시간 만에 배우는 Jquery
[하코사세미나] 한 시간 만에 배우는 Jquery정석 양
 
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)Youngil Cho
 

Mais procurados (20)

QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
 
Django를 Django답게, Django로 뉴스 사이트 만들기
Django를 Django답게, Django로 뉴스 사이트 만들기Django를 Django답게, Django로 뉴스 사이트 만들기
Django를 Django답게, Django로 뉴스 사이트 만들기
 
Django로 쇼핑몰 만들자
Django로 쇼핑몰 만들자Django로 쇼핑몰 만들자
Django로 쇼핑몰 만들자
 
회사에서 써보는 SQLAlchemy
회사에서 써보는 SQLAlchemy회사에서 써보는 SQLAlchemy
회사에서 써보는 SQLAlchemy
 
플라스크 템플릿
플라스크 템플릿플라스크 템플릿
플라스크 템플릿
 
챗봇 시작해보기
챗봇 시작해보기챗봇 시작해보기
챗봇 시작해보기
 
처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2처음배우는 자바스크립트, 제이쿼리 #2
처음배우는 자바스크립트, 제이쿼리 #2
 
Django in Production
Django in ProductionDjango in Production
Django in Production
 
처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4
 
처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3
 
처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1
 
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
2017 Pycon KR - Django/AWS 를 이용한 쇼핑몰 서비스 구축
 
Html5&css 3장
Html5&css 3장Html5&css 3장
Html5&css 3장
 
Python 으로 Slackbot 개발하기
Python 으로 Slackbot 개발하기Python 으로 Slackbot 개발하기
Python 으로 Slackbot 개발하기
 
파이썬 플라스크로 배우는 웹프로그래밍 #3 (ABCD)
파이썬 플라스크로 배우는 웹프로그래밍 #3 (ABCD)파이썬 플라스크로 배우는 웹프로그래밍 #3 (ABCD)
파이썬 플라스크로 배우는 웹프로그래밍 #3 (ABCD)
 
[NEXT] Android 개발 경험 프로젝트 3일차 (Database)
 [NEXT] Android 개발 경험 프로젝트 3일차 (Database) [NEXT] Android 개발 경험 프로젝트 3일차 (Database)
[NEXT] Android 개발 경험 프로젝트 3일차 (Database)
 
레거시 시스템에 Django 들이밀기
레거시 시스템에 Django 들이밀기레거시 시스템에 Django 들이밀기
레거시 시스템에 Django 들이밀기
 
Handlebars
HandlebarsHandlebars
Handlebars
 
[하코사세미나] 한 시간 만에 배우는 Jquery
[하코사세미나] 한 시간 만에 배우는 Jquery[하코사세미나] 한 시간 만에 배우는 Jquery
[하코사세미나] 한 시간 만에 배우는 Jquery
 
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
Python/Django를 이용한 쇼핑몰 구축(2018 4월 Django Girls Seoul)
 

Semelhante a QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기

파이썬 플라스크 이해하기
파이썬 플라스크 이해하기 파이썬 플라스크 이해하기
파이썬 플라스크 이해하기 Yong Joon Moon
 
안드로이드기초
안드로이드기초안드로이드기초
안드로이드기초hylo926
 
Rails style-guide-2
Rails style-guide-2Rails style-guide-2
Rails style-guide-2Yunho Jo
 
Servlet design pattern
Servlet design patternServlet design pattern
Servlet design patternSukjin Yun
 
Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Nam Hyeonuk
 
SJBoard Project Portfolio
SJBoard Project PortfolioSJBoard Project Portfolio
SJBoard Project PortfolioJuyoungKang7
 
06.실행환경 실습교재(easy company,해답)
06.실행환경 실습교재(easy company,해답)06.실행환경 실습교재(easy company,해답)
06.실행환경 실습교재(easy company,해답)Hankyo
 
Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Ryan Park
 
[C언어]함수오버로딩과오버라이딩
[C언어]함수오버로딩과오버라이딩[C언어]함수오버로딩과오버라이딩
[C언어]함수오버로딩과오버라이딩jusingame
 
Programming java day2
Programming java day2Programming java day2
Programming java day2Jaehoonyam
 
Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4성연 김
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)익성 조
 
스프링시큐리티와 소셜연습 이해를 위한 글
스프링시큐리티와 소셜연습 이해를 위한 글스프링시큐리티와 소셜연습 이해를 위한 글
스프링시큐리티와 소셜연습 이해를 위한 글라한사 아
 
[Toolcon2014] WebStorm에서 자바스크립트 리팩토링하기
[Toolcon2014] WebStorm에서 자바스크립트 리팩토링하기[Toolcon2014] WebStorm에서 자바스크립트 리팩토링하기
[Toolcon2014] WebStorm에서 자바스크립트 리팩토링하기우영 주
 

Semelhante a QnA Blog Using Django - 회원가임/로그인폼, Post, 글보기 (20)

파이썬 플라스크 이해하기
파이썬 플라스크 이해하기 파이썬 플라스크 이해하기
파이썬 플라스크 이해하기
 
안드로이드기초
안드로이드기초안드로이드기초
안드로이드기초
 
Xe hack
Xe hackXe hack
Xe hack
 
Rails style-guide-2
Rails style-guide-2Rails style-guide-2
Rails style-guide-2
 
Servlet design pattern
Servlet design patternServlet design pattern
Servlet design pattern
 
Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약Effective c++ chapter 1,2 요약
Effective c++ chapter 1,2 요약
 
SJBoard Project Portfolio
SJBoard Project PortfolioSJBoard Project Portfolio
SJBoard Project Portfolio
 
딥러닝이 바꾸는 애자일 테스팅
딥러닝이 바꾸는 애자일 테스팅딥러닝이 바꾸는 애자일 테스팅
딥러닝이 바꾸는 애자일 테스팅
 
06.실행환경 실습교재(easy company,해답)
06.실행환경 실습교재(easy company,해답)06.실행환경 실습교재(easy company,해답)
06.실행환경 실습교재(easy company,해답)
 
Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005
 
[C언어]함수오버로딩과오버라이딩
[C언어]함수오버로딩과오버라이딩[C언어]함수오버로딩과오버라이딩
[C언어]함수오버로딩과오버라이딩
 
Programming java day2
Programming java day2Programming java day2
Programming java day2
 
Meteor2015 codelab
Meteor2015 codelab Meteor2015 codelab
Meteor2015 codelab
 
Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)
 
Web_07_Rails Advanced
Web_07_Rails AdvancedWeb_07_Rails Advanced
Web_07_Rails Advanced
 
스프링시큐리티와 소셜연습 이해를 위한 글
스프링시큐리티와 소셜연습 이해를 위한 글스프링시큐리티와 소셜연습 이해를 위한 글
스프링시큐리티와 소셜연습 이해를 위한 글
 
3-2. selector api
3-2. selector api3-2. selector api
3-2. selector api
 
2. template
2. template2. template
2. template
 
[Toolcon2014] WebStorm에서 자바스크립트 리팩토링하기
[Toolcon2014] WebStorm에서 자바스크립트 리팩토링하기[Toolcon2014] WebStorm에서 자바스크립트 리팩토링하기
[Toolcon2014] WebStorm에서 자바스크립트 리팩토링하기
 

Mais de Kwangyoun Jung

장고로 웹서비스 만들기 기초
장고로 웹서비스 만들기   기초장고로 웹서비스 만들기   기초
장고로 웹서비스 만들기 기초Kwangyoun Jung
 
Virtual Development Environment Setting
Virtual Development Environment SettingVirtual Development Environment Setting
Virtual Development Environment SettingKwangyoun Jung
 
문과생 대상 파이썬을 활용한 데이터 분석 강의
문과생 대상 파이썬을 활용한 데이터 분석 강의문과생 대상 파이썬을 활용한 데이터 분석 강의
문과생 대상 파이썬을 활용한 데이터 분석 강의Kwangyoun Jung
 
Python+anaconda Development Environment
Python+anaconda Development EnvironmentPython+anaconda Development Environment
Python+anaconda Development EnvironmentKwangyoun Jung
 
Python, Development Environment for Windows
Python, Development Environment for WindowsPython, Development Environment for Windows
Python, Development Environment for WindowsKwangyoun Jung
 

Mais de Kwangyoun Jung (7)

python and database
python and databasepython and database
python and database
 
장고로 웹서비스 만들기 기초
장고로 웹서비스 만들기   기초장고로 웹서비스 만들기   기초
장고로 웹서비스 만들기 기초
 
Light Tutorial Python
Light Tutorial PythonLight Tutorial Python
Light Tutorial Python
 
Virtual Development Environment Setting
Virtual Development Environment SettingVirtual Development Environment Setting
Virtual Development Environment Setting
 
문과생 대상 파이썬을 활용한 데이터 분석 강의
문과생 대상 파이썬을 활용한 데이터 분석 강의문과생 대상 파이썬을 활용한 데이터 분석 강의
문과생 대상 파이썬을 활용한 데이터 분석 강의
 
Python+anaconda Development Environment
Python+anaconda Development EnvironmentPython+anaconda Development Environment
Python+anaconda Development Environment
 
Python, Development Environment for Windows
Python, Development Environment for WindowsPython, Development Environment for Windows
Python, Development Environment for Windows
 

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

  • 1. 6주차: 회원가입/로그인폼, Post, 글 보기 NOTE 매 주차별 소스는 https://github.com/studybee/webDevForBeginner 에서 다운로드받 을 수 있습니다. git를 사용할 줄 아는 분들은 git 를, 잘 모르겠으면 우측 하단에 있는 Download zip file 버튼을 눌러 다운로드 받아 사용하시기 바랍니다. 1. 회원가입 Form 우리는 지금까지 Django에서 기본적으로 제공하는 User 모델을 사용하고 있습니다. 또한 Django 에서 기본적으로 제공하는 UserCreationForm을 이용해 회원가입 폼을 만들었습니다. 다만 UserCreationForm에는 Username, Password1, Password2만 입력할 수 있도록 되어있습니다. 여기에서 우리는 Email을 추가해보도록 하겠습니다. 어렵게 만들지 말고 최대한 Django에서 제공 하는 것을 토대로 만드는 것으로 하겠습니다. 그리고 Django에서 이미 만들어 놓은 좋은 기능을 그대로 사용하는것이 좋습니다. 앞으로 forms.py에서 다루게 될 내용은 Django에서 만들어 놓은 Class의 메소드와 속성을 재정 의해서 사용할 것입니다. 이를 Overriding이라고 합니다. 객체지향의 중요한 특징 중 하나이지요. 더불어 Overriding과 Overloading을 헷갈리시면 안됩니다. Overloading은 이번 스터디에 안나오 니 인터넷에서 한 번 찾아보시길 권해드립니다. 1. forms.py 사용자에게 특정값을 입력받기 위해선 필수적으로 HTML의 Form 태그를 사용한다고 했습니다. 이 때 사용자가 입력한 값을 검증 등의 작업이 더 있습니다. 이를 간편하게 해결해줄 수 있는 것이 바로 Django의 Form이라는 기능입니다. 우리는 Django에서 이미 만들어 제공하는 것(User 모델, UserCreationForm)을 최대한 이용하도 록 하겠습니다. 그러기 위해서 우선 Django에서 제공하는 UserCreationForm을 상속받아야 합니 다. 아래와 같이 따라 실행해보겠습니다.
  • 2. 1) Editor > forms.py 파일 신규 생성 qna 앱 안에 forms.py 를 새로 만듭니다. 그리고 앞으로 이 곳에 Form 관련된 내용을 입력하도 록 하겠습니다. 그리고 후에 views.py 에서 forms.py 에서 만든 폼 클래스를 불러와 사용하도 록 하겠습니다. 2) Django의 form을 사용하겠다고 선언 Django의 form 기능을 사용할 것이므로 당연히 먼저 django 패키지안의 forms 모듈을 사용하겠 다고 말해줘야 합니다. 아래와 같이 작성해봅시다. 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의 form 두 가지 중 하
  • 3. 나인 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의 구조
  • 4. 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 클래스의 괄호 안에 여러가지가 들어가 있습니다. 이를 Field Argument라고 하는데 여기에 들어가는 Argument는 아래와 같습니다. 이는 공식 문서의 내용을 정리해서 가져왔습니다. Argument 명 내용 Default required 필수 입력 True label 필드의 Human-friendly 라벨 필드명 initial 필드의 최초값 없음 widget HTML로 문서의 표현 부분 각 폼 필드마다 다름 help_text 필드의 도움말 없음 error_messages 필드가 에러가 났을 때의 메시지 에러마다 다름 validators 제약 조건 설정 없음
  • 5. localize 필드 데이터의 지역화 없음 Argument 명 내용 Default 위 인자 중 우리는 두 가지만 사용했습니다. - required, widget. required는 써주지 않아도 디폴트 로 True이지만 한 번 사용하고 싶어서 써봤습니다.ㅎㅎㅎ 문제는 Widget입니다. 위에서 설명드린대로 이는 HTML로 표현하는 부분입니다. 만약 Username 을 입력받고 싶다면 폼 필드가 CharField 이고, Widget 필드는 TextInput 입니다. 하지만 Password를 입력바도 싶다면 폼 필드가 CharField 이고, Widget 필드는 PasswordInput 이 됩니다. 데이터베이스에는 똑같이 스트링으로 저장되지만 화면상에 나오는 필드는 비밀번호 형 식이어야 하기 때문입니다. 그렇다면 먼저 폼 필드에는 무엇이 있는지 보겠습니다. Built-in Field Classes 폼 필드명 Default Widget Field Model Field BooleanField CheckboxInput BooleanField CharField TextInput CharField DecimalField NumberInput DecimalField EmailField EmailInput EmailField 공식 문서에 들어가보면 위 4개 보다 더 많다는 것을 알 수 있습니다. 그런데 좀 헷갈리지요. 폼 필 드, 위젯 필드, 모델 필드. 폼 필드와 위젯 필드는 짝꿍입니다. 폼 필드 가는 곳에 위젯 필드가 항상 같이 있습니다. 폼 필드는 사용자로 부터 값을 입력받을 수 있도록 설정하는 클래스 인데 그 안에 HTML로는 어떤 걸 쓰겠냐 라고 물어볼 때 쓰는게 바로 위젯 필드입니다. 그리고 우리가 사용하는 ModelForm은 Model과도 연결이 되어있는데 그 이유는 사용자에게 값을 입력받고 바로 데이터베이스에 저장할 수 있게 하기 위해서입니다. 따라서 모델 필드와도 연결되 어 있습니다. 위 사항들은 공식 문서에 자세히 나와있습니다. Widget 그렇다면 위젯 필드에는 무엇이 있을까요!? 위젯 필드 HTML TextInput <input type="text" ...>
  • 6. NumberInput <input type="number" ...> URLInput <input type="url" ...> EmailInput <input type="email" ...> PasswordInput <input type="password" ...> Textarea <extarea> ... </textarea> 위젯 필드 HTML 앞에서도 말씀드렸지만 위젯 필드는 HTML로 보여지는 부분을 담당합니다. 따라서 HTML 코드를 만들어주는데 위 테이블에는 각 필드마다 어떻게 표현되는지 나와있습니다. 더 많은 내용은 공식 문서를 참고하시기 바랍니다. attrs 속성 우리는 attrs라는 argument로 EmailField의 속성을 줄 수 있습니다. 이는 HTML에 보여지는 태그 의 속성을 정의하는 것입니다. widget=forms.EmailInput( attrs={ 'class': 'form-control', 'placeholder': 'Email', 'required': 'True', } ) EmailField 안의 attrs라는 Argument로 HTML의 속성을 아래와 같이 정의해주었습니다. 1. class: form-control 2. placeholder: Email 3. required: True 위 속성을 HTML로 변환하면 아래와 같이 변환됩니다. <input type="email" class="form-control" placeholder="Email" required="True"/> 위 세 가지 속성이 이렇게 HTML의 속성으로 정의가 되는 것입니다. 그 중 1번의 form- control 은 Bootstrap에서 Inputbox를 예쁘게 바꿔주는 CSS 클래스입니다.
  • 7. 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
  • 8. 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) 추가하기
  • 9. 이 소스에서 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
  • 10. verification.") class Meta: model = User fields = ("username", "email", "password1", "password2",) email필드를 작성했던 것과 마찬가지로 widget에 attrs 으로 속성값을 설정한 것을 알 수 있습 니다. 2. views.py 1) 소스 수정 기존 소스는 아래와 같이 되어있을 것입니다. 이는 UserCreationForm을 사용하는 것입니다. from django.shortcuts import render from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse from 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을 사용할 수 있도록 수정하겠습니다.
  • 11. from django.shortcuts import render from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse from 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 -> SignupForm 2. is_valid()(유효성 검사) 이후 email추가로 저장하기 위 2번, 즉 is_valid() 유효성 검사 이후에 하는 행동들은 아래와 같습니다. signupform.cleaned_data[‘email’] 1. self 클래스로부터 인스턴스를 만들 때 해당 인스턴스 이름이 뭔지는 모르겠지만 우선 self로 부르자! 라는 뜻을 가지고 있습니다. 즉 만들어진 인스턴스를 가리킵니다. 2. cleaned_data Form은 is_valid() 함수를 통해 사용자가 입력한 값이 문제가 있는지 없는지 체크를
  • 12. 합니다. 그리고 체크가 없을 경우 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 에 있던 폼 내용을 복사하여 우리 상황에 맞게 수정한 것입니다.
  • 13. <!-- 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-
  • 14. 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> 여기선 아래 세 가지를 살펴보겠습니다. {{ signupform.username.label_tag }} views.py 에서 signupform이라는 이름으로 보낸 signupform 기억하시죠? 이 객체에는 사 용자가 입력할 수 있도록 username, email, password1, password2를 만들어 놓았었습니다. 그 리고 여기서는 username을 먼저 보여주는데 Django의 form은 항상 세 가지로 짝지어 있습니다. 1. Label 2. 폼태그(에. <input type="text" ... /> ) 3. 에러 메시지 이렇게 세 가지를 작성하는 것인데, 첫번째로 label을 프린트하는 것입니다. {{ signupform.username.label }} 이렇게만 해도 되는데 label_tag 라고 한 이유는 단순히 label 만 적으면 HTML 태그의 <ul> 태그가 붙여져 나와서 그 태그를 없애라는 의미의 기능입 니다. {{ signupform.username }}
  • 15. 이제 위에서 말한 세 가지 중 두 번째 인 폼태그입니다. 이 부분이 바로 사용자가 입력할 수 있도록 폼태그를 생성하는 부분입니다. 아무것도 입력하지 않고 바로 필드명만 적어주면 됩니다. {{ signupform.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
  • 16. 2) LoginForm 클래스 생성 class LoginForm(AuthenticationForm): 3) 클래스 내용 추가하기 우리는 기본적으로 제공하는 것에 조금 수정해서 사용할 것입니다. 따라서 우선 수정할 부분만 복 사해오겠습니다. django.contrib.auth.forms 파일에 가서 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에 이미 만들어져 있고 우리는 상속받아왔기 때문
  • 17. 에 그 기능을 그대로 사용할 수 있는 것입니다. 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을 사용하겠다고 말한 것입니다. 그리
  • 18. 고 이 urlpattern의 이름을 login_url 이라는 이름으로 설정합니다. logout 호스트명/logout 으로 접속했을 경우 django에서 이미 만들어 놓은 logout 함수로 이동을 합 니다. 함수로 전달해주는 인자값은 next_page 이라는 이름에 /login/ 라는 값을 전달해줍니 다. 즉 로그아웃하고 나서 이동하는 페이지를 설정한 것입니다. 그리고 이 urlpattern의 이름을 logout_url 이라는 이름으로 설정합니다. urls.py 전체 소스 만들어진 전체 소스는 아래와 같습니다. from django.conf.urls import patterns, include, url from django.contrib import admin from django.views.generic import TemplateView from 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 양식을
  • 19. 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>
  • 20. 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 %}
  • 21. <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에서 제공해주는 것을 이용해 금방 끝냈습니다. 아래부터 는 우리가 모델부터 직접만들어 사용하도록 하겠습니다.
  • 22. 3. Post 이번에는 새로운 글을 작성하는 것을 만들어 보겠습니다. 우리가 위에서 했던 방식과 차이는 별로 없습니다만, 이번에는 Django에서 기본적으로 제공해주는 모델이 없으므로 새로운 글이 저장될 수 있는 모델을 만들도록 하겠습니다. 이 모델은 데이터베이스의 테이블과 같다는 것을 잊으시면 안됩니다. 1. models.py 위 MTV 패턴을 기억하시나요? 우리는 이제 글을 작성해서 담을 수 있는 그릇을 models.py 에서 만들 것입니다. 1) 모델 디자인
  • 23. 사용자가 글을 작성하면 그 글이 어딘가에 저장이 됩니다. 그 어딘가를 정의하는 곳이 바로 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)
  • 24. 필드타입 모델의 경우 이렇게 필드명 = models.필드타입() 이런 식으로 선언합니다. 그리고 그 필드타 입의 경우 아래와 같이 여러가지가 있습니다. 이는 공식문서를 참고하였습니다. 필드명 설명 AutoField IntegerField that automatically increments BooleanField true/false CharField string field DateTimeField date and time DecimalField fixed-precision decimalnumber EmailField valid emailaddress IntegerField Values from -2147483648 to 2147483647 SmallIntegerField Values from -32768 to 32767 BigIntegerField Values from -9223372036854775808 to 9223372036854775807 PositiveIntegerField Values from 0 to 2147483647 TextField Large text Relationship Field 그런데 우리는 위에서 User 모델을 사용할 때 1:Many 관계를 사용했습니다. 이를 Relationship field라고 부릅니다. 이런 필드는 아래와 같이 세 가지가 있습니다. 공식 문서를 참고했습니다. 1. ForeignKey 2. ManyToManyField 3. OneToOneField Question 모델에 대한 속성 설정 class Question(models.Model): # 중략 class Meta: ordering = ['-updated_at']
  • 25. Meta Option Python에는 메타 클래스라는 것이 있습니다. Data about data 라는 뜻입니다. 그리고 메타 클래 스에는 들어가는 옵션을 Django에서 지정해주고 있습니다. 이를 Meta Option이라고 합니다. 공 식 문서를 찾아보면 아래와 같은 것들이 있습니다. 1. app_label 2. db_table 3. ordering 4. 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
  • 26. __str__ 더 간결하며 사용자에게 정보를 제공하는 문자열 반환, 비공식적인 문자열 __unicode__ 유니코드 문자열을 반환 특수 메소드 설명 우리는 unicode 함수를 사용했는데 해당 클래스의 인스턴스를 출력(print)할 때 이 객체의 내용을 출력할 수 있도록 해 놓은 것입니다. String formatting u"{0}".format(self.title) 인스턴의 내용을 출력하는데 format 함수를 사용했습니다. 이는 치환자라고도 합니다. Python 문법이다. 좌측의 값중 0번째 인덱스 값을 format함수의 0번째 인자값으로 치환해준다는 뜻입니 다. 자세한 건 공식 문서를 참고하시기 바랍니다. 완성된 소스 from django.db import models from 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
  • 27. 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 을 열어서 아래와 같이 작성합니다.
  • 28. from django_summernote.widgets import SummernoteWidget from qna.models import Question 우리가 만들 PostForm은 Question 모델을 기본으로 하기 때문에 Question까지 불러왔습니다. 3) PostForm클래스 만들기 아래와 같이 작성합니다. class PostForm(forms.ModelForm): django의 forms에서 제공하는 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(
  • 29. 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, })
  • 30. 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)
  • 31. 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
  • 32. #중략 @login_required def 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">
  • 33. {{ 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'),
  • 34. 4. 작성한 글 보기 우리는 위에서 글을 작성하는 것을 구현해보았습니다. 이제는 그 작성한 글을 볼 수 있는 페이지를 만들어 보겠습니다. 잘 생각해보면 이 부분은 딱 하나만 필요합니다. 원하는 데이터를 가져오는 것 이지요. 1. models.py 위에서 말씀드렸지요. 원하는 데이터를 가져오는 것만 필요하다고요! ㅋㅋ 그렇기 때문에 데이터 를 담는 그릇인 모델 부분은 여기에서 필요가 없습니다. 2. forms.py 위에서 말씀드렸지요. 원하는 데이터를 가져오는 부분만 필요하다고요!^^ forms는 사용자에게 입력을 받을 때에만 사용하는 것이기 때문에 이 부분에서는 forms도 필요없습니다. 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로 접속했을 때 실행하게 될 함수에 보내지
  • 35. 는 값을 의미합니다. 영문자 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 을 default 로 하라는 것입니다. question_id가 0일 때 그런데 생각해보면 question_id 가 0일 때는 없을 것입니다. 왜냐면 여기서 말하는 question_id 는 Django에서 autoincrement되는 글 번호인데 항상 1이상이기 때문이지요. 그 렇기 때문에 악의적인 뜻을 가진 사용자가 URL에 호스트명/question/0 이라고 입력했을 경우 를 대비해 0이 들어오면 첫 화면으로 가게끔 설정하도록 하겠습니다.
  • 36. 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 함수를 사용할 것입니다. 이 함수는 데이터를 불러오는데 데이터가 있으면 상관은 없지만 만약 없 다면 404 에러 메시지를 보여주라는 함수입니다. 404 에러 메시지는 해당 페이지를 못 찾았을 경 우 나오는 메시지입니다. 위 함수는 공식 문서를 참고하세요. 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'))
  • 37. 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>
  • 38. <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를 참고해서 작성했습니다.