'웹 공부 자료' 시리즈Chap 5. 새로운 애플리케이션 만들기

  • books 애플리케이션

    • 책, 저자, 출판사 정보를 관리하는 웹 애플리케이션
  • 새로운 애플리케이션을 만들기 위해서는 상위에 프로젝트를 먼저 만들어야 한다.

    • 프로젝트
      • 애플리케이션

1. 설계 순서

  • 애플리케이션 설계하기
  • 프로젝트 뼈대 만들기
  • 애플리케이션
    • Model 코딩하기
    • URLconf 코딩하기
    • View 코딩하기
    • Template 코딩하기

2. 애플리케이션 설계하기

books 애플리케이션의 내용은 책, 저자, 출판사의 정보를 관리(입력, 수정, 삭제) 할 수 있는 웹 애플리케이션

A. UI

B. Model

a. Book 테이블 설계
컬럼명타입제약조건설명
idintegerNotNull, PK, AUtoIncrementPrimary Key
titlevarchar(100)NotNull책 제목
authorsintegerNotNull, MTM(Author.id), indexMany-To-Many
publisherintegerNotNull, FK(Publisher.id), indexForeign Key
publicationdateNotNull책 출판일
b. Author 테이블 설계
컬럼명타입제약조건설명
idintegerNotNull, PK, AutoIncrementPrimary Key
salutationvarchar(100)NotNull저자 인사말
namevarchar(50)NotNull저자 성명
emailemailNotNull저자 이메일
c. Publisher 테이블 설계
컬럼명타입제약조건설명
idintegerNotNull, PK, AutoIncrementPrimary Key
namevarchar(50)NotNull출판사 이름
addressvarchar(200)NotNull출판사 주소
websiteurlNotNull출판사 홈페이지

C. View 흐름

3. 프로젝트 뼈대 만들기 (애플리케이션 추가)


python manage.py startapp books

setting.pyINSTALLED_APPS 항목에 books 애플리케이션 등록하기.


INSTALLED_APPS = [  

    (...)
    
     'books.apps.BooksConfig',  
]

4. 애플리케이션 - Model 코딩하기

A. model.py 파일에 테이블 정의하기


from django.db import models  
  
class Book(models.Model):  
    title = models.CharField(max_length=100)  
    authors = models.ManyToManyField('Author')  
    publisher = models.ForeignKey('Publisher', on_delete=models.CASCADE)  
    publication_date = models.DateField()  
  
    def __str__(self):  
        return self.title  
  
class Author(models.Model):  
    name = models.CharField(max_length=50)  
    salutation = models.CharField(max_length=100)  
    email = models.EmailField()  
  
    def __str__(self):  
        return self.name  
  
class Publisher(models.Model):  
    name = models.CharField(max_length=50)  
    address = models.CharField(max_length=100)  
    website = models.URLField()  
  
    def __str__(self):  
        return self.name

더 많은 필드 타입은 Django documentation | Django documentation | Django (djangoproject.com) 을 참고.

ForeignKey

N:1

여러 권의 Book과 하나의 Publisher 간에 관계

한 권의 Book은 하나의 Publisher에 관련, 하나의 Publisher는 여러 권의 Book과 관련.

필수

on_delete 옵션 필수 지정.

상대 테이블의 레코드가 삭제될 때 본 테이블에서의 동작을 지정하는 옵션

CASCADE 옵션은 연쇄 삭제.

Book:Publisher = N:1 이면, Publisher의 레코드가 삭제되면 관련 Book 레코드도 삭제됨.

ManyToManyField

N:N

여러 권의 Book과 여러 명의 Author 간의 관계

한 권의 Book에 여러 명의 Author가 존재 가능, 한 명의 Author는 여러 권의 Book을 작성 가능

OneToOneField

1:1

B. admin.py 파일에 등록하기


from django.contrib import admin  
from books.models import Book, Author, Publisher  
  
admin.site.register(Book)  
admin.site.register(Author)  
admin.site.register(Publisher)

C. 데이터베이스에 반영


python manage.py makemigrations
python manahe.py migrate

5. 애플리케이션 (URLconf 코딩하기)

A. mysite/urls.py


from django.contrib import admin  
from django.urls import path, include  
  
urlpatterns = [  
    
    (...)
    
    path('books/', include('books.urls')),

B. books/urls.py


from django.urls import path  
from . import views  
  
app_name = 'books'  
  
urlpatterns = [  
    # /books/  
 path('', views.BooksModelView.as_view(), name='index'),  
  
 # /books/book/  
 path('book/', views.BookList.as_view(), name='book_list'),  
  
 # /books/author/  
 path('author/', views.AuthorList.as_view(), name='author_list'),  
  
 # /books/publisher/  
 path('publisher/', views.PublisherList.as_view(), name='publisher_list'),  
  
 # /books/book/99/  
 path('book/<int:pk>/', views.BookDetail.as_view(), name='book_detail'),  
  
 # /books/author/99/  
 path('author/<int:pk>/', views.AuthorDetail.as_view(), name='author_detail'),  
  
 # /books/publisher/99/  
 path('publisher/<int:pk>/', views.PublisherDetail.as_view(), name='publisher_detail'),  
]

  • 7개의 URL 정의
  • 7개의 클래스형 뷰 정의
    • 클래스형 뷰로 정의하기 위해 각 URL에 따른 뷰 클래스 및 as_view() 메소드를 지정
  • 7개의 템플릿 파일 필요함.

하나의 뷰에서 여러 개의 html 파일을 사용할 수 있다.

동일한 html 파일을 여러 개의 뷰에서 사용할 수도 있다.

6. 애플리케이션 (클래스형 View 코딩하기)

A. views.py


from django.views.generic.base import TemplateView  
from django.views.generic import ListView  
from django.views.generic import DetailView  
from books.models import Book, Author, Publisher  
  
#--- TemplateView  
class BooksModelView(TemplateView):  
    template_name = 'books/index.html'  
  
 def get_context_data(self, **kwargs):  
        context = super().get_context_data(**kwargs)  
        context['model_list'] = ['Book', 'Author', 'Publisher']  
  
        return context  
  
#--- ListView  
class BookList(ListView):  
    model = Book  
  
class AuthorList(ListView):  
    model = Author  
  
class PublisherList(ListView):  
    model = Publisher  
  
#--- DetailView  
class BookDetail(DetailView):  
    model = Book  
  
class AuthorDetail(DetailView):  
    model = Author  
  
class PublisherDetail(DetailView):  
    model = Publisher

아래는 코드 설명

a. TemplateView 상속

#--- TemplateView  
class BooksModelView(TemplateView):  

특별한 로직이 없고 템플릿 파일만을 렌더링하는 경우 TemplateView 제네릭 뷰를 상속받아 사용

TemplateView를 사용하는 경우 template_name 클래스 변수를 오버라이딩해서 지정해야 함.

템플릿 시스템으로 넘겨줄 컨텍스트 변수가 있는 경우 get_context_data() 메소드를 오버라이딩해서 정의

b. TemplateView 상속받고 get_context_data 오버라이딩할 때.

 def get_context_data(self, **kwargs):  
        context = super().get_context_data(**kwargs)  
        context['model_list'] = ['Book', 'Author', 'Publisher']  
  
        return context  

첫 줄에 super() 메소드 호출해야 함.

books 애플리케이션의 첫 화면에 테이블 리스트를 보여주기 위해 컨텍스트 변수 model_list에 담아 템플릿 시스템에 넘겨줌.

return 문장도 필수

c. ListView 제네릭 뷰

#--- ListView  
class BookList(ListView):  
    model = Book  
  
class AuthorList(ListView):  
    model = Author  
  
class PublisherList(ListView):  
    model = Publisher  

ListView를 상속받는 경우 객체가 들어있는 리스트를 구성해서 컨텍스트 변수로 템플릿 시스템에 넘겨주면 된다.

리스트를 테이블에 들어있는 모든 레코드를 가져와 구성하는 경우 모델 클래스명(테이블명)만 지정해주면 된다.

장고에서 디폴트로 지정해주는 속성 2가지

  1. 컨텍스트 변수로 object_list 를 사용
  2. 템플릿 파일을 (모델명 소문자)_list.html 형식으로 지정.
d. DetailView 제네릭 뷰

#--- DetailView  
class BookDetail(DetailView):  
    model = Book  
  
class AuthorDetail(DetailView):  
    model = Author  
  
class PublisherDetail(DetailView):  
    model = Publisher

DetailView를 상속받는 경우 특정 객체 하나를 컨텍스트 변수에 담아서 템플릿 시스템에 넘겨주면 된다.

테이블에서 Primary Key로 조회해서 특정 객체를 가져오는 경우 모델 클래스명(테이블명)만 지정한다.

조회 시 사용할 PrimaryKey 값은 URLconf에서 추출하여 뷰로 넘어온 파라미터를 사용한다.

Primary Key 값은 URLconf에서 넘겨받는데, 이에 대한 처리는 DetailView 제네릭 뷰에서 알아서 처리한다.

장고에서 디폴트로 지정해주는 속성 2가지

  1. 컨텍스트 변수로 object를 사용
  2. 템플릿 파일을 (모델명 소문자)_detail.html 형식으로 지정.

7. 애플리케이션 (Template 코딩하기)

A. index.html


{% extends "base_books.html" %}  
  
{% block content %}  
    <h2>Books Management System</h2>  
 <ul> {% for modelname in model_list %}  
{% with "books:"|add:modelname|lower|add:"_list" as urlvar %}  
        <li><a href="{% url urlvar %}">{{ modelname }}</a></li>  
{% endwith %}  
        {% endfor %}  
    </ul>  
{% endblock content %}

base_books.html 템플릿을 상속받아 content 블록만 재정의

뷰로부터 model_list 컨텍스트 변수를 전달받아 model_list에 들어있는 모델명들을 순회하면서 화면에 하나씩 보여준다.

모델명 클릭 시 접속한 URL을 추출하기 위해 {% url urlvar %} 템플릿 태그 사용

urlvar 인자는 {% with %} 태그를 사용하여 정의

add, lower 템플릿 필터를 사용하여 모델명을 소문자로 변환하고, 필요한 문자열을 붙여준다.

B. *_list.html

공통적으로 base_books.html 템플릭 상속받음

뷰로부터 object_list 컨텍스트 변수를 전달받아 object_list에 들어 있는 객체들을 순회하면서 하나씩 보여줌


<li><a href="{% url 'books:book_detail' book.id %}">{{ book.title }}</a></li>

화면에 book 객체title 속성(book.title)을 표시하고 텍스트를 클릭할 경우, books:book_detail URL 패턴으로 웹 요청을 보낸다.

a. book_list.html

{% extends "base_books.html" %}  
  
{% block content %}  
    <h2>Book List</h2>  
 <ul> {% for book in object_list %}  
            <li><a href="{% url 'books:book_detail' book.id %}">{{ book.title }}</a></li>  
 {% endfor %}  
    </ul>  
{% endblock content %}

b. author_list.html

{% extends "base_books.html" %}  
  
{% block content %}  
    <h2>Author List</h2>  
 <ul> {% for author in object_list %}  
            <li><a href="{% url 'books:author_detail' author.id %}">{{ author.name }}</a></li>  
 {% endfor %}  
    </ul>  
{% endblock content %}

c. publisher_list.html

{% extends "base_books.html" %}  
  
{% block content %}  
    <h2>Publisher List</h2>  
 <ul> {% for publisher in object_list %}  
            <li><a href="{% url 'books:publisher_detail' publisher.id %}">{{ publisher.name }}</a></li>  
 {% endfor %}  
    </ul>  
{% endblock content %}

C. *_detail.html

공통적으로 base_books.html 템플릭 상속받음

뷰로부터 object 컨텍스트 변수를 전달받아 object에 들어 있는 객체들을 순회하면서 하나씩 보여줌


{% if not forloop.last %}, {% else %}{ endif %}

object.author.all() 구문은** object에 연결된 authors 모두**라는 의미.

템플릿 문법상 메소드 호출을 의미하는 ()는 사용하지 않는다.

a. book_detail.html

{% extends "base_books.html" %}  
  
{% block content %}  
<h1>{{ object.title }}</h1>  

<li>Authors: {% for author in object.authors.all %} {{ author }} {% if not forloop.last %}, {% else %}{ endif %} {% endfor %} </li> <li>Publisher: {{ object.publisher }}</li> <li>Publication date: {{ object.publication_date }}</li> {% endblock content %}
b. author_detail.html

{% extends "base_books.html" %}  
  
{% block content %}  
<h1>{{ object.name }}</h1>  
<p>{{ object.salutation }}</p>  
<li>Email: {{object.email }}</li>  
{% endblock content %}

c. publisher_detail.html

{% extends "base_books.html" %}  
  
{% block content %}  
<h1>{{ object.name }}</h1>  
<p>{{ object.website }}</p>  
<li>Address: {{ object.address }}</li>  
{% endblock content %}

8. 애플리케이션 (Template 상속 기능 추가)

base.html 템플릿에서는 상속용으로 {% block title %}, {% block sidebar %}, {% block content %} 3개의 블록을 정의.

base_books.html 템플릿은 base.html 템플릿을 상속받아서 title 블록과 sidebar 블록을 재정의.

위에 2개의 템플릿은 각 템플릿의 부모 템플릿 역할을 하며, 개별 애플리케이션 템플릿이 아니라 공통 템플릿

그래서 최상위 template 디렉토리에 공통 템플릿을 생성함. 위치는 settings.py 파일에서 TEMPLATES 항목으로 정의한 디렉토리.


TEMPLATES = [  
    {  
        
        (...)
        
        'DIRS': [os.path.join(BASE_DIR, 'templates')],  

        (...)

]

base_books.html 파일은 books 애플리케이션에 종속된 것으로 보고 books 앱 템플릿 디렉토리에 두기도 함.

A. base.html 템플릿


<!DOCTYPE html>  
<html lang="en">  
<head>  
 {% load static %}  
    <!-- C:\python\anaconda3_64\envs\Study_web\Lib\site-packages\django\contrib\admin\static\admin\css -->  
 <link rel="stylesheet" href="{% static 'admin/css/base.css' %}" />  
 <title>{% block title %}My Amazing Site{% endblock %}</title>  
</head>  
<body>  
 <div id="sidebar">  
 {% block sidebar %}  
        <ul>  
 <li><a href="/">Project_Home</a></li>  
 <li><a href="/admin/">Admin</a></li>  
 </ul> {% endblock %}  
        
</div> <div id="content"> {% block content %}{% endblock %} </div> </body> </html>

{% load static %} 템플릿 태그static이라는 사용자 정의 태그를 로깅해주고, {% static %} 사용자 정의 태그를 통해 admin/css/base.css 스타일시트 파일을 찾게 된다.

이는 장고의 Admin 사이트에서 사용하는 스타일시트를 사용하여 룩앤필을 보여주는 효과를 준다.

B. base_books플릿


{% extends "base.html" %}  
  
<title>{% block title %}Books Application Sit{% endblock %}</title>  
  
{% block sidebar %}  
{{ block.super }}  
<ul>  
 <li><a href="/books/">Books_Home</a></li>  
</ul>  
{% endblock %}

base_books.html 템플릿은 base.html 템플릿을 상속받아서** title 블록과 sidebar 블록을 재정의**한다.

{{ block.super }} 템플릿 변수는 부모 base.html 템플릿에서 정의한 내용을 재사용 한다는 의미

9. 결과

여기까지는 정상적으로 출력되고.

여기는 구현하지 않아서 출력되지 않는다.

참고문헌

김석훈, "Django로 배우는 쉽고 빠른 웹개발 파이썬 웹프로그래밍", 개정판, 4쇄, 한빛미디어, 2020년

#Django #애플리케이션 #View #Model #UI #Template

0분전
작성된 댓글이 없습니다. 첫 댓글을 달아보세요!
    댓글을 작성하려면 로그인이 필요합니다.