Django로 설문조사 앱 만들기 - part 1 ~ 4

6 minute read

간단한 설문조사(Polls) 어플리케이션 만들기

장고 앱 작성하기, part 1

사용 명령어

  • virtualenv 디렉토리명(venv) 가상환경 생성
  • .\Scripts\activate 가상환경 실행
  • pip install django django 설치
  • django-admin startproject 디렉토리명 Django project를 실행할 폴더 생성
  • django-admin startapp 이름(polls, user, order, project…) app생성
  • python manage.py runserver 서버 실행
  • python manage.py runserver 8080 포트8080으로 서버 시작

1. views.py

2. urls.py

- view 호출을 위해 URLconf를 생성
  • polls/urls.py
urlpatterns = [
    path('', views.index, name='index'),
]
  • mysite/urls.py
urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]

3. path() 함수 인수에 대해

  • 필수 인수 : route, view
  • 선택 인수 : kwargs, name
from django.urls import path

path(route, view, kwargs=None, name=None)
  • route: URL pattern을 가진 문자열
    • 도메인 이름 이후 요청된 URL을 각 패턴과 리스트의 순서대로 비교
    • 각 pattern들은 GET/POST의 매개변수 검색x
  • view
    • 일치하는 패턴을 찾으면, HttpRequest 객체를 첫번째 인수로,
    • view함수 호출
  • kwargs
  • name
    • URL에 이름을 부여 -> Django 어디서나 명확히 참조


장고 앱 작성하기, part 2

사용 명령어

  • python manage.py makemigrations polls 모델의 변경(생성)을 migration으로 저장
  • python manage.py migrate 데이터베이스 테이블 만들기, 변경사항 적용
  • python manage.py sqlmigrate polls 0001 migration이름을 인수로 받아, 실행하는 SQL 문장을 보여줌
  • python manage.py shell Python 쉘에서 Django가 접근할 수 있는 Python 모듈 경로를 그대로 사용가능 - 시험용
  • python manage.py createsuperuser 관리자 생성하기
  • python manage.py runserver 개발 서버 시작

1. 기본 어플리케이션

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',         # 관리용 사이트. 곧 사용하게 될 겁니다.
    'django.contrib.auth',          # 인증 시스템.
    'django.contrib.contenttypes',  # 컨텐츠 타입을 위한 프레임워크.
    'django.contrib.sessions',      # 세션 프레임워크.
    'django.contrib.messages',      # 메세징 프레임워크.
    'django.contrib.staticfiles',   # 정적 파일을 관리하는 프레임워크.
]

2. Django 모델 API

1. INSERT

데이타를 삽입하기 위해서는 먼저 테이블에 해당하는 모델(Model Class)로부터 객체를 생성하고, 그 객체의 save() 메서드를 호출하면 된다. save() 메서드가 호출되면, SQL의 INSERT이 생성되고 실행되어 테이블에 데이타가 추가된다.

2. SELECT

Django는 디폴트로 모델 틀래스에 대해 “objects”라는 Manager(django.db.models.Manager) 객체를 자동으로 추가한다.

  • 사용 :ModelClass.objects.QueryMethod
  • Query란?
    • 데이터 베이스에 정보를 요청하는 것.
    • 쿼리는 웹 서버에 특정한 정보를 보여달라는 웹 클라이언트 요청(주로 문자열을 기반으로 한 요청)에 의한 처리이다.
  • 자주 사용되는 주요 메서드

    • all() : 테이블 데이터를 전부 가져오기
      ex) User 테이블의 모든 데이터의 id와 name 컬럼을 출력
    for u in User.objects.all():
        s += str(u.id) + ' : ' + u.name + '\n'
    
    • get() : 하나의 Row만을 가져오기 위한 메서드
      ex) Primary Key(일반적으로 id 컬럼)가 1인 row를 가져오기
    row = User.objects.get(pk=1)    #pk = primary key
    print(row.name)
    
    • filter() : 특정 조건에 맞는 Row들을 가져오기 위한 메서드
      ex) name 필드가 Lee인 데이터만 가져오기
    rows = User.objects.filter(name='Lee')
    
    • exclude() : 특정 조건을 제외한 나머지 Row 가져오기 위한 메서드
      ex) name 필드가 Lee가 아닌 데이터만 가져오기
    rows = User.objects.exclude(name='Lee')
    
    • count() : 데이터의 갯수(row 수)를 세기위한 메서드
    n = User.objects.count()
    
    • order_by() : 데이터를 key에 따라 정렬. 인자로 정렬키를 나열할수 있고, 앞에 -가 붙으면 내림차순이다.
      ex) id 올림차순, date 내림차순으로 정렬
    rows = User.objects.order_by('id','-date')
    
    • distinct() : 중복된 값은 하나로만 표시하기 위한 메서드. SQL의 SELECT DISTINCT와 같은 효과
      ex) name 필드가 중복되는 경우 한번만 표시
    rows = User.objects.distinct('name')
    
    • first() : 데이터들 중 처음에 있는 row만 return
      ex) name 필드 정렬 후 처음 row를 return 하기
    rows = User.objects.order_by('name').first()
    
    • last() : 데이터들 중 마지막에 있는 row를 return
      ex) name 필드 정렬 후 마지막 row return
    rows = User.objects.order_by('name').last()
    

3. UPDATE

데이터를 수정하기 위해 먼저 수정할 Row 객체를 얻은 후 변경할 필드들을 수정한다.
이어 마지막에 save() 메서드를 호출하면, SQL의 UPDATE가 실행되어 테이블에 데이터가 갱신된다.
ex) id=1인 User 객체의 이름을 변경하기

tmp = User.objects.get(pk=1)
tmp.name = 'Kim'
tmp.save()

4. DELETE

데이터를 삭제하기 위해서는 먼저 삭제할 Row 객체를 얻은 후 delete()메서드를 호출한다. ex) id=2인 User객체 삭제

user1 = User.objects.get(pk=2)
user1.delete()

Link : 참고사이트

5. Model API에 대한 몇가지 정리 사항

  • choice_set 등장

    • 구현한적 없는 q.choice_set.all() 라는 메서드가 등장.
    • 이는 기본적으로 객체에 접근할 수 있는 Manager 객체의 이름은 모델명(소문자)_set 으로 지어진다고 한다.
      ex) class Choice 의 Manager 객체 = choice_set
    q.choice_set.all()
    q.choice_set.create(choice_text='Not much', votes=0)
    q.choice_set.count()
    
  • Field lookups : SQL의 WHERE 절에 해당하는 부분이다.

    • QuerySet methods인 filter(), exclude(), get() 에 키워드 인자의 형태로 전달된다.
    • 형태 : field__lookupType=조건값
      Question.objects.get(pub_date__year=current_year)
      Choice.objects.filter(question__pub_date__year=current_year)
      

3. Django 관리자 사이트

python manage.py createsuperuser
관리자 사이트에서 편집 가능한 그룹과 사용자와 같은 몇 종류의 컨텐츠를 볼 수 있다. 이것들은 django.contrib.auth 모듈에서 제공되는데, Django 에서 제공되는 인증 프레임워크이다.

  • 관리 사이트에서 poll app 을 변경가능하도록 만들기 polls/admin.py
from django.contrib import admin
from .models import Question

admin.site.register(Question)


장고 앱 작성하기, part 3

View 만들기 - 만들 페이지

우리가 만드는 poll 어플리케이션에서 다음과 같은 네개의 view 를 만들어 본다.

  1. 질문 “색인” 페이지 – 최근의 질문들을 표시합니다.
  2. 질문 “세부” 페이지 – 질문 내용과, 투표할 수 있는 서식을 표시합니다.
  3. 질문 “결과” 페이지 – 특정 질문에 대한 결과를 표시합니다
  4. 투표 기능 – 특정 질문에 대해 특정 선택을 할 수 있는 투표 기능을 제공합니다.

view

  • polls/views.py 에 뷰를 추가 -> polls.urls 연결
  • 참고 사항
    • <int:question_id>에서 괄호를 사용하여 URL 의 일부를 “캡처”하고, 해당 내용을 keyword 인수로서 뷰 함수로 전달
    • :question_id> 부분: 일치되는 패턴을 구별하기 위해 정의한 이름
    • <int: 부분: 어느 패턴이 해당 URL 경로에 일치되어야 하는 지를 결정하는 컨버터
  • Django에서 각 뷰에 필요한 것은 HttpResponse 객체 혹은 예외

템플릿 네임스페이싱

템플릿을 polls/templates 이 아닌 polls/templates/polls/index.html 에 넣는 이유
-> Django 는 name이 match되는 첫번째 template를 선택하는데, 만약 다른 application에 동일한 이름의 template이 있으면 두 templates의 구별이 불가능 할 수 있다. 따라서, application 이름의 다른 directory안으로 namespacing 함으로 확실히 구별해 두는 것이 좋다.

지름길 : render()

템플릿에 context 를 채워넣어 표현한 결과를 HttpResponse 객체와 함께 돌려주는 구문은 자주 쓰인다. polls/views.py

from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))  #template을 불러온 후 context를 전달

↓ 단축 기능(shortcuts) 사용

from django.shortcuts import render
def index(request):
    return render(request, 'polls/index.html', context)

지름길 : get_object_or_404()

from django.http import Http404
from django.shortcuts import render

from .models import Question
# ...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

↓ 단축 기능(shortcuts) 사용

from django.shortcuts import get_object_or_404, render

from .models import Question

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

객체가 존재 하지 않으면 Http404 예외 발생, get_list_or_404()도 비슷한 동작

템플릿 시스템

  1. 하드코딩하지 않기

    • 하드코딩된 접근 방식의 문제 : 많은 템플릿을 가진 프로젝트들의 URL을 바꾸는 게 어려운 일이 됨
    • {% url %} template 태그를 사용하여 url 설정에 정의된 특정한 URL 경로들의 의존성을 제거할 수 있음
  2. URL의 이름공간 정하기

    • Django가 {% url %} 템플릿태그를 사용할 때, 어떤 앱의 뷰에서 URL을 생성했는지 알 수 있도록!
    • URLconf에 이름공간(namespace)을 추가
app_name = 'polls'


장고 앱 작성하기, part 4

form 요소


<input
  type="radio"
  name="choice"
  id="choice{{ forloop.counter }}"
  value="{{ choice.id }}"
/>

form을 submit -> POST 데이터 전송 -> choice=# 전달

  • request.POST : 키로 전송된 자료에 접근할 수 있도록 해주는 사전과 같은 객체 (항상 문자열 값)
  • request.POST[‘choice’] : 선택된 설문의 ID를 문자열로 반환

reverse() 함수

reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)

from news import views

path('archive/', views.archive, name='news-archive')

# using the named URL
reverse('news-archive')

# passing a callable object
# (This is discouraged because you can't reverse namespaced views this way.)
from news import views
reverse(views.archive)

from django.urls import reverse

def myview(request):
    return HttpResponseRedirect(reverse('arch-summary', args=[1945]))
>>> reverse('admin:app_list', kwargs={'app_label': 'auth'})
'/admin/auth/'

generic view 사용

여태껏 사용했던 뷰의 변화

  1. HttpResponse를 이용한 함수형 뷰
    • 함수형뷰를 사용해 template을 읽어 HttpResponse로 응답
    • 번거롭고 복잡, 가장 기초적인 부분
  2. render를 이용한 함수형 뷰
    • index뷰를 shortcuts.render를 사용하여 보다 간결하게 표현
    • template = … 를 없앰
    • HttpResponse로 리턴하던 것이 render함수로 대체
    • render함수의 패키지 이름이 왜 shortcuts인지 의문이 풀림
  3. 제네릭 뷰 시스템(Generic views system)
    • 위의 반복되는 공통 부분을 패턴화하여 사용하기 쉽게 추상화
    • 최소한 그 뷰가 어떤 모델을 사용할 것인지만 지정하면, 나머진 모두 상위 클래스(List generic view, DetailView 등)가 모든 걸 알아서 해준다.
    • 필요에 따라 각 뷰마다 달라지는 값을 넣어주기만 하면 됨 -> 상속을 사용할 수 있는 클래스 뷰를 사용하는 힘(이유)

ListView와 DetailView

  1. DetailView : “특정 개체 유형에 대한 세부 정보 페이지 표시”

    • URL에서 캡쳐 된 기본 키 값이 “pk” 라고 기대. 따라서 question_id를 제너릭 뷰를 위해 pk로 변경
    • 기본적으로 DetailView 제너릭 뷰는 /\_detail.html 템플릿을 사용
    • template_name 속성을 이용해 특정 템플릿 이름을 사용하도록 알려줄 수 있음
  2. ListView : “개체 목록 표시”

    • ListView 제네릭 뷰는 /\_list.html 템플릿을 기본으로 사용
    • template_name 속성을 이용

Leave a comment