1. django form 서론

django 에서는 form 이라는 편리한 도구를 제공합니다. models.py에서 작성한 모델을 바탕으로 템플릿 파일에서 models.py의 model에 맞게 폼을 형성해주는 것입니다. 물론 django에서 제공하기에 views.py에서도 연동하여 이용할 수 있습니다. 여러가지 귀찮은 검사를 django에게 맡길 수 있습니다. 그리고 복잡한 html 코드를 단순하게 줄여줍니다.


django form documentation : https://docs.djangoproject.com/en/2.0/topics/forms/


2. django form 유무에 따른 html 코드 비교 

먼저 form을 사용하지 않았을 때의 html 코드입니다.

<form action="." method="post">
    {% csrf_token %}
    <div>
        <label for="name">username:</label>
        <input type="text" id="name" />
    </div>
    <div>
        <label for="mail">E-mail:</label>
        <input type="email" id="mail" />
    </div>
    <div>
        <label for="password">password:</label>
        <textarea id="password"></textarea>
    </div>
     <div>    
        <label for="password_confirm">password_confirm:</label>
        <textarea id="password_confirm"></textarea>
    </div>
    <div class="button">
        <button type="submit">Submit</button>
    </div>
</form>
cs


하나의 입력 폼을 작성할 때 두 줄의 코드를 필요로 하게 됩니다.


이번엔 form을 사용했을 때의 코드입니다.


<form class="form-horizontal" method="post" action=".">
 
  {% csrf_token %}
 
 
  <div class="form-group">
    <label class="control-label col-sm-2" for="id_username">Username(ID)</label>
    <div class="col-sm-10">{{ form.username }}</div>
  </div>
  <div class="form-group">
    <label class="control-label col-sm-2" for="id_email">Email</label>
    <div class="col-sm-10">
      {{ form.email }}
    </div>
  </div>
  <div class="form-group">
    <label class="control-label col-xs-2 col-sm-2" for="id_password">Password</label>
    <div class="col-sm-10">
      {{ form.password }}
    </div>
  </div>
  <div class="form-group">
    <label class="control-label col-xs-2 col-sm-2" for="id_password_confirm">Password</label>
    <div class="col-sm-10">
      {{ form.password_confirm }}
    </div>
  </div>
 
  <div class="form-group">
    <div class="col-xs-offset-5 col-sm-offset-5">
      <button class="btn btn-default" type="submit">Create</button>
    </div>
  </div>
</form>
cs


얼핏 보면 더 코드가 증가한 것 같지만 부트스트랩을 이용하기 위해 들어간 코드들을 제외하면 훨씬 더 간결해졌음을 알 수 있습니다.

특히 하나의 입력 폼이 {{ form.email }} 처럼 짧아지게 됩니다. 코드의 재사용시에도 {{ form.email }} 같이 짧게 이용할 수 있으니 효율적입니다.


3. models.py

form을 적용할 모델을 정하겠습니다. 위의 form은 기본 User모델을 참고로 만들었으므로 User모델을 확인해보겠습니다.

class User(AbstractUser):
cs


이러므로 이번엔 AbstractUser를 살펴보겠습니다.


class AbstractUser(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
    )
    email = models.EmailField(_('email address'), blank=True)
cs


AbstractBaseUser 까지 살펴보면 이렇습니다.


@python_2_unicode_compatible
class AbstractBaseUser(models.Model):
    password = models.CharField(_('password'), max_length=128)
cs


결국 username과 email, password 까지 위에서 사용된 모든 필드를 찾을 수 있었습니다. 

이 필드들이 바탕이 되어 forms.py에서 형성되게 됩니다. 각각 CharField와 EmailField가 사용되었습니다.


4. forms.py

forms.py 에서는 사용할 모델을 가져오거나 django 의 form 을 가져와줘야 합니다.

from django import forms
from django.contrib.auth.models import User
cs

그 다음에 필요한 폼을 작성하시면 됩니다.


class UserCreateForm(forms.ModelForm):
    username = forms.CharField(widget=forms.TextInput(attrs={'class''form-control ''placeholder''Username'}))
    email = forms.EmailField(widget=forms.EmailInput(attrs={'class''form-control''placeholder''email'}))
    password = forms.CharField(widget=forms.PasswordInput(attrs={'class''form-control''placeholder''Password'}))
    password_confirm = forms.CharField(widget=forms.PasswordInput(attrs={'class''form-control',
                                                                         'placeholder''Password_confirm'}))
 
    class Meta:
        model = User
        fields = ['username''email''password']
 
cs


이렇게 작성했습니다. 위에서 언급한 것처럼 CharField와 EmailField가 그대로 사용되었습니다. widget은 html에서 어떤 입력창을 사용하는지를 결정합니다. 그 옆에 괄호 안의 'class', 'placeholder'는 그대로 html에서 반영되는 요소들입니다. 원하는대로 적용해주세요. 저는 부트스트랩을 이용중이니 'class'에 'form-control'을 추가해줬습니다.


4. views.py


views.py의 def create(request): 에서 이 form을 사용한다고 해 봅시다. 그렇게 되면 얼마나 편리한지 알 수 있게 됩니다.


def create(request):
    if request.method == 'POST':
 
        form = UserCreateForm(request.POST)
 
        username = form.data['username']
        email = form.data['email']
        password = form.data['password']
        password_confirm = form.data['password_confirm']
        if form.is_valid:
            pass
    else:
        form = UserCreateForm()
        return render(request, 'create.html', {'form': form})
cs


이게 다입니다. 폼은 request와 함께 views.py로 전송되는데 그 폼을 읽기 위해서 단지 form = UserCreateForm(request.POST) 이 한 줄이 필요합니다. 그 다음엔 form.is_valid 같은 유효성 검사도 django에서 다 지원해주니 원하시는 대로 사용하시면 됩니다.



django channels 는 웹소켓을 django project 에서 사용할 수 있게 해주는 도구입니다. 웹소켓 관련 유명 도구로는 Socket.io 가 있습니다. 그러나 사용언어는 파이썬이 아닌 자바스크립트입니다. channels는 최근 정식으로 django project 안에 들어가게 되었습니다. 

django project 의 channels 공식 채택 : https://www.djangoproject.com/weblog/2016/sep/09/channels-adopted-official-django-project/

channels 의 공식문서 : https://channels.readthedocs.io/en/latest/


1. channels를 쓰기위한 준비


django channels를 쓰기 위해선 몇 가지 파이썬 패키지를 다운받아야 합니다.

그 전에 파이썬 가상환경을 설정합니다.


$ mkdir example-channels
$ cd example-channels
$ python3.6 -m venv myenv
$ source myenv/bin/activate
cs


그 다음에 pip를 이용해서 패키지를 다운받습니다.


(myenv)$ pip install django==1.10.5 channels==1.0.2 asgi_redis==1.0.0
cs


asgi_redis는 redis를 쓸 경우에 필요하겠지만 이 경우엔 redis를 다운받아서 실행되는지 확인해보고 설치하는 것을 추천합니다. 그렇지 않다면 asgi_redis는 굳이 다운받지 않아도 됩니다. 다른 방법으로도 실행되게 할 수 있습니다. 만약 실행된다면 프로젝트와 앱을 생성합니다. 


(myenv)$ django-admin.py startproject example
(myenv)$ cd example
(myenv)$ python manage.py startapp exam
(myenv)$ python manage.py migrate
cs


2. django 설정


그 후 settings.py 에서 exam 앱과 channels를 등록합니다.


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels',
    'exam',
]
cs


settings.py 에 DATABASE 설정과 모양이 비슷한 설정을 넣어줍니다.


CHANNEL_LAYERS = {
    'default': {
        'BACKEND''asgi_redis.RedisChannelLayer',
        'ROUTING''exam.routing.channel_routing',
    }
}
cs


여기에 등록한대로 exam 앱에 routing.py를 만들어서 channel_routing을 만들어줍니다. 

그 전에 consumers.py는 이렇게 해줍니다. consumers.py는 exam앱 안에 위치시키는 것을 추천합니다.


from channels import Group
 
def ws_connect(message):
    Group('users').add(message.reply_channel)
 
def ws_disconnect(message):
    Group('users').discard(message.reply_channel)   
cs


그 다음에 routing.py를 만들어줘야 합니다. urls.py와 비슷한 역할을 수행합니다. websocket용 urls.py 라고 생각하시면 됩니다. 


from channels.routing import route
from exam.consumers import ws_connect, ws_disconnect
 
 
channel_routing = [
    route('websocket.connect', ws_connect),
    route('websocket.disconnect', ws_disconnect),
]   
cs


3. client에서 실행


이제 jquery를 이용해 websocket을 클라이언트에서 실행해줍니다. 클라이언트는 django 의 templates에 위치한 html 파일에서 동작합니다.


  <script>
    var socket = new WebSocket('ws://' + window.location.host + '/users/');
 
    socket.onopen = function open() {
      console.log('created.');
    };
 
    if (socket.readyState == WebSocket.OPEN) {
      socket.onopen();
    }
  </script>
cs


기본적인 설정이 끝났습니다. runserver 한 후 클라이언트 상에서 위 페이지에 접속하면 로그가 뜰 것입니다.


 WebSocket HANDSHAKING /users/
 WebSocket DISCONNECT /users/
cs


이런식으로 나오면 됩니다.

django 는 모델을 만들고 그 모델을 이용해 object를 만듭니다. object를 가져오는 방법은 여러가지가 있습니다. 그 중 object가 존재하지 않을 경우의 에러를 다루는 방법으로는 DoesNotExist의 도움을 받는 방법을 고를 수 있습니다.

from django.shortcuts import get_object_or_404, get_list_or_404
 
cs

물론 이런 shortcuts 를 가져와서 처리하는 방법도 있습니다. 하지만 이번엔 DoesNotExist를 써 보겠습니다.


1. DoesNotExist 사용방법

try:
    model_your_model = ModelYourModel.objects.get(you=happy)
except ModelYourModel.DoesNotExist:
    pass
cs


이렇게 DoesNotExist 를 이용해서 예외 구문을 만들게 됩니다.

pass 라고 쓴 자리에 예외가 발생할 경우 하고 싶은 걸 작성하면 됩니다. 


2. Backend 사용방법


그리고 django에서 authenticate 함수를 쓸 일이 있습니다. 이건 인증 함수입니다. settings.py에서 그 흔적을 찾을 수 있습니다.


AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
)
cs

이 부분입니다. 이 부분이 django 의 authentication 을 관장합니다. 이 ModelBackend를 살펴보겠습니다.

 

class ModelBackend:
 
    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        try:
            user = UserModel._default_manager.get_by_natural_key(username)
        except UserModel.DoesNotExist:
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user
 
    def get_user(self, user_id):
        try:
            user = UserModel._default_manager.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None
        return user if self.user_can_authenticate(user) else None
cs


authenticate 함수가 username 과 password 를 받아서 user를 return 하는 것을 알 수 있습니다. 

그러므로 이 backend를 받은 다음에 authenticate 함수를 재정의해 주면 인증에 사용할 수 있게 됩니다.


from django.contrib.auth.backends import ModelBackend
 
class CustomAuthBackend(ModelBackend):
 
    def authenticate(self, username=None, password=None):
        pass
cs


이런 식으로 모양을 맞춰주면 쓸만할 것입니다. 원하는대로 수정하여 사용가능합니다. 비밀번호만을 이용한 로그인도 구현할 수 있을 것입니다.

+ Recent posts