카테고리 없음

(생활코딩) Django WebFramework 공부 정리

초콜릿나라첩자딸기요정 2024. 7. 18. 20:39

큰그림

<현재 나의 수준에 맞춘 공부 계획(변경가능)>

HTML&Internet(생활코딩) → Django WebFramework(생활코딩) → Python(점프 투 파이썬 등) → Django WebFramework(인프런 이진석님 강의)

 

공부를 통해 최종적으로 인공지능 기술을 활용한 웹을 만드는 것이 목표다. 인공지능과 관련된 라이브러리가 풍부한 Python(파이썬)을 사용하기로 하였다. Django(장고)는 대표적인 Python 웹 프레임워크 중 하나인데, 기본적인 기능이 탑재되어 있어 개발하기에 편리하고 초보자에게도 좋을 것 같아 선택하였다. 

 

공부내용정리

생활코딩님의 DjangoWebFramework 강의를 들으며 공부한 내용을 정리해보았다. (다만, 강의를 듣기 전 인프런에서 이진석님의 파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트(장고4.2 기준) 강의의 무료 부분을 수강하였기 때문에 여기서 공부한 내용도 아주 조금 같이 적혀있다.)

https://opentutorials.org/course/4886

 

Django Web Framework - 생활코딩

수업소개 웹애플리케이션을 만드는데 도움을 주는 도구인 파이썬 장고 웹 프래임워크 수업입니다. 이런 분들이 사용할 수 있는 도구입니다. 파이썬을 다룰 수 있다. html로 웹페이지를 만들 수

opentutorials.org

 

https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4-with%EB%A6%AC%EC%95%A1%ED%8A%B8/dashboard

 

파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 (장고 4.2 기준) | 이진석 - 인프런

이진석 | 파이썬/장고 웹서비스 개발의 기초를 탄탄히 다져보세요. 장고 학습에는 시간도 걸리지만, 반복되는 개발 속에 여러분의 생산성을 극대화하실 수 있습니다. 여러분의 상상을 현실로 만

www.inflearn.com

 

<2강. 장고 설치>

나는 강의를 그대로 따라했지만, 윈도우 파워쉘에서 가상환경을 만들어 실습했다.

아래와 같이 폴더를 만들고, 가상환경을 생성한 후 활성화 하였다. 이후 강의를 따라 django를 설치하였다.

PS C:\workspace\mydjango00> py -3.11 -m venv venv
PS C:\workspace\mydjango00> venv\Scripts\activate

강의와 같이 VSCode를 보기 위하여 프로젝트 생성 후 아래와 같이 VSCode를 실행하였다.
(venv) PS C:\workspace\mydjango00> code .

강의의 뒷 부분은 VSCode의 터미널에서 가상환경이 활성화 된 상태로 이어서 하였다.

 

명령 요약

1. python -m pip install django : 장고 설치 (pip 명령 사용시 python -m pip를 붙이면 현재 파이썬 명령이 바라보는 인터프리터 혹은 가상환경에 정확하게 패키지 설치가 가능함.)

2. django-admin startproject 프로젝트명 . : 프로젝트 생성( . 은 현재 디렉토리를 의미하는데, 안붙이면 프로젝트명의 폴더를 만들고 그안에 같은 이름의 폴더가 또 생기고 mange.py가 생성됨.)

3. python manage.py runserver [포트번호] : 장고 기본 서버 실행 (주소로 접속)

 

기본으로 생성된 파일별 역할

settings.py : 프로그램을 운영하는데 필요한 설정들 

urls.py : 사용자가 접속하는 path에 따라서 그 접속(요청)을 어떻게 누가 처리해줄 것인지 지정하는(라우팅하는) 파일

(프로젝트 폴더 파일 밖에 생성) manage.py : 프로젝트를 진행하는데 있어 필요한 여러가지 기능이 들어간 유틸리티 파일

 

 

<3강. 포트의 개념>

어떤 프로그램이 80번 포트에 접속되어있다 = 리스닝 하고 있다.

이 프로그램에 접속하기 위해서 주소에 80이라는 포트 번호를 적어주면, 80번 포트에서 리스닝하고 있는 서버와 통신할 수 있음.

파이썬 개발 서버는 기본적으로 8000번 포트에서 리스닝하게 됨. 만약 이미 8000번 포트에 다른 서버가 실행되고 있다면 다른 서버를 끄고 실행하거나 혹은 개발 서버를 다른 포트에 리스닝시키면 됨.

python manage.py runserver : (기본) 8000번 포트에 리스닝됨.

python manage.py runserver 8888 : 8888번 포트에 리스닝됨. 주소는 http://127.0.0.1:8888로 접속

 

<4강. 앱 만들기>

출처: (생활코딩 강의 영상 중 캡처 2분 15초) https://opentutorials.org/course/4886/31113

 

(실제 어플리케이션을 프로젝트 안에서 구현하는 것이 아니라, app이라는 더 작은 단위를 만들어서 그 안에서 실제 구현을 하게 됨. model을 통해서 데이터베이스를 사용)

 

명령요약

django-admin startapp 앱이름 : 앱 생성 (폴더가 생성되고 그 안에  여러 파일이 생성됨. 이 파일을 구현하는 것을 통해 구체적인 어플리케이션을 만들어가게 되는 것임.)

 

<5강. 장고 라우팅(URL conf)>

라우트: 경로

라우팅: 접속한 주소에 따라서 적절한 로직을 연결해주는 작업.

라우터: 라우팅 해주는 도구

장고의 라우터: urls.py

(웹 프레임 워크에서는 라우팅이 반이다!)

 

1. 프로젝트의 urls.py

프로젝트 폴더의 urls.py의 urlpatterns라는 리스트 안에 라우팅과 관련된 정보가 적혀있음.

현재 urls.py가 아니라 다른 urls.py로 위임을 하려면 include() 함수 사용. myapp의 urls.py를 사용하게 하기 위해 함수의 인자로 myapp.urls를 넣음.

#myproject>urls.py

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

#http://127.0.0.0.1/
#http://127.0.0.0.1/app/
#http://127.0.0.0.1/create/
#http://127.0.0.0.1/read/1/

urlpatterns = [
    path("admin/", admin.site.urls),
    path('', include('myapp.urls')) #admin이 아닌 다른 경로로 접속하면 
]

 

2. (위임받은) 앱의 urls.py

-앱(myapp)에 urls.py파일을 만들어 주고, 사용자가 들어오는 각각의 경로에 대해 views로 전달하고 views의 어떤 함수를 사용할지 정해준다. (그러기 위해 myapp 패키지 안에 있는 views를 import 해주어야 함.)

-주소에 read/1/, read/2/ ...와 같은식으로 바뀔 수 있는 값이 들어올 수 있을 때에는 <>(꺽새)안에 값의 이름을 적어줌. 아래와 같이 <id>라고 작성하면 '여기의 값은 언제든지 바뀔수 있고, 거기에 들어오는 값의 이름이 id이다.'라고 볼 수 있다. 이와 같이 작성한 후에는 views.py의 read()함수에서 두번째 파라미터로 id를 주면, 이 id값을 처리할 수 있음.

#myapp>urls.py

from django.urls import path
from myapp import views

urlpatterns = [
    path('', views.index),
    path('create/', views.create),
    path('read/<id>/', views.read)
    
]

 

3. (위임받은) 앱의 views.py

- 앱의 views.py에는 클라이언트에게 정보를 전송하기 위한 역할을하는 함수 정의. 이 함수는 첫번째 파라미터의 인자로 요청과 관련된 여러가지 정보가 들어오도록 약속되어있는 객체를 전달해주도록 되어있음. 이름은 상관없으나 관습적으로 request를 사용함.

- 우리가 처리한 결과를 클라이언트에게 보내줄 때 return 값으로 보내줌.  HTTP를 이용해서 응답을 하겠다는 의미로 HttpResponse()라는 객체를 이용함. 그리고 그 인자로 전송하고 싶은 값을 적어줌.

#myapp>views.py

from django.shortcuts import render, HttpResponse

# Create your views here.
def index(request):
    return HttpResponse('Welcome!!')

def create(request):
    return HttpResponse('Create!')

def read(request, id):
    return HttpResponse("Read! " + id)

 

 

 

4. 결과

예시) http://127.0.0.0.1/8000/read/1로 접속한 경우

 

<6강. 장고를 쓰는 이유>

Web Sever Web Aplication Sever
apache, nignx, IIS django, flask, php, jsp, ROL
static
(필요로 하는 페이지 미리 만들어 놓아야 함.\
사용자 접속 시 미리 준비된 페이지로 접속)
dynamic
(웹 페이지를 생성하는 공장(view.py)을 하나 만들면 됨.
사용자 접속 시, 해당하는 데이터를 가져와 페이지를 만들어 응답, 요청이 들어올때마다 필요한 정보를 만듦)
빠름 느림
유지보수 불편함
(내용이 변경될 때마다 모든 페이지 수정)
유지보수 편함
(views.py만 바꾸면 됨. 접속한 사람에게 알맞는 정보를 만들어응답할 수 있기 때문에 개인화된 정보 제공이 가능)

 

<7강. 홈페이지 구현>

어플리케이션 작업 4가지 : CRUD

C - Create

R - Read

U - Update

D - Delete

 

Read

1. 홈페이지 (7강)

homepage

/

2. 상세보기 (8강)

article

/read/<id>/

 

파이썬 문법 참고)

- """ ... """(따옴표 3개): 문자열

- f-string: 문자열 앞에 f 혹은 F문자을 붙인 것. 중괄호 안에 변수나 함수 등을 호출하여 출력할 수 있음.

- 딕셔너리명[키] : 이대로 출력하면 Value 나옴.

- Trailing comma 사용시 마지막에 ,(콤마): 실수 방지, 소스코드 관리(변경 사항 파악) 용이.

https://mingrammer.com/tips-of-using-comma-in-python/

 

파이썬의 Comma(,) 사용팁

파이썬에서 **Comma(,)**를 활용할 수 있는 몇가지 팁을 정리해보고자 한다. 아마 많은 사람들이 이미 다 알고있을 내용이기 때문에, 이 또한 파이썬 초보자

mingrammer.com

 

실습 순서

1. index 함수의 HttpResponse()의 인자로 전송하고 싶은 값을 넣는데, 이때 html 코드를 보내줌.

2. 페이지마다 바뀌는 특정 부분은 파이썬 문법을 통해 html 코드 중간중간에 넣을 수 있도록 함.

-  f-string 문법으로 문자열 앞에 f 붙이고 바꾸고 싶은 부분을 {}(중괄호)를 통해 넣어줌.

- 딕셔너리 형태로 필요한 정보를 만들고 리스트로 묶어 관리

- 리스트를 전역변수로 선언하여 index 함수 안에서 사용할 수 있도록 함.

 

myapp의 views.py

from django.shortcuts import render, HttpResponse

# Create your views here.
topics = [
    {'id':1, 'title':'routing','body':'Routing is ..'},
    {'id':2, 'title':'view','body':'View is ..'},
    {'id':3, 'title':'Model','body':'Model is ..'},
]
def index(request):
    global topics
    ol = ''
    for topic in topics:
        ol += f'<li><a href="/read/{topic["id"]}">{topic["title"]}</a></li>'
    return HttpResponse(f'''
    <html>
    <body>
        <h1>Django</h1>
        <ol>
            {ol}
        </ol>
        <h2>Welcome</h2>
        Hello, Django
    </body>
    <html>
    ''')

def create(request):
    return HttpResponse('Create!')

def read(request, id):
    return HttpResponse("Read! " + id)

 

<8강. 상세보기 구현>

1. 중복되는 html 코드 함수로 만든 후, HttpResponse에 해당 함수 넣어서 응답.

2. 이때, 중복되지 않는 상세페이지(article) 부분은 각각에서 작성하여 변수로 정의해주고 응답 시 그 변수를 함수의 인자로 넣어줌. (즉, 각각에서 정의한 article은 HttpResponse 인자로 들어가서 반환됨. article이 articleTag에 해당한다는 뜻임.)

3. topics에서 정의된 id는 정수형(int)이지만 read의 url을 통해서 들어온 id 파라미터는 문자열(str)임. int()로 id를 감싸서 문자열을 정수형으로 바꿔줌.  

 

myapp의 views.py

from django.shortcuts import render, HttpResponse

# Create your views here.
topics = [
    {'id':1, 'title':'Routing','body':'Routing is ..'},
    {'id':2, 'title':'View','body':'View is ..'},
    {'id':3, 'title':'Model','body':'Model is ..'},
]

def HTMLTemplate(articleTag):
    global topics
    ol = ''
    for topic in topics:
        ol += f'<li><a href="/read/{topic["id"]}">{topic["title"]}</a></li>'
    return f'''
    <html>
    <body>
        <h1><a href="/"> Django </a></h1>
        <ol>
            {ol}
        </ol>
        {articleTag}
    </body>
    <html>
    '''


def index(request):
    article = '''
    <h2> Welcome </h2>
    Hello, Django
    '''
    return HttpResponse(HTMLTemplate(article))

def create(request):
    return HttpResponse('Create!')

def read(request, id):
    global topics
    article = ''
    for topic in topics:
        if topic['id'] == int(id):
            article = f'<h2>{topic["title"]}</h2>{topic["body"]}'
    return HttpResponse(HTMLTemplate(article))

 

myapp의 urls.py 

from django.urls import path
from myapp import views

urlpatterns = [
    path('', views.index),
    path('create/', views.create),
    path('read/<id>/', views.read)
    
]

 

<9강. 생성기능 구현 (form)>

만들려고 하는 것 (9강 ~ 11강)

Create링크를 누르면 주소와 화면이 바뀌고, 글을 작성할 수 있는 form이 있도록 함. 제목과 내용을 입력한 후 제출 버튼을 누르면 상단의 목록(topics 리스트)에 추가가 되도록 함. 그리고 글을 잘 썼는지 확인하기 위해  작성한 글에 대한 상세보기 페이지로 이동하는 어플리케이션 기능을 추가! 

 

실습

1. 글쓰기 페이지로 이동할 수 있는 링크를 추가. body 태그 아래에 링크를 추가함. 나중에 create 외에도 delete와 update 기능도 만들 것이기 때문에 리스트 형태로 해줌.

def HTMLTemplate(articleTag):
    global topics
    ol = ''
    for topic in topics:
        ol += f'<li><a href="/read/{topic["id"]}">{topic["title"]}</a></li>'
    return f'''
    <html>
    <body>
        <h1><a href="/"> Django </a></h1>
        <ol>
            {ol}
        </ol>
        {articleTag}
        <ul>
            <li><a href="/create/">create</a></li>
        </ul>
    </body>
    <html>
    '''

 

2. 사용자가 제목을 입력할 수 있도록 input 태그에 type으로 text를 줌(input 태크는 안닫아도 됨) / 도움말은 placeholder 속성의 값으로 줌 / 입력된 데이터의 이름은 name 속성 사용. 값으로 title 주면, 입력된 데이터를 서버로 전송할 때 title이라는 이름으로 전송하게 됨. / 줄바꿈 위해 단락 나타내는 p 태그 사용

3. 본문을 작성하기 위해 textarea 태그 사용(textarea 태그는 닫아야 함). 여러줄의 text 입력할 때 쓰는 태그임. / 마찬가지로 도움말 기능과 이름도 body로 추가해줌. 

4. 우리가 작성한 것을 서버로 보내기 위해 버튼을 만듦. / input 태그에 type을 submit으로 주면 됨. 제출 버튼이 만들어짐 / 제출 버튼을 눌렀을 때 title과 body에 담겨있는 데이터를 서버의 원하는 path로 전송하기 위해 form 태그로 감싸줌 / 보내고 싶은 서버의 path는 form 태그의 action 속성의 값으로 넣어주면 됨. /create/ 로 주어서 현재페이지로 보내도록 함.

5. 화면에서 오른쪽 버튼 클릭 - 검사 - 네트워크 탭( 웹브라우저와 서버 사이에 주고 받는 통신을 관찰할 수 있는 도구) / 제목과 내용을 적고 제출버튼을 누르고 주소창의 url 변화를 보면 url 뒤에 ?(물음표)가 따라오는 것을 볼 수 있음. 또 웹브라우저와 서버가 통신한 내용이 내역이 뜸. 내역을 누르고 Headers를 클릭해보면 Request Method가 GET임을 알 수 있음. 

def create(request):
    article ='''
        <form action = "/create/">
            <p><input type="text" name = "title" placeholder="title"></p>
            <p><textarea name = "body" placeholder = "body"></textarea></p>
            <p><input type="submit"></p>
        <form>
    '''
    return HttpResponse(HTMLTemplate(article))

 

제목과 내용 쓰고 제출버튼을 누른 후의 페이지

<10강. 생성기능(Method = GET, POST)>

URL, GET, POST

- id가 1인 글을 읽어오는(데이터를 가져오는) url

방법1.  http://localhost:8000/read/1/ (우리가 쓴 방식, 요즘 트랜드인 방식) 

방법2.  http://localhost:8000/read/?id=1  (전통적 방식, 표준)

여기서 "?id=1"은 query string임  / query srting은 서버에게 어떤 정보를 질의할 때 쓰는 문자열

둘다 브라우저가 서버로 부터 요청하는 GET하는 방식임.

 

-url의 목적이 다름

http://localhost:8000/read/1/

http://localhost:8000/read/?id=1

==> 브라우저가 서버로부터 데이터를 읽어가는 GET하는 것을 보여주는 url

 

http://127.0.0.1:8000/create/?title=CRUD&body=CRUD+is+...

==> 브라우저가 서버에 있는 데이터를 변경하려고 하는 작업임.

==> 그런데 저렇게 브라우저가 서버의 데이터를 변경할 때에는 url에 querystring을 넣어서는 절대 안됨!! 만약 url 안에 입력값까지 포함이 되어 있다면 주소를 카피해서 트위터나 페이스북 같은 곳에 공유하면 방문자들 클릭할때마다 글이 추가되기 때문임. 따라서 POST 방식을 사용해야 함.

==> 이전시간(9강)에서 Request Method가 GET 방식이었음. 이것을 POST 방식으로 보내면 url이 아닌 Header라는 것 안에 데이터를 포함해서 눈에 보이지 않게 보낼 수 있음.

 

실습

1. form 태그 작성시 아무것도 작성하지 않으면 GET방식을 사용함. form 태그 안에 method라는 속성에 post라는 값을 주면 post 방식으로 작동함. / 이렇게 수정 후 다시 오른쪽-검사-네트워크를 띄워놓은 후 제목과 내용을 입력하고 보냄. 에러가 발생하긴 하지만 내역을 누르고 Headers를 확인해 보면 POST방식을 사용하는 것을 알 수 있음. url에도 title이나 body와 같은 입력값이 포함되어 있지 않음. Payload를 확인해 보면 url이 아닌 다른 방식으로 tilte과 body를 전송한 것을 볼 수 있음. 

2. 발생한 에러는 CSRF verification failed.라는 메세지가 뜸. 장고가 가진 보안 기능임. 입문과정이기 때문에 보안기능에 대한 내용은 패쓰하고 이 기능을 우회하는 방법을 사용.  /  검색창에 "crsf django skip"이라고 검색 / from django.views.decorators.csrf import csrf_exempt를 추가해주고, 면제를 해주고 싶은 함수 위에 @csrf_exempt를 추가해 줌 / 다시 제목과 내용을 작성 후 제출해보면 해당 오류가 뜨지 않음.

3. 서버로 내용이 전송되었는데 아무런 변화가 없음. 서버쪽으로 전송되었을 때를 우리가 아무것도 하지 않았기 떄문임. GET방식으로 접속이 들어왔을 때와 POST 방식으로 접속이 들어왔을 때를 구분해서 처리를 다르게 해볼 것임. 

 

 

https://stackoverflow.com/questions/16458166/how-to-disable-djangos-csrf-validation

 

How to disable Django's CSRF validation?

I have commented out csrf processor and middleware lines in settings.py: 122 123 TEMPLATE_CONTEXT_PROCESSORS = ( 124 'django.contrib.auth.context_processors.auth', 125 # 'django.core.

stackoverflow.com

from django.views.decorators.csrf import csrf_exempt

 

@csrf_exempt
def create(request):
    article ='''
        <form action = "/create/" method="post">
            <p><input type="text" name = "title" placeholder="title"></p>
            <p><textarea name = "body" placeholder = "body"></textarea></p>
            <p><input type="submit"></p>
        <form>
    '''
    return HttpResponse(HTMLTemplate(article))

 

<11강. 생성기능(request, response object)>

문법 및 개념

1. 함수 내 전역변수 사용: 파이썬 함수 내에서 전역변수를 수정하려면 gobal 키워드를 통해서 전역변수임을 명시해주어야 함. 그렇지 않으면 그저 함수 내의 지역변수로 인식될 것임. 

https://devpouch.tistory.com/184

 

[python] 파이썬에서 전역 변수 사용하기 - global

파이썬에서 전역변수를 사용하다가 예상대로 코드가 실행되지 않는다는 것을 발견했다. 전역변수 사용의 잘못된 코드 # 전역 변수 val = False def change_val(): # 아래 val은 함수내의 '지역변수'다. val

devpouch.tistory.com

2. print() 함수에서 값들을 연결할 때

',(콤마)' 사용 :  공백이 생김 / 모든 자료형의 값을 연결할 수 있음

'+(덧셈)' 사용: 공백이 생기지 않음 / 문자열만 연결할 수 있음.  

https://velog.io/@ysyim02/print-%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C-%EA%B0%92%EB%93%A4%EC%9D%84-%EC%97%B0%EA%B2%B0-%ED%95%A0-%EB%95%8C-%EC%BD%A4%EB%A7%88-or-%EB%8D%A7%EC%85%88

 

Python) print 함수에서 값들을 연결 할 때 콤마? or 덧셈?

Python 기본 강의를 쭉 듣다가 간단한 어떤 부분이 맘에 걸려서(ㅠ) 구글링하다 첫 TIL을 남겨보기로 했다.

velog.io

3. 리다이렉트(Redirect) : 어떤 url로 다시 접속하도록 하는 것.

https://kotlinworld.com/329

 

리다이렉트(Redirect)와 포워드(Forward)의 차이는 무엇인가?

리다이렉트와 포워드 특정 URL 접속 시 리다이렉트 또는 포워드가 일어나게 되면 작업 중인 페이지가 전환된다. 리다이렉트와 포워드는 페이지가 전환된다는 점에서 비슷한 역할을 한다. 하지만

kotlinworld.com

https://webstone.tistory.com/65

 

리다이렉트란(Redirect)

HTTP 리다이렉트(Redirect)란? 리다이렉트란 말 그대로 re(다시) + 지시하다(direct) 다시 지시하는 것을 말한다. 예를 들어 브라우저가 www.webstone.com/blogA URL을 웹 서버에 요청했다고 하자 그러면 서버는

webstone.tistory.com

 

 

실습

==> 사용자 요청이 들어오면 장고가 요청을 받음. 장고 사용자의 요청과 관련된 여러 정보를 분석해 우리가 만든 create 함수를 장고가  호출함. 호출하면서 첫번째 파라미터의 인자로 HttpRequest라는 어떤 객체를 만들어 내고 그 안에 사용자의 요청과 관련된 패키징된 정보를 객체로 만들어서 우리에게 줌. 우리는 request를 분석해서 처리하는 코드를 작성하고 HttpResponse라는 객체를 응답해주면 그 정보가 장고에 의해 사용자의 브라우저로 전송됨.

 

1. django request 검색 / 우리에게 전달되는 HttpRequset 객체가 무엇인지 어떤 기능이 있는지 보여줌. / 그 중 request.method를 통해서 사용자가 GET방식으로 접속했는지 POST 방식으로 접속했는지 알 수 있음. /

create 함수 내 상단에 print('request.method',request.method)을 작성하고 제목과 내용을 입력하여 제출하면, 터미널에 POST 방식이라고 알려줌. / 이 request.method를 이용해서 GET과 POST의 처리를 분리할 수 있음. if문으로 분기를 줌.

 

2. 서버로 부터 들어온 데이터(제목과 내용)에 이름을 붙여줌. 이들로 newTopics라는 새로운 데이터를 만들어줌. 기존의 topics에 추가하여 사용하기 위해 튜플 형태로 만들어줌. / 기존에 id값이 3까지 있었으므로 nextId라는 전역변수를 만들어 4를 값으로 줌. / create 함수 내에서 사용하기 위해 함수내에서 한번 더 global nextId 라고 선언해줌. / 아까 만든 newTopics 안에 id를 nextId로 줌. 이렇게 완성된 튜플을 topics 리스트에 append()를 이용해 추가함. 그리고 다음에 추가될 데이터의 id값을 위해 nextId 값에 1을 더해줌. / 그리고 return 값은 일단 HttpResponse(HTMLTemplate('AAA'))라고 줌 / 제목과 내용 입력 후 제출을 눌러보면 화면 상단 목록에 제목으로 입력한 것이 추가된 것을 볼 수 있음. 이것을 누르면 입력된 내용 또한 볼 수 있음

 

3. 제목과 내용을 제출하면 목록이 추가된 모습과 AAA와 create가 보임. 추가된 목록을 눌러야만 내용(상세보기페이지)가 보임. / 이렇게 의미없는 정보(AAA 같은 것)을 화면을 출력하는 것이 아니라, 서버가 웹 브라우저에게 지금 생성한 글의 상세보기 페이지로 이동하라고 리디렉트(Redirect)시켜줘야 함. (즉, 제목과 내용 제출 후 원래 화면으로 돌아오면 바로 내용(상세페이지)가 보이도록 하려함) / django redirect 검색. redirect() 함수 사용위해 import 해줌/ nextId 값이 갱신되기 이전의 id값을 이용해 url를 만들어줌. return 값을 redirect(url)로 줌. (해당 url로 리다이렉트 됨.) / 이제 다시 내용과 제목을 입력해서 제출해보면 새로운 글이 목록에 추가되고 그 페이지의 고유한 id값을 가지고 있는 페이지로 이동하는 것을 볼 수 있음.

from django.shortcuts import render, HttpResponse, redirect
from django.views.decorators.csrf import csrf_exempt


nextId = 4
topics = [
    {'id':1, 'title':'Routing','body':'Routing is ..'},
    {'id':2, 'title':'View','body':'View is ..'},
    {'id':3, 'title':'Model','body':'Model is ..'},
]

def HTMLTemplate(articleTag):
    global topics
    ol = ''
    for topic in topics:
        ol += f'<li><a href="/read/{topic["id"]}">{topic["title"]}</a></li>'
    return f'''
    <html>
    <body>
        <h1><a href="/"> Django </a></h1>
        <ol>
            {ol}
        </ol>
        {articleTag}
        <ul>
            <li><a href="/create/">create</a></li>
        </ul>
    </body>
    <html>
    '''


def index(request):
    article = '''
    <h2> Welcome </h2>
    Hello, Django
    '''
    return HttpResponse(HTMLTemplate(article))

@csrf_exempt
def create(request):
    global nextId
    if request.method == 'GET':
        article ='''
            <form action = "/create/" method="post">
                <p><input type="text" name = "title" placeholder="title"></p>
                <p><textarea name = "body" placeholder = "body"></textarea></p>
                <p><input type="submit"></p>
            <form>
        '''
        return HttpResponse(HTMLTemplate(article))
    elif request. method == 'POST':
        title = request.POST['title']
        body = request.POST['body']
        newTopic = {"id": nextId, "title" : title, "body" : body}
        topics.append(newTopic)
        url = '/read/'+str(nextId)
        nextId = nextId + 1
        return redirect(url)

def read(request, id):
    global topics
    article = ''
    for topic in topics:
        if topic['id'] == int(id):
            article = f'<h2>{topic["title"]}</h2>{topic["body"]}'
    return HttpResponse(HTMLTemplate(article))

 

 

<12강. 삭제기능 구현(DELETE)>

만들려고 하는 것

글의 상세보기 페이지를 구현하면 그때 delete 버튼이 나타남. 이 delete 버튼을 클릭하면 글이 삭제되고 Home으로 이동.

실습

1. delete 버튼 만들기 : create의 경우 a태그를 사용 링크를 누르면 페이지로 이동하도록 함 => GET방식으로 서버에 접속한다는 것임. GET은 데이터를 가져올 때 쓰는 것. /

우리가 delete를 위한 페이지를 따로 만든다면 GET방식을 사용하면 되겠지만, 우리는 delete 버튼을 누르자마자 바로 서버의 데이터를 변경(수정)하는 작업을 할 것임. 이때에는 링크, GET방식 쓰는 것이 아니라, POST방식을 써야 함. 그리고 POST를 만들어주는 기계는 form임. /

2. form 태그를 만들어줌. form 태그 안에 input 태그를 주고 type 속성의 값으로 "submit", value 속성의 값으로 "delete"를 줘서 delete라는 글자가 적힌 버튼을 만들어줌.  그리고 이 버튼을 클릭했을 때 /delete로 이용하게끔 하기 위해 form 태그에 action 속성의 값으로 "/delete/",  method  속성의 값으로 "post"를 줌. /

3. 그리고 서버에게 삭제하고 싶은 글이 몇 번인지 알려줘야 함. 이것을 위해 read 페이지의 HTMLTemplate으로 들어올 때 두 번째 인자로 id값(글의 번호)을 전해줄 것임. id값을 받는 파라미터는 HTMLTemplate(articalTag)에 두번째 인자로 받으면 됨.  그런데 index와 같은 경우에는 id값이 없기 때문에 None이라는 기본값을 주어 오류를 방지함. /

4. 이렇게 들어온 id값을 서버로 전송할 때, input의 type은  hidden, name은 id, value는 {id}라고 줌. type이 hidden인 input태그는 화면상 표시가 되지 않음./

5. 이제 delete 버튼을 누르면 post방식으로 전송이 되었고 payload를 보면 id값이 잘 전달된것을 볼 수 있음./

전송이 되었으니 받는 쪽인 서버의 작업을 해야함. myapp에 있는 urls.py에 path를 추가(delete/로 들어오면 views.delete로 가도)./

6. 그리고 delete만든 뒤, post방식으로 들어온 확인하고 id값을 알아냄. (id = request.POST['id']), 이 id값을 이용해 topics의 값을 변형하면 됨. gobal topics를 선언하고 비어있는 리스트인 newTopics를 만들어줌. 그리고 쉰회하며 id와 일치하지 않는 애들을 newTopics에 추가함. 그리고 newTopics로 Topics를 바꿈. 이작업이 끝나면 홈으로 사용자를 보내버림. (return redirect('/')) /

7. 이제 목록 중 하나를 선택하고 delete를 누르면 사용자의 글이 삭제되었고 home으로 이동한것을 볼 수 있음. 근데 home에서는 delete 버튼을 누르면 오류가 남(delete할게 없음. id값이 없음.). 즉, 상세보기 페이지 일때만 delete 버튼이 있도록 하면 됨. id값이 있으면 상세보기 페이지임. /

맥락에 따라 UI가 보였다 안보였다 한다는 의미로 contextUI =''라는 변수를 만들어줌. id가 None이 아닐때만 delete 버튼이 보이게 함. contextUI에 delete 버튼의 html코드를 넣어줌. 그리고 원래 html 코드가 있던쪽에는 {contextUI}라고 작성해줌./

8. 이제 다시 실행해보면 홈 화면에는 delete 버튼이 없지만 글을 눌러 상세보기로 들어가면 delete가 생김!

 

※form - 데이터 전송: hidden

https://soohi.tistory.com/36

 

[생활코딩] form - 데이터 전송: hidden | sohinggu๑°⌓°๑

HTML - form: hidden hidden field 서버로 데이터를 전송하는데 UI가 필요없거나 몰래 데이터를 전송해야 하는 겅우 이용하는 기능 hidden : UI는 없지만 서버로 데이터를 전송 type이 hidden인 input태그는 화면

soohi.tistory.com

 

myapp의 views.py

from django.shortcuts import render, HttpResponse, redirect
from django.views.decorators.csrf import csrf_exempt


nextId = 4
topics = [
    {'id':1, 'title':'Routing','body':'Routing is ..'},
    {'id':2, 'title':'View','body':'View is ..'},
    {'id':3, 'title':'Model','body':'Model is ..'},
]

def HTMLTemplate(articleTag, id=None):
    global topics
    contextUI = ''
    if id != None:
        contextUI = f'''
         <li>
                <form action="/delete/" method="post">
                    <input type="hidden" name="id" value={id}>
                    <input type="submit" value="delete">
                </form>
            </li>
        '''

    ol = ''
    for topic in topics:
        ol += f'<li><a href="/read/{topic["id"]}">{topic["title"]}</a></li>'
    return f'''
    <html>
    <body>
        <h1><a href="/"> Django </a></h1>
        <ol>
            {ol}
        </ol>
        {articleTag}
        <ul>
            <li><a href="/create/">create</a></li>
            {contextUI}
        </ul>
    </body>
    <html>
    '''


def index(request):
    article = '''
    <h2> Welcome </h2>
    Hello, Django
    '''
    return HttpResponse(HTMLTemplate(article))

@csrf_exempt
def create(request):
    global nextId
    if request.method == 'GET':
        article ='''
            <form action = "/create/" method="post">
                <p><input type="text" name = "title" placeholder="title"></p>
                <p><textarea name = "body" placeholder = "body"></textarea></p>
                <p><input type="submit"></p>
            <form>
        '''
        return HttpResponse(HTMLTemplate(article))
    elif request. method == 'POST':
        title = request.POST['title']
        body = request.POST['body']
        newTopic = {"id": nextId, "title" : title, "body" : body}
        topics.append(newTopic)
        url = '/read/'+str(nextId)
        nextId = nextId + 1
        return redirect(url)

@csrf_exempt
def delete(request):
    global topics
    if request.method == 'POST':
        id = request.POST['id']
        newTopics =[]
        for topic in topics:
            if topic['id'] != int(id):
                newTopics.append(topic)
        topics = newTopics
        return redirect('/')



def read(request, id):
    global topics
    article = ''
    for topic in topics:
        if topic['id'] == int(id):
            article = f'<h2>{topic["title"]}</h2>{topic["body"]}'
    return HttpResponse(HTMLTemplate(article, id))

 

 

myapp의 urls.py

from django.urls import path
from myapp import views

urlpatterns = [
    path('', views.index),
    path('create/', views.create),
    path('read/<id>/', views.read),
    path('delete/', views.delete)
    
]

 

13강. 수정기능(UPDATE)

만들려고 하는 것

home에서는 글의 목록과 create 버튼만 보임. 글을 눌러 상세보기로 들어가면 create와 delete와 update버튼이 있음. update 버튼을 클릭하면 선택한 글의 제목과 내용이 폼에 들어가 있음. 이들을 수정한 뒤 제출버튼을 누르면 서버로 전송이 되면서 데이터가 변경되고 변경된 상세보기페이지로 이동하게끔 .

 

(CRUD 중 가장 어려운 UPDATE !! >.<)

실습

1. update 버튼 만들기: 상세보기로 갔을 때 update링크가 나타나야 함. 이전에 상세보기에서만 버튼이 보이게 하기 위해 만든 contextUI에 update 링크를 추가해줌. update로 접근할 때 id값을 들고 들어가도록 링크에 {id}추가. /

2. url(위의 링크)을 받을 수 있도록 라우팅 설정을 해줘야 함. myapp의 urls.py에 path 추가. ( path('update/<id>/', views.update) 를 urlpatterns에 추가) /

3. update 함수 작성 : create와 마찬가지로 get으로 요청이 들어왔을 때와 post로 요청이 들어왔을 때를 나눔.  get으로 접속하면 기존의 글자들이 작성된 채로 제목과 내용 폼이 보이게 할 것임. post로 접속했을 때는 글을 수정해서 제출버튼을 눌렀을 때임 /

우선 get으로 들어왔을 때, 재목과 내용 폼이 보이게 하기 위해 create에서 get으로 요청이 들어왔을때의 코드를 들고 와서 수정. 사용자가 submit을 했을 때 전송되는 주소에 update와 id를 줌. (<form action = "/update/{id}/" method = "post"> , 주의! /(슬래쉬)를 꼭 주소 뒤에 붙여줘야 함) /

4. 기존의 글자들이 폼에 작성되어 있어야 하므로, topics를 이용함(여기에 그 글의 id, 제목, 내용이 다 있으므로). global로 topics를 선언해주고 for문을 통해 topics의 데이터를 가져옴. 조회에 성공했을 때(선택한 글의 id값과 topics의 어떤 딕셔너리의 id값이 동일할 때), seletedTopic이라는 딕셔너리에 title과 body값을 담아줌. 그리고 이 seletedTopic의 tilte을 input의 value 속성에 줌. 그리고 seletedTopic의 body를 textarea의 컨탠츠로 넣어줌(태그 사이에). /

이제 글을 선택하고 상세페이지에서 update를 누르면 기존의 글자가 적혀진 제목과 내용 폼이 나타남.

5. 이제 서버쪽에 전송된 데이터를 가지고 topics 변수의 값을 바꿔주면 수정 완료! /  post로 들어왔을 때, title과 body의 값을 뽑아내서 변수로 저장함. (title = request.POST['title'], post = request.POST('body')) 그리고 topics를 돌면서 id값이 일치한다면 (수정한 그 글의 원래 제목과 내용이 담긴 딕셔너리이므로)  값을 변경시켜줌. 그리고 그 과정이 성공했다면 사용자를 read/{id}로 보내줌(리다이렉트) (주의! id값 일치하는지 검사할때 int()로 변환시켜줄것)

 

myapp의 views.py

from django.shortcuts import render, HttpResponse, redirect
from django.views.decorators.csrf import csrf_exempt


nextId = 4
topics = [
    {'id':1, 'title':'Routing','body':'Routing is ..'},
    {'id':2, 'title':'View','body':'View is ..'},
    {'id':3, 'title':'Model','body':'Model is ..'},
]

def HTMLTemplate(articleTag, id=None):
    global topics
    contextUI = ''
    if id != None:
        contextUI = f'''
        <li>
            <form action="/delete/" method="post">
                <input type="hidden" name="id" value={id}>
                <input type="submit" value="delete">
            </form>
        </li>
        <li><a href="/update/{id}">update</a></li>
        '''

    ol = ''
    for topic in topics:
        ol += f'<li><a href="/read/{topic["id"]}">{topic["title"]}</a></li>'
    return f'''
    <html>
    <body>
        <h1><a href="/"> Django </a></h1>
        <ol>
            {ol}
        </ol>
        {articleTag}
        <ul>
            <li><a href="/create/">create</a></li>
            {contextUI}
        </ul>
    </body>
    <html>
    '''


def index(request):
    article = '''
    <h2> Welcome </h2>
    Hello, Django
    '''
    return HttpResponse(HTMLTemplate(article))

@csrf_exempt
def create(request):
    global nextId
    if request.method == 'GET':
        article ='''
            <form action = "/create/" method="post">
                <p><input type="text" name = "title" placeholder="title"></p>
                <p><textarea name = "body" placeholder = "body"></textarea></p>
                <p><input type="submit"></p>
            <form>
        '''
        return HttpResponse(HTMLTemplate(article))
    elif request. method == 'POST':
        title = request.POST['title']
        body = request.POST['body']
        newTopic = {"id": nextId, "title" : title, "body" : body}
        topics.append(newTopic)
        url = '/read/'+str(nextId)
        nextId = nextId + 1
        return redirect(url)

@csrf_exempt
def delete(request):
    global topics
    if request.method == 'POST':
        id = request.POST['id']
        newTopics =[]
        for topic in topics:
            if topic['id'] != int(id):
                newTopics.append(topic)
        topics = newTopics
        return redirect('/')
@csrf_exempt
def update(request, id):
    global topics
    if request.method == 'GET':
        for topic in topics:
            if topic['id'] == int(id):
                selectedTopic = {
                    "title":topic['title'],
                    "body":topic['body']
                }
        article = f'''
            <form action = "/update/{id}/" method="post">
                <p><input type="text" name = "title" placeholder="title" value={selectedTopic["title"]}></p>
                <p><textarea name = "body" placeholder = "body">{selectedTopic['body']}</textarea></p>
                <p><input type="submit"></p>
            <form>
        '''
        return HttpResponse(HTMLTemplate(article, id))

    elif request.method == 'POST':
        title = request.POST['title']
        body = request.POST['body']
        for topic in topics:
            if topic['id'] == int(id):
                topic['title'] = title
                topic['body'] = body
        return redirect(f'/read/{id}')


def read(request, id):
    global topics
    article = ''
    for topic in topics:
        if topic['id'] == int(id):
            article = f'<h2>{topic["title"]}</h2>{topic["body"]}'
    return HttpResponse(HTMLTemplate(article, id))

 

myapp의 urls.py

from django.urls import path
from myapp import views

urlpatterns = [
    path('', views.index),
    path('create/', views.create),
    path('read/<id>/', views.read),
    path('update/<id>/', views.update),
    path('delete/', views.delete)
    
]

 

<14강. 수업을 마치며 >

앞으로의 방향

1. Database(데이터베이스) : 우리는 정보를 메모리에 보관함. 그래서 app이 재실행될때마다 데이터가 리셋됨. 정보를 영구적으로 보관하고 싶을 때 데이터 베이스 이용.  빠른 속도로 필요한 정보를 검색할 수도 있음. 이런 능력을 어플리케이션에게 부여하고 싶으면 데이터베이스 공부. 장고에는 Model이라는 기능이 내장되어 있음. 이를 이용하면 데이터베이스를 매우 쉽게 사용 가능

2. Security(웹 보안): 이전에 만든 제목과 내용 폼에 다음과 같이 입력

script 태그는 자바스크립트를 실행하는 코드, alert은 경고창을 띄어주는 자바스크립트 코드, location.href는 그 뒤의 값으로 브라우저를 리디렉션시키는 코드임. 방문자가 제출버튼을 누르면 (이 코드가 코드 속에 삽입되어) 실행되게 될 것임. 이 페이지를 삭제하는 코드나 페이지 정보를 어딘가로 전송하는 코드가 있을 수도 있음. 웹과 관련된 보안을 공부해야 함. 

 

3. Template engine(템플릿 엔진): 웹 애플리케이션은 html코드를 생성해주는 기계임. 우리의 코드는 파이썬 코드와 html 코드가 뒤섞여 있음. 가독성이 떨어지고 실수하기 쉬움. 이때 Template engine을 사용함. 별도의 html 파일을 만들고 그 안에 변경되어야 하는 부분만 약속된 문법에 맞게 코드 추가. 파이썬 코드와 html 코드를 분리할 수 있다는 점에서 더욱 편리하게 코딩 작업 가능 

 

지식지도 소말: https://seomal.com/map/1/174