앱 만들기 예제)
URL 설계
URL 패턴 | View name | template name |
/blog/ | PostLV | post_all.html |
/blog/post/ | PostLV | post_all.html |
/blog/post/django-example/ | PostDV | post_detail.html |
/blog/archive/ | PostAV | post_archive.html |
/blog/archive/2019 | PostYAV | post_archive_year.html |
/blog/archive/2019/nov | PostMAV | post_archive_month.html |
/blog/archive/2019/nov/10 | PostDAV | post_archive_day.html |
/blog/archive/today | PostTAV | post_archive_day.html |
/admin/ |
코딩 순서
- 뼈대 만들기
- startapp : 앱 생성
- settings.py : 앱 등록
- 모델
- models.py : 모델(테이블)정의
- admin.py : 사이트에 모델 등록
- makemigrations : 변경사항 추출
- migrate : 변경사항 반영
- URLconf
- urls.py : URL 정의
- 뷰
- views.py : 뷰 로직 작성
- 템플릿
- template 디렉터리 : html 등 작성
- 그외
뼈대 만들기
앱 생성
python manage.py startapp blog
앱 등록
settings.py
INSTALLED_APPS = [
....
'blog.apps.BlogConfig',
]
모델 정의
models.py
from django.db import models
from django.urls import reverse
class Post(models.Model):
title = models.CharField(verbose_name='TITLE', max_length=50)
# 페이지나 포스트를 설명하는 핵심 단어의 집합
# 제목의 단어들을 하이푼으로 연결해 생성, pk 대신 사용 가능
# allow_unicode : 한글 처리
slug = models.SlugField('SLUG',unique=True, allow_unicode=True,help_text='one word for title alias')
description = models.CharField('DESCRIPTION',max_length=100, blank=True,help_text='simple description text.')
content = models.TextField('CONTENT')
# auto_now_add : 객체가 생성될 때의 시각을 자동으로 기록
create_dt = models.DateTimeField('CREATE DATE',auto_now_add=True)
# auto_now : 데이터베이스에 저장될 때의 시각을 자동으로 기록 - 객체가 변경될 때의 시각 기록
modify_dt = models.DateTimeField('MODIFY DATE',auto_now=True)
# 필드 속성 외에 필요한 파라미터가 있을 때
class Meta:
verbose_name = 'post'
verbose_name_plural = 'posts' # 별칭을 복수로 가질 때
db_table = 'blog_posts'
ordering = ('-modify_dt',) # 내림차순 정렬
def __str__(self): # 뷰 출력
return self.title
def get_absolute_url(self):
return reverse('blog:post_detail',args=(self.slug,))
def get_previous(self):
return self.get_previous_by_modify_dt() # modify_dt 칼럼 기준으로 최신 포스트 반환
def get_next(self):
return self.get_next_by_modify_dt()
reverse() 함수 사용
URL 패턴을 만들어주는 장고 내장 함수
reverse('blog:post_list') # '/blog/'
reverse('blog:post_detail', args=[10]) # '/blog/10/' args 인자로 리스트 지정 필요
reverse('blog:post_detail', kwargs={'id':10}) # '/blog/10/'
reverse('/hello/') # NoReverseMatch 오류 발생
slug
slug = models.SlugField('SLUG',unique=True, allow_unicode=True,help_text='one word for title alias')
slug 칼럼은 제목의 별칭, SlugField에 unique 옵션을 추가해 특정 포스트를 검색할 때 기본키 대신 사용
allow_unicode=True
한글 처리 가능
def get_previous(self):
return self.get_previous_by_modify_dt() # modify_dt 칼럼 기준으로 최신 포스트 반환
def get_next(self):
return self.get_next_by_modify_dt()
-modify_dt 칼럼을 기준으로 포스트를 반환
사이트에 모델 등록
admin.py
from django.contrib import admin
from blog.models import Post
# admin 사이트에 보이도록 작성
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('id','title','modify_dt')
list_filter = ('modify_dt',)
search_fields = ('title','content') # 검색 박스 표시, title, content 칼럼에서 검색
prepopulated_fields = {'slug': ('title',)} # title 필드를 사용해 미리 채워지도록
데이터베이스 반영
테이블의 신규생성, 테이블의 정의 변경 등
데이터 변경이 필요한 사항이 있으면, 데이터베이스에 실제로 반영하는 작업
python manage.py makemigrations blog
python manage.py migrate
URLconf
Root urlconf 정의
include 사용
from django.contrib import admin
from django.urls import path , include # include 추가
from bookmark.views import BookmarkLV, BookmarkDV
urlpatterns = [
path('admin/', admin.site.urls),
# path('bookmark/',BookmarkLV.as_view(), name='index'),
# path('bookmark/<int:pk>',BookmarkDV.as_view(), name='detail'),
path('bookmark/', include('bookmark.urls')),
# include 를 통해 APP 단위의 URLconf 로 위임
path('blog/',include('blog.urls')),
]
path('blog/',include('blog.urls')),
include() 함수를 사용하여 APP 단위로 위임
bookmark/urls.py
from django.urls import path
from bookmark.views import BookmarkLV, BookmarkDV
app_name = 'bookmark'
urlpatterns = [
# 추가
path('',BookmarkLV.as_view(), name='index'),
path('<int:pk>/',BookmarkDV.as_view(), name='detail'),
]
이렇게 되면 템플릿에 출력하는 방식도 바뀌게 된다.
기존 코드에서
{% for bookmark in object_list %}
<li>
<!-- {{bookmark}} 는 테이블의 특정 레코드 하나를 의미, __str__ 를 호출-->
<!-- detail 의 url 패턴 : /bookmark/1/형식 으로 웹 요청 보낸다. url패턴 기능 -->
<a href ="{% url 'detail' bookmark.id %}">{{bookmark}}</a>
</li>
{% endfor%}
bookmark 의 urls.py 에 정의된 urlpatterns를 참고하여
app_name:name 형식으로 url 정의 , admin.py 에 정의한 id
<a href ="{% url 'bookmark:detail' bookmark.id %}">{{bookmark}}</a>
admin.py
@admin.register(Bookmark) # 어드민 사이트에 등록
class BookmarkAdmin(admin.ModelAdmin):
list_display = ('id','title','url')
blog/urls.py
from django.urls import path, re_path
from blog import views
app_name= 'blog'
urlpatterns = [
# /blog/
path('',views.PostLV.as_view(), name='index'),
# /blog/post/ (same as /blog/)
path('post/', views.PostLV.as_view(),name = 'post_list'),
# /blog/post/django-example/ : slus, 기본키
# /blog/post/슬러그/
re_path(r'^post/(?P<slug>[-\w]+)/$', views.PostDV.as_view(), name='post_detail'),
# ^post/로 시작 , /$ : /로 끝나는 문자열
# [-\w]+ : 문자열(숫자, 영문, 언더바)
# /blog/archive/
path('archive/',views.PostAV.as_view(),name='post_archive'),
# /blog/archive/2019/
path('archive/<int:year>/',views.PostYAV.as_view(), name='post_year_archive'),
# /blog/archive/2019/nov/
path('archive/<int:year>/<str:month>/',views.PostMAV.as_view(),name='post_month_archive'),
# /blog/archive/2019/nov/10/
path('archive/<int:year>/<str:month>/<int:day>',views.PostDAV.as_view(),name='post_day_archive'),
# /blog/archive/today/
path('archive/today/', views.PostTAV.as_view(), name='post_today_archive'),
]
View
blog/views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from django.views.generic.dates import ArchiveIndexView, YearArchiveView, MonthArchiveView
from django.views.generic.dates import DayArchiveView, TodayArchiveView
from blog.models import Post
# List View
class PostLV(ListView): # ListView 를 상속
model = Post
template_name = 'blog/post_all.html'
context_object_name = 'posts' # 템플릿 파일로 넘겨주는 객체 리스트에 대한 컨텍스트 변수 명
paginate_by =2 # 한페이지에 보여주는 객체 리스트 갯수 - 리스트 하단에 페이지를 이동할 수 있는 버튼 만들 수 있다.
# DetailView
class PostDV(DetailView):
model = Post
# Archive View
class PostAV(ArchiveIndexView):
model = Post
date_field = 'modify_dt' # 변경 날짜가 최근인 포스트를 먼저 출력
class PostYAV(YearArchiveView):
model = Post
date_field = 'modify_dt'
make_object_list = True # 해당 연도에 해당하는 객체 리스트를 만들어 템플릿에 넘겨준다. object_list
class PostMAV(MonthArchiveView):
model = Post
date_field = 'modify_dt'
class PostDAV(DayArchiveView):
model = Post
date_field = 'modify_dt'
class PostTAV(TodayArchiveView):
model = Post
date_field = 'modify_dt'
템플릿 정의
blog/templstes/blog/post_all.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>POST ALL</title>
</head>
<body>
<h1>Blog List</h1>
<br>
<!-- View 에서 context_object_name = 'posts' 라고 정의 했다., object_list 로 호출 가능 -->
{% for post in posts%}
<h3> <a href="{{post.get_absolute_url }}">{{ post.title }} </a></h3>
{{ post.modify_dt | date:"N d, Y"}}
<p>{{post.description}}</p>
{% endfor %}
<br>
<div>
<span>
<!-- page_obj 는 장고의 Page 객체가 들어 있는 컨텍스트 변수 : 현재 페이지를 기준으로 이전 페이지가 있는지 확인-->
{% if page_obj.has_previous %}
<!-- 쿼리 스트링, previous_page_number : 이전 페이지 번호 -->
<a href="?page={{page_obj.previous_page_number}}">PreviousPage</a>
{% endif %}
Page {{ page_obj.number}} of {{page_obj.paginator.num_pages}}
{% if page_obj.has_next %}
<!-- ?page=5, next_page_number : 다음 페이지 번호-->
<a href="?page={{page_obj.next_page_number}}">NextPage</a>
{% endif %}
</span>
</div>
</body>
</html>
제목 하이퍼링크 부분
<!-- View 에서 context_object_name = 'posts' 라고 정의 했다., object_list 로 호출 가능 -->
{% for post in posts%}
<h3> <a href="{{post.get_absolute_url }}">{{ post.title }} </a></h3>
{{ post.modify_dt | date:"N d, Y"}}
<p>{{post.description}}</p>
{% endfor %}
views.py 에 정의된 PostLV 클래스
# List View
class PostLV(ListView): # ListView 를 상속
model = Post
template_name = 'blog/post_all.html'
context_object_name = 'posts' # 템플릿 파일로 넘겨주는 객체 리스트에 대한 컨텍스트 변수 명
paginate_by =2 # 한페이지에 보여주는 객체 리스트 갯수 - 리스트 하단에 페이지를 이동할 수 있는 버튼 만들 수 있다.
<div>
<span>
<!-- page_obj 는 장고의 Page 객체가 들어 있는 컨텍스트 변수 : 현재 페이지를 기준으로 이전 페이지가 있는지 확인-->
{% if page_obj.has_previous %}
<!-- 쿼리 스트링, previous_page_number : 이전 페이지 번호 -->
<a href="?page={{page_obj.previous_page_number}}">PreviousPage</a>
{% endif %}
Page {{ page_obj.number}} of {{page_obj.paginator.num_pages}}
{% if page_obj.has_next %}
<!-- ?page=5, next_page_number : 다음 페이지 번호-->
<a href="?page={{page_obj.next_page_number}}">NextPage</a>
{% endif %}
</span>
</div>
blog/templstes/blog/post_detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Post Detail</title>
</head>
<body>
<div>
<h2>{{object.title}}</h2>
<br>
<p>
{% if object.get_previous %}
<!-- get_previous.get_absolute_url 함수를 지징하는 URL 패턴을 반환한다. -->
<!-- « 는 HTML 특수문자 '<<<' 를 의미한다. -->
<a href="{object.get_previous.get_absolute_url}" title="View previous post">«- {{object.get_previous}}</a>
{% endif %}
{% if object.get_next %}
<!-- get_next.get_absolute_url 함수를 지칭하는 URL 패넡을 반환한다. -->
<!-- » 는 HTML 특수문자 '>>>'를 의미한다. -->
| <a href="{{object.get_next.get_absolute_url}}" title="View next post">{{object.get_next}} -»</a>
{% endif %}
</p>
<!-- 장고 date 템플릿 필터에 설명 참고 -->
<p>{{object.modify_dt|date:"j F Y"}}</p>
<br>
</div>
<div>
{{object.content | linebreaks}} <!-- linebreaks : \n -->
</div>
</body>
</html>
/blog/archive/
blog/templates/blog/post_archive.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Post Archive</title>
</head>
<body>
<div>
<div>
<h1>Post Archives until {% now "N d, Y" %}</h1>
<ul>
<!-- date_list : DateQuerySet 객체 리스트를 담고 있다. , datetime.date 타입 객체-->
{% for date in date_list %}
<li style="display: inline;">
<a href="{% url 'blog:post_year_archive' date|date:'Y' %}">
Year {{date | date:"Y"}}</a>
</li>
{% endfor %}
</ul>
</div>
<br/>
<div>
<ul>
{% for post in object_list %} <!-- object_list or latest -->
<li> {{ post.modify_dt|date:"Y-m-d"}}
<a href="{{post.get_absolute_url}}"><strong>{{ post.title}} </strong></a>
</li>
{% endfor %}
</ul>
</div>
</div>
</body>
</html>
blog/templates/blog/post_year.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Archive Year</title>
</head>
<body>
<h1> Post Archives for {{ year|date:"Y"}}</h1>
<ul>
{% for date in date_list %}
<li style="display: inline;">
<a href="{% url 'blog:post_month_archive' year|date:'Y' date|date:'b' %}">
{{date|date:"F"}}
</a>
</li>
{% endfor %}
</ul>
<br>
<div>
<ul>
{% for post in object_list %}
<li>
{{post.modify_dt|date:"Y-m-d"}}
<a href="{{post.get_absolute_url}}"><strong>{{post.title}}</strong></a>
</li>
{% endfor %}
</ul>
</div>
</body>
</html>
blog/templates/blog/post_month.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Post Archive Month</title>
</head>
<body>
<div>
<h1>Post Archives for {{month|date:"N, Y"}}</h1>
<div>
<ul>
{% for post in object_list %}
<li>{{post.modify_dt|date:"Y-m-d"}}
<a href="{{post.get_absolute_url}}"><strong>{{post.title}}</strong></a>
</li>
{% endfor %}
</ul>
</div>
</div>
</body>
</html>
blog/templates/blog/post_day.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Post Archive Day</title>
</head>
<body>
<div>
<h1>Post Archives for {{day|date:"N d ,Y"}}</h1>
<div>
<ul>
{% for post in object_list %}
<li>{{post.modify_dt|date:"Y-m-d"}} </li>
<a href="{{post.get_absolute_url}}"><strong>{{post.title}}</strong></a>
{% endfor %}
</ul>
</div>
</div>
</body>
</html>
이로써 장고의 MVT 패턴을 토대로 블로그, 아카이브 페이지를 만들어 보았다.
모델, 뷰 , 템플릿에 해당하는 각 파일들의 역할 및 URL 에서 시작해 뷰를 거쳐 템플릿으로 전개되는 로직의 흐름은
모든 애플리케이션의 공통된 사항이다.
책 참고 : 파이썬 웹 프로그래밍 실전편(개정판) - 김석훈
'Language > 파이썬' 카테고리의 다른 글
장고(django) 태그 taggit - 파이썬 장고를 활용한 쉽고 빠른 웹 개발 프로그래밍 (0) | 2022.01.17 |
---|---|
장고(django) 프로젝트 첫 페이지, 템플릿 상속 - 파이썬 장고를 활용한 쉽고 빠른 웹 개발 프로그래밍 (0) | 2022.01.11 |
장고(django) 셋팅(setting)과 북마크 앱(bookmark app) - 파이썬 장고를 활용한 쉽고 빠른 웹 개발 프로그래밍 (0) | 2022.01.01 |
파이썬 AI 온라인 실무 기본 교육과정 / 데이터 핸들링을 위한 라이브러리 NumPy (0) | 2021.09.21 |
2021 파이썬 AI 온라인 실무 기본 및 응용 교육과정을 무료로 수강 ! | 엘리스 코딩 (0) | 2021.09.19 |
댓글