Skip to content

4-3. 사용자 어플리케이션 작성

startapp polls

이전 장에서 Polls 어플리케이션을 설계할 때 3개의 화면 페이지가 필요했습니다. 이 3개의 페이지에 대해서 필요한 뷰 와 템플릿을 작성하겠습니다. 화면을 작성하기 위해서 처리흐름에 대한 로직이 설계되어야 합니다. 여기서는 4개의 URL와 뷰와 3개의 템플릿 파일이 필요합니다.

ch04-03_01.png 그림4-3. URL 구성

4-3-1. URL 설계

다음은 요청 URL 과 장고 View를 매핑한 리스트입니다. 각 뷰에서 처리하는 내용을 잘 이해해야 합니다.

URL 패턴뷰 이름뷰가 처리하는 내용
/polls/index()index.html 템플릿을 보여 줍니다.
/polls/1/detail()detail.html 템플릿을 보여 줍니다.
/polls/1/votevote()detail.html의 폼을 POST 방식으로 처리합니다.
/polls/1/resultsresults()results.html 템플릿을 보여 줍니다.
/admin/(기본제공)관리자 사이트를 보여 줍니다. (장고제공)

4-3-2. 어플리케이션 생성

다음은 파이썬 startapp 명령어로 어플리케이션을 생성하도록 하겠습니다. polls라는 어플리케이션 명을 입력합니다.

windows command 
C:\hyungsik74\pycharm\> cd polls-project
C:\hyungsik74\pycharm\polls-project> cd 
C:\hyungsik74\pycharm\polls-project
C:\hyungsik74\pycharm\polls-project> py manage.py startapp polls
linux command 
$/home/hyungsik74/pycharm> cd polls-project
$/home/hyungsik74/pycharm/polls-project> pwd
/home/hyungsik74/pycharm/polls-project
$/home/hyungsik74/pycharm/polls-project> python manage.py startapp polls 

어플리케이션 생성후 디렉토리 구조입니다.

hyungsik74
└─ pycharm
  └─ polls-project
    ├─ config
    |  ├─ __init__.py
    |  ├─ asgi.py
    |  ├─ settings.py
    |  ├─ urls.py
    |  └─ wsgi.py
    ├─ polls
    |  ├─ migrations
    │  |  ├─ __init__.py
    │  |  └─ 0001_initial.py
    |  ├─ templates
    │  |  ├─ polls
    |  │  |  ├─ detail.html
    |  │  |  ├─ index.html
    |  │  |  └─ results.html
    |  ├─ __init__.py
    |  ├─ admin.py
    |  ├─ apps.py
    |  ├─ models.py
    |  ├─ tests.py
    |  ├─ urls.py
    |  └─ views.py
    ├─ db.sqlite3
    └─ manage.py
hyungsik74
└─ pycharm
  └─ polls-project
    ├─ config
    |  ├─ __init__.py
    |  ├─ asgi.py
    |  ├─ settings.py
    |  ├─ urls.py
    |  └─ wsgi.py
    ├─ polls
    |  ├─ migrations
    │  |  ├─ __init__.py
    │  |  └─ 0001_initial.py
    |  ├─ templates
    │  |  ├─ polls
    |  │  |  ├─ detail.html
    |  │  |  ├─ index.html
    |  │  |  └─ results.html
    |  ├─ __init__.py
    |  ├─ admin.py
    |  ├─ apps.py
    |  ├─ models.py
    |  ├─ tests.py
    |  ├─ urls.py
    |  └─ views.py
    ├─ db.sqlite3
    └─ manage.py

프로젝트 설정 파일(settings.py) 추가

py
# Application definition

INSTALLED_APPS = [
    'polls.apps.PollsConfig',  # 추가`
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
# Application definition

INSTALLED_APPS = [
    'polls.apps.PollsConfig',  # 추가`
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

4-3-3. 설문조사 데이터모델 생성

데이터모델 생성은 장고 프레임웍에서 데이터베이스 관련 테이블 등을 생성을 하는 단계입니다. 다음의 순서대로 생성합니다.

windows command 
C:\hyungsik74\pycharm\polls-project> cd polls
C:\hyungsik74\pycharm\polls-projectpolls> cd 
C:\hyungsik74\pycharm\polls-projectpolls
C:\hyungsik74\pycharm\polls-project\polls> notepad models.py   // 테이블을 정의함
C:\hyungsik74\pycharm\polls-project\polls> notepad admin.py   // 관리자화면에 표시
C:\hyungsik74\pycharm\polls-project\polls> cd ..
C:\hyungsik74\pycharm\polls-project
C:\hyungsik74\pycharm\polls-project> py manage.py makemigrations polls  // DB 변경사항
C:\hyungsik74\pycharm\polls-project> py manage.py sqlmigrate polls 0001

C:\hyungsik74\pycharm\polls-project> py manage.py migrate // DB 변경사항 반영 C:\hyungsik74\pycharm\polls-project> py manage.py runserver // 실행

linux command
$/home/hyungsik74/pycharm/polls-project> cd polls
$/home/hyungsik74/pycharm/polls-project/polls> pwd
/home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> vi models.py
$/home/hyungsik74/pycharm/polls-project/polls> vi admin.py
$/home/hyungsik74/pycharm/polls-project/polls> cd ..
$/home/hyungsik74/pycharm/polls-project> pwd
/home/hyungsik74/pycharm/polls-project
$/home/hyungsik74/pycharm/polls-project> python manage.py makemigrations polls
$/home/hyungsik74/pycharm/polls-project> python manage.py migrate
$/home/hyungsik74/pycharm/polls-project> python manage.py runserver
py
from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField("date published")

    def __str__(self):
        return self.question_text

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text
from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField("date published")

    def __str__(self):
        return self.question_text

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text
py
from django.contrib import admin

from .models import Question, Choice

admin.site.register(Question)
admin.site.register(Choice)
from django.contrib import admin

from .models import Question, Choice

admin.site.register(Question)
admin.site.register(Choice)

지금까지 작성한 내용을 확인하기 위해서, 프로젝트를 실행합니다. 관리자 페이지에 접속하면, 다음과 같이 새로 생성한 2개의 테이블을 확인 할 수 있습니다.

*Admin (http://127.0.0.1:8000/admin) 사이트 로그인 후 첫 페이지

ch04-03-03_01.png 그림4-03-01. 관리자 Polls 테이블 생성 화면

4-3-4. URL Config

이전 장에서 처리 흐름에 따라 URL 및 View를 설계했습니다. URL config라 함은 이런 URL 과 뷰를 연결해 주는 작업입니다. 관리자 페이지를 포함해서 5개의 URL 과 뷰가 필요합니다.

windows command 
C:\hyungsik74\pycharm\polls-project\config> cd 
C:\hyungsik74\pycharm\polls-project\config
C:\hyungsik74\pycharm\polls-project\config> notepad urls.py
C:\hyungsik74\pycharm\polls-project\config> cd ..\polls
C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> cd
C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> notepad urls.py
linux command
$/home/hyungsik74/pycharm/polls-project> cd /home/hyungsik74/pycharm/polls-project/config
$/home/hyungsik74/pycharm/polls-project/confing> pwd
/home/hyungsik74/pycharm/polls-project/config
$/home/hyungsik74/pycharm/polls-project/config> vi urls.py
$/home/hyungsik74/pycharm/polls-project> cd /home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> pwd
/home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> vi urls.py
py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("polls/", include("polls.urls")),
    path("admin/", admin.site.urls),
]
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("polls/", include("polls.urls")),
    path("admin/", admin.site.urls),
]
py
from django.urls import path

from . import views

app_name = "polls"
urlpatterns = [
    path("", views.index, name="index"),
    path("<int:question_id>/", views.detail, name="detail"),
    path("<int:question_id>/results/", views.results, name="results"),
    path("<int:question_id>/vote/", views.vote, name="vote"),
]
from django.urls import path

from . import views

app_name = "polls"
urlpatterns = [
    path("", views.index, name="index"),
    path("<int:question_id>/", views.detail, name="detail"),
    path("<int:question_id>/results/", views.results, name="results"),
    path("<int:question_id>/vote/", views.vote, name="vote"),
]

4-3-5. Tempate

이번 장에서는 하면 UI 에 해당하는 템플릿을 작성하겠습니다.

windows command 
C:\hyungsik74\pycharm\polls-project> cd C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> cd 
C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> mkdir templates
C:\hyungsik74\pycharm\polls-project\polls> mkdir templates\polls
C:\hyungsik74\pycharm\polls-project\polls> cd templates\polls
C:\hyungsik74\pycharm\polls-project\polls\templates\polls> notepad index.html
linux command
$/home/hyungsik74/pycharm/polls-project> cd /home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> pwd
/home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> mkdir templates
$/home/hyungsik74/pycharm/polls-project/polls> mkdir templates/polls
$/home/hyungsik74/pycharm/polls-project/polls> cd templates/polls
$/home/hyungsik74/pycharm/polls-project/polls/templates/polls> vi index.html
html
{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}
{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}
windows command 
C:\hyungsik74\pycharm\polls-project> cd C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> cd 
C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> mkdir templates
C:\hyungsik74\pycharm\polls-project\polls> mkdir templates\polls
C:\hyungsik74\pycharm\polls-project\polls> cd templates\polls
C:\hyungsik74\pycharm\polls-project\polls\templates\polls> notepad index.html
linux command
$/home/hyungsik74/pycharm/polls-project> cd /home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> pwd
/home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> mkdir templates
$/home/hyungsik74/pycharm/polls-project/polls> mkdir templates/polls
$/home/hyungsik74/pycharm/polls-project/polls> cd templates/polls
$/home/hyungsik74/pycharm/polls-project/polls/templates/polls> vi index.html
html
<h1>{{ question.question_text }}</h1>

<!--
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
-->

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
    <legend><h1>{{ question.question_text }}</h1></legend>
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
    {% endfor %}
</fieldset>
<input type="submit" value="Vote">
</form>
<h1>{{ question.question_text }}</h1>

<!--
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
-->

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
    <legend><h1>{{ question.question_text }}</h1></legend>
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    {% for choice in question.choice_set.all %}
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
    {% endfor %}
</fieldset>
<input type="submit" value="Vote">
</form>
windows command 
C:\hyungsik74\pycharm\polls-project> cd C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> cd 
C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> mkdir templates
C:\hyungsik74\pycharm\polls-project\polls> mkdir templates\polls
C:\hyungsik74\pycharm\polls-project\polls> cd templates\polls
C:\hyungsik74\pycharm\polls-project\polls\templates\polls> notepad index.html
linux command
$/home/hyungsik74/pycharm/polls-project> cd /home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> pwd
/home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> mkdir templates
$/home/hyungsik74/pycharm/polls-project/polls> mkdir templates/polls
$/home/hyungsik74/pycharm/polls-project/polls> cd templates/polls
$/home/hyungsik74/pycharm/polls-project/polls/templates/polls> vi index.html
html
<h1>{{ question.question_text }}</h1>

<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

<a href="{% url 'polls:detail' question.id %}">Vote again?</a>
<h1>{{ question.question_text }}</h1>

<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

<a href="{% url 'polls:detail' question.id %}">Vote again?</a>

4-3-6. View

뷰는 polls/views.py 파일에서 아래의 4개 function 에 대해서 작성해야 합니다.

  • index
  • detail
  • vote
  • results
windows command 
C:\hyungsik74\pycharm\polls-project\> cd C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> cd 
C:\hyungsik74\pycharm\polls-project\polls
C:\hyungsik74\pycharm\polls-project\polls> notepad views.py
linux command
$/home/hyungsik74/pycharm/polls-project> cd /home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> pwd
/home/hyungsik74/pycharm/polls-project/polls
$/home/hyungsik74/pycharm/polls-project/polls> vi views.py
py
from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    context = {"latest_question_list": latest_question_list}
    return render(request, "polls/index.html", context)
from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    context = {"latest_question_list": latest_question_list}
    return render(request, "polls/index.html", context)
py
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})
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})
py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse

from .models import Choice, Question


# ...
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST["choice"])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(
            request,
            "polls/detail.html",
            {
                "question": question,
                "error_message": "You didn't select a choice.",
            },
        )
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse

from .models import Choice, Question


# ...
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST["choice"])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(
            request,
            "polls/detail.html",
            {
                "question": question,
                "error_message": "You didn't select a choice.",
            },
        )
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
py
from django.shortcuts import get_object_or_404, render


def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/results.html", {"question": question})
from django.shortcuts import get_object_or_404, render


def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/results.html", {"question": question})
py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.shortcuts import get_object_or_404, render
from .models import Question, Choice

# ...
def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    context = {"latest_question_list": latest_question_list}
    return render(request, "polls/index.html", context)

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

# ...
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST["choice"])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(
            request,
            "polls/detail.html",
            {
                "question": question,
                "error_message": "You didn't select a choice.",
            },
        )
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
# ...
def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/results.html", {"question": question})
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.shortcuts import get_object_or_404, render
from .models import Question, Choice

# ...
def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    context = {"latest_question_list": latest_question_list}
    return render(request, "polls/index.html", context)

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

# ...
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST["choice"])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay the question voting form.
        return render(
            request,
            "polls/detail.html",
            {
                "question": question,
                "error_message": "You didn't select a choice.",
            },
        )
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
# ...
def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/results.html", {"question": question})

4-3-7. 실행하기

TIP

장고 웹 프레임워크를 사용해서 프로젝트를 처음 생성할 때 마다 반듯이 필요한 절차 이므로 꼭 이해하고, 익숙해져야 합니다.

windows command 
C:\hyungsik74\pycharm\polls-project> cd C:\hyungsik74\pycharm\polls-project
C:\hyungsik74\pycharm\polls-project> cd 
C:\hyungsik74\pycharm\polls-project
C:\hyungsik74\pycharm\polls-project> py manage.py runserver
linux command
$/home/hyungsik74/pycharm/polls-project> cd /home/hyungsik74/pycharm/polls-project
$/home/hyungsik74/pycharm/polls-project> pwd
/home/hyungsik74/pycharm/polls-project
$/home/hyungsik74/pycharm/polls-project> python manage.py runserver

ch04-03-07_01.png 그림4-3-7. 사용자 어플리케이션 초기 화면

Released under the MIT License.