Django / 복습(book)
Web/Django

Django / 복습(book)

뉴비뉴 2019. 7. 5.

기본적인 설치과정은 제외하고, 터미널에서 실행하는 것들만 정리해보자.

 

$ pip install django # django 설치

$ django-admin startproject config . # config 라는 프로젝트 생성

$ python manage.py migrate # DB 생성, db.sqlite3 파일생성

 

프로젝트 구조

config 폴더: 프로젝트 설정 파일과 웹 서비스 실행을 위한 파일이 들어있다.

  • __init__.py : 파이썬 2.x대 버전과의 호환을 위해 만들어진 비어있는 파일이다. 3.x 대에서는 불필요하지만 계속 생성된다. 지워져도 프로젝트를 진행하는데 지장이 없다.
  • settings.py : 프로젝트 설정에 관한 다양한 내용이 들어 있다. 
  • urls.py : 특정 기능을 수행하기 위해 접속하는 주소를 url이라고 하고 이를 설정해 주는데, 그 설정을 이 파일에 기록한다. 한 프로젝트 안에 여러 개의 urls 파일을 만들 것이다. config 폴더 안에 있는 urls 파일이 최초로 탐색되는 기준 url 파일이다.기준 url 파일은 settings.py 에서 변경할 수 있다.
  • wsgi.py :  웹 서비스를 실행하기 위한 wsgi 관련 내용이 들어 있다. 특별히 변경할 일은 거의 없다.

venv 폴더 : 프로젝트 구동에 필요한 가상환경이 들어있는 폴더이다.

db.sqlite3 : Sqlite3 DB파일이다.

manage.py : Django의 다양한 명령어를 실행하기 위한 파일이다.

 

settings.py : 프로젝트에 관련된 다양한 설정이 들어있다.

  • BASE_DIR : 프로젝트 루트 폴더, 설정 파일이나 py 파일 등에서 프로젝트의 루트 폴더를 찾아 그 하위를 탐색한다거나 하는 일들을 빈번하게 수행한다.
  • SECRET_KEY : 다양한 보안을 위해 사용한다. 세션 값의 보호나 비밀번호 변경 시 사용되는 보안 URL을 만드는 등의 일에 주로 사용된다. 임의로 변경할 수 있으나 변경하면 로그인이 풀리는 등의 부작용이 있다. 또 정해진 값은 외부에 노출되어서는 안된다.
  • DEBUG : 디버그 모드를 설정, True일 경우 다양한 오류 메시지를 확인, 실제로 배포할 때는 False로 변경하고, 이 때는 다른 설정값을 이용해 관리자가 오류 메시지를 받아 보게 설정할 수 있다.
  • ALLOWED_HOSTS : 현재 서비스의 호스트를 설정한다. 개발시에는 비어두고 사용하나 배포시에는 '*' 이나 실제 도메인을 기록한다. '*'은 위험하기 때문에 실제 도메인을 기록하기를 권장하고 있다.
  • INSTALLED_APPS : Django 웹 서비스는 다양한 앱의 결합으로 만들어진다.
  • MIDDLEWARE : Django의 모든 요청/응답 메시지 사이에 실행되는 트굿한 프레임워크들이다. 주로 보안에 관한 내용들이 많다.
  • ROOT_URLCONF : 기준이 되는 urls.py 파일의 경로를 설정한다.
  • TEMPLATES : Django에서 사용하는 템플릿 시스템에 관한 설정들이다. 템플릿 해석 엔진과 템플릿 폴더의 경로 등을 변경하는데 쓰인다
  • WSGI_APPLICATION : 실행을 위한 wsgi 어플리케이션을 설정
  • DATABASES : DB관련 설정
  • AUTH_PASSWORD_VALIDATORS : 비밀번호 검증을 위한 설정이다. 기본적으로 들어있는 검증 규칙은 사용자 정보와 유사한지, 숫자로만 만들었는지, 너무 짧은지, 평범한 비밀번호 인지 검증하도록 되어 있다.
  • LANGUATE_CODE : 다국어에 관한 설정들이다.
"""
Django settings for config project.

Generated by 'django-admin startproject' using Django 2.2.3.

For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'bfa22adz=rp#nasj3-5wi28l8d_t@0mw1)t%-!v7%^6(!19241'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

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

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'config.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'config.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'

wsgi.py

wsgi 어플리케이션 구동을 위해 사용되는 파일이다. 실제로는 웹 서버와 Django 애플리케이션 사이에 통신 역할을 담당하는 것이 wsgi 이다.

MVC(Model-View-Controller)

  • Model : DB의 구조를 결정한다. 대부분의 경우 테이블을 작성할 때 사용하는데 테이블의 이름, 각 열의 이름고 타입 등을 설계할 때 사용한다.
  • View : 사용자에게 보여지는 UI 부분이다. HTML 코드 등 실질적으로 클라이언트에게 보여질 부분이 들어간다.
  • Controller : 기능, 로직 부분이다. 넘겨 받을 데이터를 어떻게 해석하고 가공할지 그리고 처리된 결과를 어떻게 클라이언트에게 돌려줄지 결정하는 부분이다.

하지만 Django 에서는 MTV(Model-Template-View) 라고 말하지만, 결국 기능은 MVC와 똑같은 기능을 한다.

  • models.py : DB 구조를 결정, 모델은 클래스로 만드는데 클래스의 이름이 테이블 이름이 되고 클래스의 속성들이 컬럼이 된다. 
    ex) class Post(Models.model) 테이블 Post / title = models.charField("제목", max_length(100) 칼럼과 속성
    또, SQL 구문을 모르더라도 DB를 편하게 이용할 수 있도록 ORM 이라는 기능을 제공하낟. 객체를 이용해 관계형 DB를 이용할 수 있게 해주는 개념이다. Django 에서만 사용하는 것은 아니고 웹 프레임워크에서 많이 사용하고 있다.
  • admin.py : DB에 테이블을 생성했다면 이를 추가, 수정, 삭제, 확인이 가능해야 한다. 웹 프로그래밍은 이것은 CRUD라고 부른다. 모델이 제대로 만들어 졌는지 바로 확인을 하고 싶다면 Django의 기본 관리자 페이지를 사용할 수 있다. 이 때 자신이 만든 모델을 관리자 페이지에서 확인하거나 특별한 기능을 추가하고 싶다면 admin.py 에 작성하면 된다.
  • views.py : 글쓰기, 글보기, 글수정, 글삭제 등등 페이지를 만들 때 views.py에 프로그래밍 해준다.
  • urls.py : views.py에 어떤 기능을 만들면 해당 기능을 동작 시키는 방식은 url을 통한 호출 방식이 주가된다. '그럼 어떤 url를 이용해 어떤 view를 동작시킬까'를 결정해야 하는데 이 내용을 urls.py에 기록한다.
  • template : 보통 HTML이 들어 있는 파일이다. 

urls.py

path 함수는 path(route, view, kwargs, name) 형태로 호출한다.

  • route : 주소를 의미한다
  • view : route의 주소로 접근했을 때 호출할 뷰
  • kwargs : 뷰에 전달할 값들
  • route의 이름을 의미,이 이름을 가지고 원하는 곳에서 주소를 호출해 출력하거나 사용할 수 있다.

include는 다른 urls.py 파일을 참조할 수 있도록 한다. 만약 127.0.0.1:8000/polls/list/ 라는 주소로 접속하면 polls/까지는 일치하기 때문에 잘라내고 나머지 list/ 부분만 polls/urls.py 에서 찾아보는 방식으로 동작한다.

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

# config/urls.py 에서 path('')로 지정해준다는 것은 http://127.0.0.1:8000/ 로 접속했을 때 화면을 띄우라는 소리와 같다.

path('polls/', include('polls.urls')),

# 위와 같은 코드는 http://127.0.0.1:8000/polls/ 를 의미한다.

models.py

Django의 모델은 models.Model을 상속받아 만든다.

ForeignKey 다른 모델과의 관계를 만들기 위해서 사용한다. Choice 모델이 ForeignKey로 Question 모델을 갖는다는 것은 Choice 모델이 Question에 소속된다는 것을 의미한다.

 

모델을 완성했으니 데이터베이스에 적용해야 한다. 이때 사용하는 명령이 migrate 명령들인데 이 명령을 사용하려면 polls 앱이 현재 프로젝트에 설치되어 있다고 알려줘야 한다. settings.py 에 installed_apps에 polls 를 추가한다.

 

이제 데이터베이스에 적용해보자.

 

$ python manage.py makemigrations polls

$ python manage.py sqlmigrate polls 0001 # 로 SQL구문이 어떻게 동작하는지 확인할 수 있다. [!] 확인만

$ python manage.py migrate polls 0001 # 적용

 

모델에 함수 추가하기

 

Question 모델과 Choice 모델에 __str__ 메서드를 추가한다. 해당 메서드는 관리자 화면이나 쉘에서 객체를 출력할 때 나타날 내용을 결정한다.

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    def __str__(self):
        return self.question_text

다음은 was_published_recently 메서드를 추가하고 메서드가 잘 동작하도록 datetime과 timezone도 import 한다.

import datetime

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

# Create your models here.
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

관리자 페이지 확인하기

 

관리자 페이지에서 Question 모델을 관리하려면 등록해야한다. admin.py 파일에 다음 코드를 입력해보자

from django.contrib import admin

from .models import Question
# Register your models here.

admin.site.register(Question)

admin.site.register(Question) 의 결과물, Questions가 생성되었다.
models.py 에서 정의했던 칼럼들이 생성 된 걸 확인할 수 있다.
__str__을 question_text 로 해주었기 때문에 question_text가 표시되는 걸 확인할 수 있다. 

여러가지 뷰 추가하기

  • 투표 목록 : 등록된 투표의 목록을 표시하고 상세 페이지로 이동하는 링크 ㅈ공
  • 투표 상세 : 투표의 상세 항목을 보여줌
  • 투표 기능 : 선택한 답변을 반영
  • 투표 결과 : 선택한 답변을 반영한 후 결과를 보여줌
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.

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

def detail(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)

# 항상 views를 생성했으면 url 도 추가해주어야 한다.

urls.py

from django.urls import path
from . import views

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

# url에 있는 <>는 변수를 의미한다. 이 부분에 해당하는 값을 뷰에 인자로 전달한다.

# 실제 동작되는 뷰를 만들기 위해 index View부터 수정하겠다.

index 부분 추가 views.py

from django.shortcuts import render
from django.http import HttpResponse
from .models import Question
# Create your views here.

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5] #[:5] 5개 까지만 출력

    output = ', '.join([q.question_text for q in latest_question_list])
    #for 설명: latest_question_list를 q에다가 저장하고 그 q를 q.question_text로 변수에 저장해서
    #output 설명 : 1, 2, 3, 4, 5 이런식으로 출력 ex)pub_date에는 날짜가 들어있으니 날짜로 출력
    return HttpResponse(output)

def detail(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)

기능이 있는 뷰를 만들었지만 MTV 패턴에 따르지 않은 형태입니다. 템플릿을 만들어 파이썬 코드와 HTML 코드를 분리하자. templates 폴더 생성 polls/templates/polls

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</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>

만든 템플릿을 이용하도록 뷰를 변경하겠습니다. 템플릿을 불러오기 위해 [loader]를 임포트 하고 index뷰의 내용을 다음 코드를 참조해 변경한다.

 

views.py

from django.shortcuts import render
from django.http import HttpResponse
from django.template import loader # 템플릿을 불러오기 위한 loader 를 임포트
from .models import Question
# Create your views here.

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

loader를 이용해 index.html를 불러오고 여기에 미리 만들어 둔 투표 목록을 context라는 변수를 이용해 전달한다.

하지만 이런 절차가 약간 불편하다. 그래서 장고에는 render라는 단축함수가 존재한다. 이 함수를 이용하여 코드를 변경하겠습니다.

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)

render 메서드는 request와 템플릿 이름 그리고 사전형 객체를 인자로 받는다. 여기서 사전형 객체는 템플릿에서 사용할 변수들입니다. render 에서는 loader를 사용하지 않는다. 코드가 줄어듬으로 더 효율적이다.

 

404 오류 일으키기

해당 데이터가 존재하지 않는다는 의미로 사용한다.

 

detail view를 수정

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")
    return render(request, 'polls/detail.html', {'question': question})

Http404를 이용하면 상세 정보를 불러올 수 있는 투표 항목이 없을 경우 404 오류를 발생시킬 수 있다.

이전의 index 뷰와 마찬가지로 detail 뷰에서 detail.html 이라는 템플릿을 사용하는 것을 볼 수 있다. detail.html 파일을 만들고 body 태그 안에 다음 코드를 입력합니다. {{ question }}

 

Http404를 처리할 때는 loader-render 관계처럼 단축 함수가 존재 합니다. 바로 get_object_or_404 입니다. 이 함수를 사용해 detail 뷰를 수정해보자.

from django.shortcuts import render, get_object_or_404

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

하드 코딩된 URL 없애기

index.html 파일을 살펴보면 상세 페이지로 이동하기 위한 링크의 주소가 하드 코딩 되어 있는 것을 알 수 있다.

<li><a href="/polls/{{ question.id }}">{{ question.question_text }}</a></li>

href 속성의 값을 직접 써주는 방식으로 해둘 경우 나중에 주소를 polls가 아닌 다른 형태로 바꾸려하면 html을 직접 다 열어서 변경해야 하는 불편함이 있습니다. 그래서 url 템플릿 태그를 사용해 하드 코딩된 URL을 없애보겠다.

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

url 템플릿 태그를 사용해 주소를 만들어 출력하는 방식입니다. url 템플릿 태그는 URL의 이름을 필수 인자로 전달 받습니다. detail이라는 이름을 가진 URL 형식을 찾아서 URL을 만들어 출력하는 것입니다. 해당 이름을 가진 URL은 urls.py 전체를 검색해 찾습니다.

URL 네임스페이스 설정하기

분리된 경로를 만드는 개념인데, 예를 들어 detail이라는 주소 이름을 가진 뷰가 polls 에도 있고 다른 앱에도 있을 경우 Django는 어느 뷰의 URL을 만들지 알 수가 없습니다. 그래서 이런 경우 네임스페이스를 설정해 각각의 뷰가 어느 앱에서 속한 것인지 구분하도록 할 수 있습니다. 필수로 설정할 필요는 없지만 프로젝트가 복잡 해질수록 네임스페이스가 있는 것이 편리합니다.

urls.py

app_name = 'polls'
urlpatterns =[

]

네임페이스를 사용하기 위해서 템플릿에도 수정을 해야 한다.

index.html

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

간단한 폼 만들기

투표 기능이 동작하려면 detail.html 을 수정하고 vote 뷰에도 기능을 추가해야 합니다.

#detail.html

<h1>{{ question.question_text }}</h1>
{% if error_message %} <p><strong>{{ error_mmessage }}</strong></p> {% endif %}

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

[form] 태그를 사용해서 사용자가 답변 항목을 선택하고 전달할 수 있도록 만들었다.

{% url 'polls:vote' question.id %} 사용자가 선택한 항목의 번호를 vote뷰에 전달하도록 action 속성에 vote URL이 출력되게 url 템플릿 태그를 사용했습니다. method="post" 속성에 써 있는 post는 HTTP 메서드 중에 하나이며 서버로 정보를 전달할 때 사용하는 일반적인 방법입니다. {{forloop.counter}} 는 템플릿 문법에서 제공하는 기능 중 하나로 반복문의 반복 횟수를 출력해주는 기능을 합니다. 여기서 선택한 답변의 번호를 vote뷰에 [choice=번호] 형태로 전달합니다.

csrf_token 은CSRF 공격을 막기 위한 수단 중 하나입니다. 간단히 말해서 방금 서버로 들어온 요청이 사이트 내부에서 온 것이 맞는지 확인하는 용도로 csrf_token 값을 사용합니다.

#views.py

from django.shortcuts import render, get_object_or_404
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:resultes',
                                                args=(quesiton.id,)))
# request.POST['choice']를 통해 전달받은 변수의 값들을 확인할 수 있습니다. 이 때 전달되는 값은
# 항상 문자열이기 때문에 문자열이라는 사실을 기억하고 다뤄야 합니다. 전달 받은 답변이 해당 투표 항목에
# 있는지 확인하고 없다면 다시 상세 페이지로 이동합니다. 이 때 답변을 선택하지 않았다는 오류 메시지도
# 같이 전달합니다. 반대로 제대로된 답변을 선택한 것이라면 해당 답변의 답변수를 1 증가시키고 
# 결과화면으로 이동

제네릭 뷰 사용하기

제네릭 뷰는 장고에서 미리 준비한 뷰를 말한다. 웹 프로그래밍에 일반적으로 사용되는 뷰들은 이미 장고에서 대부분 만들어 두었다. 이것들을 사용하면 코드를 적게 사용하고 기능들을 빠르게 완성 할 수 있다.

기존에 있던 index, detail.result 뷰는 삭제한다.

#views.py

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.views import generic

class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'
    
    def get_queryset(self):
        """Return the last five published question."""
        return Question.objects.order_by('-pub_date')[:5]
    
class DetailView(generic.ListView):
    model = Question
    template_name = 'polls/detail.html'
    
class ResultsView(generic.ListView):
    model = Question
    template_name = 'polls/results.html'

새로 추가한 뷰는 기존의 함수형 뷰와 달리 클래스형 뷰이다. 클래스 형뷰로 바꾸면서 장고가 만들어둔 제니릭 뷰 중에 한가지씩을 상속받아 적용했습니다. 코드가 훨씬 간결해지는 것을 확인할 수 있습니다.

 

클래스형 뷰로 변경하면 urls.py도 수정해줘야 한다.

urlpatterns = [
    path('', views.IndexView.as_view(), name='index'), # ex: /polls/
    path('<int:question_id>/', views.DetailView.as_view(), name='default'), # ex: /polls/5/
    path('<int:question_id>/results/', views.ResultsView.as_view(), name='results'), # ex: /polls/5/results
    path('<int:question_id>/vote/', views.vote, name='vote'), # ex: /polls/5/vote
]

정적파일 사용하기

polls/static/polls/style.css 생성

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

}
li a{
    color: green;
}

css 파일을 불러와 사용할 수 있도록 index.html 파일을 수정

index.html 파일의 head 태그 안에 다음 코드 실행

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

 

'Web > Django' 카테고리의 다른 글

Django - Dstagram part2-Account app  (0) 2019.07.18
Django - Dstagram part1-Photo app  (0) 2019.07.18
Django / 복습(AWS)  (0) 2019.07.05
Django / 복습(실습)  (0) 2019.07.03
Django / 복습  (0) 2019.07.01

댓글

💲 추천 글