Django 소개


참고 강의 : (유튜브)생활코딩 - Python Django Framework


1. Web Framework

  • 웹 애플리케이션을 만들기 위해 만들어둔 공통적인 framework

  • 웹 페이지를 만드는 프로그램을 만들고,

    요청이 들어오면 해당 프로그램으로 새 웹페이지를 만들어줌!

  • ex) Django


django 설치 : pip install django


django-admin

  • sub명령들 확인


django-admin startproject myproject .

  • 현재 경로(.)에 myproject라는 이름의 프로젝트 폴더 생성
  • 생성되는 파일/폴더
    • myproject (폴더)
      • settings.py : 프로젝트를 운영하는데 필요한 설정들
      • urls.py: 사용자가 접속하는 path를 routing해주는 역할
    • manage.py
      • 프로젝트 운영에 필요한 util 파일


python3 manage.py

  • 사용 가능한 명령어들 확인 가능


python manage.py runserver

  • django 기본 서버 : http://127.0.0.1:8000/

figure2

figure2

  • ctrl +C : 서버 종료


이미 8000번 포트가 사용 중이라면? 다른 포트 사용 가능!

python manage.py runserver 8888


figure2


2. 전체적인 Framework

  • 사용자가 접속하면, project 내의 urls.py는 적절한 app으로 보내줌
  • 해당 app안에 있는 urls.py는 적당한 view 내의 함수로 보내줌
  • model을 통해서 DB에 접속하고, DB에서 받아온 정보를 (html/json/xml 등의 형태로) 다시 응답해줌

figure2


app 만들기

django-admin startapp myapp

  • myapp이라는 이름의 app을 생성(시작)


Routing

사용자가 접속한 경로를 적절한 곳으로 보내주기


myproject 폴더 내의 urls.py

  • urlpatterns 라는 리스트를 반드시 가지고 정의해야하고,

    그 안에는 routing에 관한 정보가 담겨 있어야한다.


ex) http://127/0.0.1/에 접속했을 때, myapp내의 view로 routing시켜주려면?

ex) `http://127/0.0.1/temp/에 ~ myapp2~

[ myproject 내의 urls.py ]

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('myapp.urls')),
    path('temp', include('myapp2.urls'))
]
  • 이와 같이 수정한 뒤, 위 urls.py파일을 복사해서 myapp내에 붙여넣는다.


[ myapp 내의 urls.py ]

ex) http://127/0.0.1/create에 접속했을 때

ex) http://127/0.0.1/read/1에 접속했을 때

urlpatterns = [
    path(''),
    path('create/'),
    path('read/1/'),
]


[ myapp 내의 views.py ]

  • index함수를 실행하면, Welcome! 이 뜨도록 한다.
  • create함수를 실행하면, Create! 이 뜨도록 한다.
  • read함수를 실행하면, Read! 이 뜨도록 한다.
from django.shortcuts import render, HttpResponse

def index(request):
    return HttpResponse('Welcome!')

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

def read(request):
    return HttpResponse('Read!')


만약, 사용자가….

  • 1) 아무런 경로를 지정하지 않고 접속할 경우 : views 내의 index 함수로 보내줌
  • 2) create 경로로 접속할 경우 : views 내의 create 함수로 보내줌
  • 3) read/1 경로로 접속할 경우 : views 내의 read함수로 보내줌
from myapp import views

urlpatterns = [
    path('', views.index),
    path('create/', views.create),
    path('read/1/', views.read),
]

figure2

figure2

figure2


궁금증 : 저기서, read/1에서 1대신 다른 (가변적인) 숫자들이 와도 되지 않을까?

[ myapp 내의 urls.py ] 수정하기

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


[ myapp 내의 views.py ] 수정하기

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


Web Server vs Web Application Server

Web Server :

  • 필요로 하는 웹을 미리 만들어놔야함

  • static & 빠르다


Web Application Server :

  • 웹 페이지를 생성해내는 “프로그램” 하나를 만들어냄
    • view.py 하나만 바꾸면 전부 변경 가능!
  • dynamic & 느리다


figure2


3. CRUD

(1) Read

from django.shortcuts import render, HttpResponse

topic_text1 = {
    'id':1,
    'title':'Routing',
    'body':'Routing is ...'
}

topic_text2 = {
    'id':2,
    'title':'View',
    'body':'View is ...'
}

topic_text3 = {
    'id':3,
    'title':'Model',
    'body':'Model is ...'
}

topics = [topic_text1, topic_text2, topic_text3]

def index(request):
    global topics
    ol = ''
    for topic in topics:
        ol += f'<li><a href="/read/{topic["id"]}">{topic["title"]}</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)


결과 :

  • 1) 메인 화면에 접속할 경우
  • 2-1) Routing 클릭했을 경우 : 127.0.0.1:8000/read/1/로 라우팅
  • 2-2) Routing 클릭했을 경우 : 127.0.0.1:8000/read/2/로 라우팅
  • 2-3) Routing 클릭했을 경우 : 127.0.0.1:8000/read/3/로 라우팅

figure2


우선, 위의 HttpResponse~ 내의 내용들이 매우 길기 때문에, 함수로 만들 것이다.

( + “create 링크” & “delete 폼” 맨 하단에 생성 )

  • index 함수 수정
  • HTMLTemplate 함수 생성
def HTMLTemplate(articleTag):
    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><a href="/">Django</a></h1>
    	<ul>
    		{ol}
    	</ul>
    	{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))


이제 read함수를 수정하면 될 것이다!

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


(2) Create (Write)

목표 : 다음과 같이 입력란(폼)을 만들고, 제출 버튼을 누르면, topics 리스트에 새로운 내용이 추가되도록!

figure2


def create(request):
    article = '''
    	<form action="/create/">
	    	<p><input type="text" name="title" placeholder="title"></p>
    		<p><textarea type="text" name="body" placeholder="body"></textarea></p>
    		<p><input type='submit'></p>
		</form>
    '''
    return HttpResponse(HTMLTemplate(article))
  • <form> : 담겨있는 내용들을 원하는 path로 보내주기 위함

figure2


해당 form안에 내용을 작성해서 “제출”을 누르면…?

[ Request Method ] GET vs POST

  • GET : 주로 내용을 읽어들일 때 ( 내용 숨김 X )
    • http://localhost:8000/read/1/
    • http://localhost:8000/read/?id=1/ ( query string )
  • POST : 주로 내용을 작성할 때 ( 내용 숨김 O )
    • header안에 내용을 숨겨서 보내기!
    • <form action="/create/" method="post">


에러 방지 위해… ( 내용 ski p )

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 type="text" name="body" placeholder="body"></textarea></p>
    		<p><input type='submit'></p>
		</form>
    '''
    return HttpResponse(HTMLTemplate(article))
  • @csrf_exempt 를, 면제시키고 싶은 함수 앞에 decorator로 넣기


GET & POST 방식 다르게 처리하기

@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_to_move = '/read/' + str(nextId)
        nextId += 1
        return redirect(url_to_move)


(3) Delete

  • HttpTemplate함수 내에, “delete 폼”을 생성한다.
  • read 템플릿에서, id값도 반환해서 뭘 삭제할지 알려준다.
  • delete 버튼은,
    • home 화면에서는 보이지 않고
    • 상세 링크화면에서만 보이도록!
def HTMLTemplate(articleTag, id=None):
    global topics
    
    # (case 1) home 화면일 경우
    context_UI = ''
    
    # (case 2) 상세 링크일 경우
    if id!=None:
        context_UI = 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 HttpResponse(f'''
    <html>
    <body>
    	<h1><a href="/">Django</a></h1>
    	<ul>
    		{ol}
    	</ul>
    	{articleTag}
        <ul>
            <li><a href="/create/">create</a></li>
			{context_UI}
        </ul>
    </body>
    </html>
    ''')
                        
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))


figure2


delete 함수

@csrf_exempt
def delete(request):
    global topics
    if request.method == 'POST':
        id = request.POST['id']
        # 해당 id 제거 = 해당 id 제외 나머지만 remain
        remained_Topics = []
        for topic in topics:
            if topic['id'] != int(id):
                remained_Topics.append(topic)
        topics = remained_Topics   
        return redirect('/')

urls.py에 방금 만든 delete함수 불러오는 path 추가하기

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


(4) Update

delete와 마찬가지로, context_UI에 update 버튼도 추가

( context_UI : home화면에서는 안뜨고, 상세 링크에서만 뜨는 UI )

def HTMLTemplate(articleTag, id=None):
    global topics
    
    # (case 1) home 화면일 경우
    context_UI = ''
    
    # (case 2) 상세 링크일 경우
    if id!=None:
        context_UI = 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 HttpResponse(f'''
    <html>
    <body>
    	<h1><a href="/">Django</a></h1>
    	<ul>
    		{ol}
    	</ul>
    	{articleTag}
        <ul>
            <li><a href="/create/">create</a></li>
			{context_UI}
        </ul>
    </body>
    </html>
    ''')


update함수 구현하기

@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}')


urls.py에 방금 만든 update함수 불러오는 path 추가하기

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