본문 바로가기

IT/Django

Django 에서 어플리케이션 설정

이 포스트는 Django 에서 어플리케이션을 추가 한 이후, 어플리케이션을 만들어가는 과정을 기술하고자 합니다.

만약 어플리케이션을 어떻게 추가하는지 모르시겠다면 이전 포스트를 확인 부탁 드립니다.


1. Django의 프로젝트 및 어플리케이션 구조

바로 어플리케이션 설정으로 들어가면 많은 혼란을 야기 시킬듯 하여, 프로젝트 및 어플리케이션에 대한 구조를 간단히 설명 드리겠습니다.

의의 그림을 보시면 하나의 프로젝트에 여러개의 어플리케이션이 존재하는 구조입니다. 즉, 어플리케이션이 존재하기 위해서는 반드시 프로젝트가 존재 해야 한다는 의미 입니다.


1.1 프로젝트와 어플리케이션의 디렉토리 구조

프로젝트와 어플리케이션은 상하 관계이지만, 디렉토리 구조를 보면 혼돈이 시작 됩니다.


보면 WEBROOT 아래 Project_Home 이 있고, 다시 WEBROOT 아래 APP1~4 까지 존재 합니다.

즉, 프로젝트 홈과 어플리케이션의 디렉토리 구조는 동일 선상에 존재 합니다.


1.2 각 디렉토리 내 저장 파일 및 의미

 디렉토리 및 파일명

 기능 및 역할

 WEBROOT/manage.py

django 의 프로젝트 관리 프로그램 으로, 서버 기동 및 DB 생성 등의 역할을 한다. 

 WEBROOT/Project_Home/settings.py

 프로젝트의 환경 설정을 하는 파일

 WEBROOT/Project_Home/urls.py

URL 패턴에 따라서 연결할 어플리케이션을 설정하는 파일 

 WEBROOT/APP1~4/urls.py

각 어플리케이션의 url 패턴에 따라 어느 view 로 연결할지 설정하는 파일 

 WEBROOT/APP1~4/views.py

view가 호출 될때 처리할 비즈니스 로직이 구현 되어 있으며, 노출할 화면 템플릿이 설정되어 있는 파일 

 WEBROOT/APP1~4/models.py

해당 어플에서 사용할 DB 테이블 구조가 명시된 파일이며, 해당 디렉토리에서 데이터를 접근 할때 사용할 함수도 같이 정의되어 있는 파일 

 WEBROOT/APP1~4/templates/

 해당 어플에서 views.py 설정된 템플릿 파일이 저장될 디렉토리


1.3 HTTP 요청의 흐름

Django 는 웹 서버로써 단독 기동도 가능 하므로, Django 를 단독 기동 하였을때, 요청은 다음과 같은 과정을 거쳐서 처리가 됩니다.



  1. HTTP 요청 인입
  2. 요청 URL 패턴을 보고 처리할 Application 판단
  3. 요청 URL 패턴을 보고 처리할 view 함수 호출
  4. 비즈니스 로직 및 모델 처리
  5. 응답할 html 템플릿으로 응답 결과 생성
  6. 요청자에게 응답


어플리케이션이 추가 되면 Project_Home/urls.py 에 URL 패턴을 추가 하여야 하며, 각 어플에서 제공할 페이지의 URL 패턴을 application의 urls.py 에 등록 하여 정상적으로 동작 하게 됩니다.


2. 어플리케이션 설정

Django 에서 Project는 하나의 사이트를 의미 하며, 어플리케이션은 각 개별 기능이라고 보시면 됩니다.
예를 들어, 블로그를 예를 들면, 블로그는 하나의 프로젝트가 되며, 블로그의 주인이 게시물 및 블로그를 관리 하는 페이지와 일반 이용자가 들어와서 게시물을 읽는 페이지가 각각의 어플리케이션이라고 보시면 됩니다.

 여기에서는 하나의 블로그를 만들어서 일반 이용자가 보는 페이지를 만들고, 해당 페이지에서 CRUD가 되도록 기능을 추가 해보도록 하겠습니다.
물론 django 에서 프로젝트를 만들고, 어플리케이션을 추가 하면, 관리 페이지가 별도로 생기긴 합니다만, 해당 관리 페이지를 이용하지 않고, 자체적인 CRUD 를 발생 시켜 보겠습니다. 단순히, CRUD 를 처리 하기 위한 절차를 위해서 디자인이나, 세션, 로그인의 기능은 전혀 없슴을 이해 부탁 드립니다.

프로젝트 생성 에서 부터 어플리케이션 생성까지는 이전 포스트에서 확인 부탁 드립니다.

2.1 프로젝트에서 어플리케이션 URL 패턴 설정

HTTP 요청 인입시, Django 에서 어떤 Application 으로 요청을 전달 해야 할지 판단을 위새서 URL 패턴을 등록 해줘야 합니다.
project_home/urls.py의 내용을 아래와 같이 수정 했습니다.

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

urlpatterns = [
     path('', include('blog.urls')),
]


위 수정 내용의 의미는 http://ip:port/ 로 접속 하였을 때 application blog의 urls.py  파알을 이용하라는 의미 입니다.


2.2 어플리케이션에서 사용할 모델(DB 테이블) 생성

어플리케이션에서 사용할 모델(테이블)을 생성할 차례입니다. 직접 쿼리를 실행 하여 생성하는 것이 아니라 , 프로젝트의 관리 프로그램을 이용하여 만들수 있습니다.


먼 테이블 구조의 모델 객체를 생성 하여야 합니다.

${application}/models.py 파일을 다음과 같으 수정 합니다.

from django.db import models

from django.utils import timezone



class Post(models.Model):

    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)  #작성자

    title = models.CharField(max_length=200)                                   #게시물 제목

    text = models.TextField()                                                      #본문

    created_date = models.DateTimeField(                                      #게시물 생성일자

            default=timezone.now)

    published_date = models.DateTimeField(                                   #게시물 공개여부

            blank=True, null=True)


    def publish(self):

        self.published_date = timezone.now()

        self.save()


    def __str__(self):

        return self.title


Post란 모델은 위와 같은 구조로 생성 되었고, 명시되어 있지 않지만, pk 란 필드가 추가 되어 있습을 기억해야 합니다.

모델의 구조를 만들었으니, 이제 실제로 DB 에 해당 구조를 생성하도록 하겠습니다. 아래의 커맨드를 입력 하시면 됩니다.

cd ${WEBROOT}

python3 manage.py makemigrations

python3 manage.py migrate


2.3 사이트 사용자 계정 생성

사이트 계정을 별도 페이지를 만들지 않고, 추가가 가능 합니다. 이게 가능 한게 장고에서 기본적으로 사용자 정보 모델을 제공 하고 있습니다.

해당 모델에서 제공하지 않는 정보를 관리하고자 한다면, 추가로 사용자 추가 정보 모델을 만들어서 사용하면 가능 할듯 합니다.

python3 manage.py createsuperuser


2.4 어플리케이션의 url 패턴 추가

이번에 추가한 어플리케이션인 블로그에서 사용할 URL 패턴을 등록 해야 합니다.

블로그에서 필요한 기능은 게시물 목록 노출/게시물 작성/게시물 수정/게시물 읽기/게시물 삭제 의 기능이 있어야 할듯 합니다.

하여 각 기능별 URL 패턴을 아래와 같이 정의 하겠습니다.


기능 

URL  

 게시물 목록

 / 

 게시물 작성

 /write 

 게시물 수정

 /update/${게시물번호} 

 게시물 읽기

 /post/${게시물번호}

 게시물 삭제

 /delete/${작성자}/${게시물번호}

 게시물 저장 /save 

위의 정의된 URL 패턴으로 등록 하도록 하겠습니다. application/urls.py 파일을 다음과 같이 수정 합니다.

from django.urls import path

from . import views


urlpatterns = [

    path('', views.post_list, name='post_list'),

    path('post/<int:pk>/', views.post_detail, name='post_detail' ),

path('update/<int:pk>/', views.post_update, name='post_update'),

path('write/', views.post_write, name='post_write'),

path('save/', views.post_save, name='post_save'),

path('delete/<str:id>/<int:pk>/', views.post_del, name='post_del'),

]

url 패턴에서 어떠한 파라미터를 받을지를 지정할 수 있습니다. update/ 이후에 나오는 <int:pk> 는 int 형으로 pk 라는 이름의 파라미터를 받겠다는 의미 입니다. 만약 파라미터를 받겠다고 지정 하였는데, 파라미터 없이 호출하면 404 Not Found 가 발생 하니 유의 하시기 바랍니다..


각 파라미터의 타입별 예약문자열은 다음과 같습니다.

 예약문자열

의미 

str

 일반 적인 문자열. '/' 을 제외한 문자열 

int

 정수형 숫자 

slug

 아스키코드, +, -,_  을 포함한 문자열 

 path

 '/' 포함한 문자열

uuid

 uuid 타입의 문자열


2.5 어플리케이션의 비즈니스 로직 구현 및 템플릿 지정

url에 따라 로직 분기를 등록 하였으니, 해당 로직을 구현할 차례 입니다. application/views.py를 다음과 같이 수정 합니다.

from django.shortcuts import render , get_object_or_404 , redirect

from django.utils import timezone

from django.contrib.auth.models import User

from .models import Post


# Create your views here.

def post_list(request):

    posts = Post.objects.order_by('-created_date')

    return render(request, 'blog/post_list.html', {'posts': posts})


def post_detail(request, pk):

    post = get_object_or_404(Post, pk=pk)

    return render(request, 'blog/post_detail.html', {'post': post})


def post_update(request, pk):

post = Post.objects.filter(pk=pk)

if len(post) == 0 :

return redirect("/")

else :

return render(request, 'blog/post_write.html', {'post': post[0]})

def post_write(request):

return render(request, 'blog/post_write.html', {})

def post_save(request):

pk = request.POST.get("pk", '')

try :

user = User.objects.get(username=request.POST.get("writter"))

if pk == '' :

Post.objects.create(author=user, title=request.POST.get("title", ''), text=request.POST.get("text", ''))

else :

post = Post.objects.get(author=user, pk=request.POST.get("pk", ''))

post.title = request.POST.get("title", '')

post.text = request.POST.get("text", '')

post.save()

except :

print("Post Svae Error:" + request.POST.get("writter") + "^" + request.POST.get("pk", ''))

return redirect("/")

def post_del(request, id, pk):

try :

user = User.objects.get(username=id)

post = Post.objects.get(author=user, pk=pk)

post.delete()

except Exception  as ex:

print("delete error:" + id + "^" + pk)


return redirect("/")


models.objects.get() 의 경우, 조회 결과가 없을 경우, 에러가 발생 합니다. 하여 try except로 감싸 줘서 처리를 해줘야 합니다.


2.6 템블릿 구성

각 기능별 화면을 구성할 html 을 render 에서 지정 하였습니다. 단, 경로는 앞에 templates 가 기본적으로 붙는거 같습니다.

하여 템플릿들은 모두 ${WEBROOT}/${application}/templates 아래 존재해야 합니다. 즉, render 함수에 지정된 'blog/post_write.html' 은 ${WEBROOT}/${application}/templates/blog/post_write.html 파일을 이용한다는 의미 입니다.


각 템플릿 파일을 보도록 하겠습니다.

2.6.1 base.html

모든 페이지의 기본 바탕이 되는 디자인이 있는 페이지 입니다.

{% load static %}

<html>

    <head>

        <title>PP's blog</title>

        <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">

        <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">

        <link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'>

        <link rel="stylesheet" href="{% static 'css/blog.css' %}">

    </head>

    <body>

    <div class="page-header">

        <h1><a href="/">PP's Blog</a></h1>

    </div>

    <div class="content container">

        <div class="row">

            <div class="col-md-8">

            {% block content %}

            {% endblock %}

            </div>

    <div>

            {% block comment %}

            {% endblock %}

            <div>

        </div>

    </div>

    </body>

</html>


이 페이지에서 주로 봐야 할 곳은 {% block content%}{% endblock%},{% block comment %}{% endblock%} 입니다. 일단 넘어가겠습니다.


2.6.2 post_list.html

게시물 목록을 노출할 html 소스 코드 입니다.

{% extends 'blog/base.html' %}


{% block content %}

   {% for post in posts %}

       <div class="post">

           <div class="date">

               {{ post.created_date }}

           </div>

           <h1><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h1>

       </div>

   {% endfor %}

   

{% endblock %}

{% extends 'blog/base.html' %}는 base.html 을 상송 받아서 사용하겠다는 의미 입니다.

또한 base.html 의 {% block content %}{% endblock %} 부분이 post_list.html 의 {% block content %}{% endblock %} 사이로 대체 됩니다.

{%%} dms python 구문으로 처리 되니 참고하여 주시고, {{...}}  는 안의 내용을 화면에 출력 합니다.


2.6.3 post_detail.html

게시물의 상세 내용을 노출 하는 html 소스 코드 입니다.

{% extends 'blog/base.html' %}


{% block content %}

    <div class="post">

        {% if post.created_date %}

            <div class="date">

                {{ post.created_date }}

            </div>

        {% endif %}

        <h1>{{ post.title }}</h1>

        <p>{{ post.text|linebreaksbr }}</p>

    </div>

{% endblock %}


{% block comment %}

{% load disqus_tags %}

{% set_disqus_url post_absolute_url %}

{% disqus_show_comments %}

{% endblock %}


내용은 2.6.2의 내용을 참고하시면 됩니다. 차이가 없이니..


2.6.4 post_write.html

게시물 작성 혹은 수정을 위해 사용되는 html 소스 코드 입니다.

{% extends 'blog/base.html' %}

{% block content %}

<form id=frm" method="post" action="/save/">

{% csrf_token %}

<input type="hidden" id="pk" name="pk" value="{% if post %}{{post.pk}}{%endif%}">

    <div class="post">

<div>

<p><span> 작성자 : </span><span><input type="text" name="writter"></span></p>

<p><span> 제목 : </span><span><input type="text" name="title"></span></p>

<p><span> 내 용 : </span><span><textarea name="text" rows="10" cols="50">{{post.text}}</textarea></span></p>

</div>

    </div>

<div>

<input type="submit" value="저장">

</div>

</form>

{% endblock %}

대체로 설명은 동일 하지만 이곳에서는 추가 설명이 하나 필요 합니다.

바로, {% csrf_token %} 구문 입니다. 이 구분은 form 을 post 방식으로 전달 할 경우, <form></form> 사이에 존재 해야만, 이동 페이지로 정상적으로 이동이 됩니다. 이점 꼭 기억하시기 바랍니다.


마치며

지금까지 python 기반 Django의 프로젝트 및 어플리케이션 구조와 간단한 블로그 어플리케이션을 만들어 보았습니다.

이는 웹 어플리케이션의 기본이 되는 형태이므로, 이걸 참고하여 더 복잡한 프로그램을 하시길 기원 합니다.


긴글 읽어 주셔서 감사합니다.

2019/03/06 - [IT/Django] - Django 설치 절차


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

크롤러를 Django에 추가하기  (0) 2019.04.10
Django 설치 절차  (0) 2019.03.06