• Home
  • About
    • CodeLab photo

      CodeLab

      UnprettyCoder's Blog

    • Learn More
    • Github
  • Posts
    • All Posts
    • Computer Science
      • JAVA
      • C
      • PYTHON
      • JavaScript
      • DATABASE
      • Git
      • nodeJS
    • Industrial Engineering
      • O.R.
      • Statistics
      • E.E.
    • Others
      • NETWORK
      • ACCOUNTANCY
      • OTHERS
  • Projects

Polls Project

24 Mar 2020

Reading time ~6 minutes

Polls Project

1. Create Project

2. Install Django

$ pip install django

3. Django Project start

$ django-admin startproject config .

4. Test Web service

$ python manage.py runserver

5. Create App [polls]

$ python manage.py startapp polls

6. Create First View

polls/views.py

from django.http import httpResponse;

def index(request):
	return httpResponse("Hello, world. You're at the polls index.");

polls/urls.py

from django.urls import path;
from . import views;

urlpatterns = [
	path('', views.index, name='index');
];

path(route, view, kwargs, name)

route : 주소를 의미

view : route주소로 접급했을 때 호출할 뷰

kwargs : 뷰에 전달할 인자들

name : route의 이름을 의미


config/urls.py

from django.contrib import admin;
from django.urls import path, include;

urlpatterns = [
	path('polls/', include('polls.urls/')),
	path('admin/', admin.site.urls)
];

include()

include : 최상위 urls.py가 다른 app에 포함된 urls.py를 참조하도록 하는 함수


$ python manage.py runserver

이후 서버를 실행시키고 첫 화면이 설정한대로 출력되는지 확인

7. Create Database

config/settings.py

[ENGINE] : 어떤 종류의 데이터베이스를 사용할지 결정 (settings.py파일 내부에 존재하는 설정)

django.db.backends.mysql

django.db.backends.sqlite3

django.db.backends.postgresql

django.db.backends.oracle 등을 기본적으로 사용할 수 있다.


$ python manage.py migrate (데이터베이스를 만들고 초기화)

8. Create Model

polls/models.py

from django.db import models;

class Question(models.Model):
	question_text = models.CharField(max_length=200);
	pub_date = models.DateTimeField("date published");
class Choice(models.Model):
	question = models.ForeignKey(Question, on_delete=models.CASCADE);
	choice_text = models.CharField(max_length=200);
	votes = models.IntegerField(default=0);

config/settings.py ([INSTALLED_APPS]에 polls 앱 추가)

INSTALLED_APPS = [
	'polls.apps.PollsConfig',   # 'polls'라고만 써도 무방
	'django.contrib.admin',
	'django.contrib.auth',
	...
];

$ python manage.py makemigrations polls

위 명령으로 앱의 변경사항을 추적해 데이터베이스에 적용할 내용을 만든다.

[결과값은 polls/migrations/0001_initial.py에 기록된다.]

$ python manage.py sqlmigrate polls 0001

$ python manage.py migrate polls 0001

위 두 명령을 실행하면 데이터베이스에 테이블을 생성하고 초기화 할 수 있다.

9. Add function in Model

polls/models.py

from django.db import models;
import datetime;
from django.utils import timezone;

class Question(models.Model):
	question_text = models.CharField(max_length=200);
	pub_date = models.DateTimeField("date published");
	def __str__(self):
		return self.question_text;
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1);
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;

10. Check Administrator Page

$ python manage.py createsuperuser

$ python manage.py runserver

위 명령을 실행하고 127.0.0.1:8000/admin으로 접속하면 로그인 페이지를 확인할 수 있다.

polls/admin.py

from django.contrib import admin;
from .models import Question;

admin.site.register(Question);

다음과 같이 파일을 설정하면 관리자 페이지에 POLLS 항목과 Question이 생성된 것을 확인할 수 있다.

(나머지는 관리자 페이지 UI를 통해 투표를 등록할 수 있다.)

11. Add other Views

투표 목록 : 등록된 투표의 목록을 표시하고 상세 페이지로 이동하는 링크 제공

투표 상세 : 투표의 상세 항목을 보여줌

투표 기능 : 선택한 답변을 반영

투표 결과 : 선택한 답변을 반영한 후 결과를 보여줌


polls/views.py

def details(request, question_id):
	return HttpResponse("You're looking at question %s." %question_id);

def results(request, question_id):
    response = "You're looking at the results of question %s.";
    return HttpResponse(response %question_id);

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." %question_id);

# 아직 특별한 기능없이 값만 출력하는 뷰

polls/urls.py

from django.urls import path;
from . import views;

urlpartterns = [
	# ex : /polls/
	path('', views.index, name='index'),
	# ex : /polls/5/
	path('<int:question_id>/', views.detail, name='detail'),
	# ex : /polls/5/results/
	path('<int:question_id>/results/', views.results, name='results'),
	# ex : /polls/5/vote/
	path('<int:question_id>/vote/', views.vote, name='vote')
];

polls/views.py

from .models import Question

def index(request):
	lastest_question_list = Question.objects.order_by('-pub_date')[:5];
	
    output = ', '.join([q.question_text for q in lastest_question_list]);
    return HttpResponse(output);

# At first, Modify index Page

여기까지 만들면 기능이 있는 뷰를 만들었지만 MTV 패턴에 따르지는 않는다. 템플릿을 만들어 python code와 HTML을 분리해야한다. 그를 위해 polls디렉터리 하위에 templates디렉터리를 생성한다.(HTML code가 들어갈 디렉터리)


polls/templates/polls/index.html


<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Title</title>
</head>
<body>
    {% 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 %}
</body>
</html>

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));

polls/views.py [render() 이용]

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);

12. Error Page

polls/views.py

from django.http import Http404;

def detail(request, question_id):
	try:
        question = Question.objects.get(pk=question_id);
    except Question.DoesNotExist:
        raise Http404("Question does not exist.");
        # 상세 정보를 불러올 수 없는 경우 404 에러를 발생시킨다.
    return render(request, 'polls/detail.html', {'question': question});

polls/templates/polls/detail.html


<body>
	{{ question }}
</body>

polls/views.py [django.shortcuts 이용]

from django.shortcuts import render, get_object_or_404;

def detail(request, question_id):
	question = get_object_or_404(Question, pk=question_id);
    # get_object_or_404()에 의해 try-except문을 하나의 함수로 표현
	return render(request, 'polls/detail.html', {'question': question});

polls/templates/polls/detail.html


<body>
	<h1>{{ question.question_text }}</h1>
    <ul>
    {% for choice in question.choice_set.all %}
        <li>{{ choice.choice_text }}</li>
    {% endfor %}
    </ul>
</body>

13. Delete Hard-coded URLs

polls/templates/polls/index.html


<!-- Hard-coded url -->
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

<!-- delete Hard-coding -->
<li><a href="{% url 'detail' question.id %}"> question.question_text </a></li>

14. Set URL Namespace

polls/urls.py

app_name = 'polls';

polls/templates/polls/index.html


<li><a href="{% url 'polls:detail' question.id %}">{{ question_text }}</a></li>

15. Create simple Form

polls/templates/polls/detail.html


<h1>{{ question.question_text }}</h1>

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

polls/views.py

from django.http import HttpResponse, HttpResponseRedirect;
from django.urls import reverse;

from .models import Question, Choice;

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();
        
    	return HttpResponseRedirect(reverse('polls:results', args=(question.id)));

polls/views.py

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

polls/templates/polls/results.html


<h1>{{ question.question_text }}</h1>

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

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

16. Using Generic View

Generic view : Django에서 미리 준비되어 있는 뷰 [보편적인 틀]

polls/views.py

from django.views import generic;

from .models import Question, Choice;

class IndexView(generic.ListView):
    template_name = 'polls/index.html';
    context_object_name = 'latest_question_list';
	
    def get_queryset(self):
        # Return the last five published questions.
        return Question.objects.order_by('-pub_date');

class DetailView(generic.DetailView):
    model = Question;
    template_name = 'polls/detail.html';
    
class ResultsView(generic.DetailView):
    model = Question;
    template_name = 'polls/results.html';

이전에 작업했던 뷰를 함수형 뷰[functional view]라고 하고, 지금 작성한 뷰가 클래스형 뷰[classical view]이다.

polls/urls.py

urlpatterns = [
	path('', views.IndexView.as_view(), name='index'),
	path('<int:pk>/', views.DetailView.as_view(), name='detail'),
	path('<int:pk/results/', views.ResultsView.as_view(), name='results'),
	path('<int:question_id>/vote/', views.vote, name='vote')
];

17. Using Static Files

Static File : css, js와 같은 파일을 의미, static 디렉터리를 만들고 그 안에 파일을 저장한 후 사용 가능

polls/static/polls/style.css

body {
	background: white url("images/background.png") no-repeat;
	background-position: right bottom;
}

li a {
	color: green;
}

polls/templates/polls/index.html


<head>
	...
	{% load static %}
	<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}"
</head>

18. Customizing Admin Form

polls/admin.py

class QuestionAdmin(admin.ModelAdmin):
	fieldsets = [
		(None,					{'fields': ['question_text']}),
		('Date information',	 {'fields': ['pub_date']})
	];
	
admin.site.register(Question, QuestionAdmin);

polls/admin.py

from .models import Question, Choice;

class ChoiceInline(admin.StackedInline):
	model = Choice;
	extra = 3;
	
class QuestionAdmin(admin.ModelAdmin):
	fieldsets = [
		(None,					{'fields': ['question_text']}),
		('Date information',	 {'fields': ['pub_date']})
	];
	
	inlines = [ChoiceInline];

polls/admin.py

class ChoiceInline(admin.TabularInline):
	model = Choice;
	extra = 3;

19. Admin Page List Customizing

polls/admin.py

class QuestionAdmin(admin.ModelAdmin):
	fieldsets = [
		(None,					{'fields': ['question_text']}),
		('Date information',	 {'fields': ['pub_date']})
	];
	
	list_display = ('question_text', 'pub_date', 'was_published_recently');
	
	inlines = [ChoiceInline];

polls/models.py

class Question(models.Model):
	# ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1);
    
    was_published_recently.admin_order_field = 'pub_date';
    # admin_order_field -> 정렬 기준을 결정
    was_published_recently.boolean = True;
    # boolean -> 값 대신 아이콘 사용 여부
    was_published_recently.short_description = 'Published recently?'
    # short_description -> 항목 헤더 이름 설정

polls/admin.py

class QuestionAdmin(admin.ModelAdmin):
	fieldsets = [
		(None, 				{'fields': ['question_text']}),
		('Date information', {'fields': ['pub_date']})
	];
	
	list_display = ('question_text', 'pub_date', 'was_published_recently');
	
	inlines = [ChoiceInline];
	
	list_filter = ['pub_date'];
    # list_filter -> 필터 기능
	search_fields = ['question_text'];
    # search_fields -> 검색 기능


Share Tweet +1