python(django) - 장고로 웹페이지 만들기, detail page (7)
지난 글에서는 sqlite3 datebase에 저장된 data를 실제 웹 화면에 출력하는 것을 진행했다.
2022.11.11 - [Python/Do something] - python(django) - 장고로 웹페이지 만들기, views (6)
blog의 detail한 화면을 보기 위해서 modal을 이용해서 팝업 형태로 상세 내용을 확인했다. 하지만 팝업 형태가 아니라 별도 도의 URL주소와 page를 할당하는 형태로 하려면 어떻게 해야 할까?
추가 app은 만들지 말고 기존 blog app을 활용해서 최대한 간단하게 만들어보자.
우선 blog의 app의 views.py에 아래 새로운 class를 추가한다.
class PostList2(ListView):
model = Post
template_name = 'blog/base2.html'
# ordering = '-pk'
다음으로 urls.py에 아래 내용을 추가하면 http://127.0.0.1:8000/blog/blog2/ 로 접속했을때 위에서 설정한 base2.html이 열릴 것이다. (기존 base.html을 복사해서 잘 열리는지 테스트해보자)
urlpatterns = [
# path('', views.index),
path('', views.PostList.as_view()),
path('blog2/', views.PostList2.as_view()),
]
이번에는 blog 페이지 리스트도 기존과 다른걸로 해보자. 뭐가 좋을지 몰라서 bootstrap을 구경하다보니 괜찮은 것을 찾았다. Media list
https://getbootstrap.com/docs/4.6/components/media-object/
위에 있는 code를 그대로 복사해서 base2.html에 복사해보자. 샘플 리스트 3개가 잘 나타난다.
샘플 리스트에 있는 html을 이전 편에서 했던 것처럼 database에서 가져온 data를 출력할 수 있도록 살짝 변경해준다.
(여기 truncatewords_html:20 은 출력되는 글자수가 많은 경우 출력되는 글자수를 제한하기 위해서 추가하였다.)
<section class="bg-light" style="padding: 10px 10px">
<div class="container">
<br>
<h1> Pymin Story </h1>
<br>
<ul class="list-unstyled">
{% for p in post_list %}
<li class="media mb-1 mt-1">
{% if p.head_image %}
<img class="mr-3" style="width:100px; height:100px;" src="{{ p.head_image.url }}" alt="{{ p }} head image">
{% else %}
<img class="mr-3" style="width:100px; height:100px;" src="{% static 'blog/images/no_image.jpg' %}" alt="{{ p }} No image">
{% endif %}
<div class="media-body">
<h5 class="mt-0 mb-1">{{ p.title }}</h5>
<p>{{ p.content | truncatewords_html:20 | safe }}</p>
</div>
</li>
{% endfor %}
</ul>
</div>
</section>
이렇게 하고 새로 추가한 주소(http://127.0.0.1:8000/blog/blog2/)로 들어가보면 아래와 같이 기존에 있던 data들이 출력되게 된다.
그림 크기가 작은것 같으니 크기만 100에서 150으로 변경해보자.
{% if p.head_image %}
<img class="mr-3" style="width:150px; height:150px;" src="{{ p.head_image.url }}" alt="{{ p }} head image">
{% else %}
<img class="mr-3" style="width:150px; height:150px;" src="{% static 'blog/images/no_image.jpg' %}" alt="{{ p }} No image">
{% endif %}
이제 detail한 페이지로 넘어가기 위한 버튼을 추가해야 한다.
버튼은 또가시 bootstrap에 가서 괜찮은 것을 찾아보자.
https://getbootstrap.com/docs/4.6/components/buttons/
Buttons
Use Bootstrap’s custom button styles for actions in forms, dialogs, and more with support for multiple sizes, states, and more.
getbootstrap.com
버튼을 보다보니 에메랄드 색깔이 맘에 든다. 이것을 그대로 복사하자.
그리고 html에 그대로 붙여넣으면 끝이다.
<!-- main content -->
<section class="bg-light" style="padding: 10px 10px">
<div class="container">
<br>
<h1> Pymin Story </h1>
<br>
<ul class="list-unstyled">
{% for p in post_list %}
<li class="media mb-1 mt-1">
{% if p.head_image %}
<img class="mr-3" style="width:150px; height:150px;" src="{{ p.head_image.url }}" alt="{{ p }} head image">
{% else %}
<img class="mr-3" style="width:150px; height:150px;" src="{% static 'blog/images/no_image.jpg' %}" alt="{{ p }} No image">
{% endif %}
<div class="media-body">
<h5 class="mt-0 mb-1">{{ p.title }}</h5>
<p>{{ p.content | truncatewords_html:18 | safe }}</p>
<button type="button" class="btn btn-outline-info">More Info</button>
</div>
</li>
{% endfor %}
</ul>
</div>
</section>
아래처럼 detail page를 위한 버튼이 나타난다. 이제 버튼을 누르면 detail한 페이지가 나타나도록 설정해보자.
먼저 blog app의 views.py를 열어 아래 Detail Class를 추가해주자. 이번에는 template_name을 넣지 않았다. 그러면 html파일은 post_detail.html로 설정하면 된다. 뒤에서 한번 더 언급하도록 하겠다.
(아래에는 이전 내용들을 참고하기 위해 전체 코드를 넣었다)
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .models import Post
def index(request):
return render(
request,
'blog/base.html',
)
class PostList(ListView):
model = Post
template_name = 'blog/base.html'
# ordering = '-pk'
class PostList2(ListView):
model = Post
template_name = 'blog/base2.html'
# ordering = '-pk'
class PostDetail(DetailView):
model = Post
다음은 무엇을 해야 할까? urls.py를 수정하는 것이다. 아래 <int:pk>는 /blog/<pk>로 이해하면 된다. 즉 blog뒤에 database Post table의 고유값이 pk값을 주소로 사용한다는 내용이다.
from django.urls import path
from . import views
urlpatterns = [
# path('', views.index),
path('', views.PostList.as_view()),
path('blog2/', views.PostList2.as_view()),
path('<int:pk>/', views.PostDetail.as_view()),
]
이제 URL에 http://127.0.0.1:8000/blog/1/ 을 입력해보자. 그럼 아래와 같이 post_detail.html이 없다고 나올 것이다. 그럼 이제 아까 만들었던 base2.html을 복사해서 post_detail.html로 변경하자.
django.template.loaders.app_directories.Loader: C:\pyminv\pymin_django2\lib\site-packages\django\contrib\admin\templates\blog\post_detail.html (Source does not exist)
django.template.loaders.app_directories.Loader: C:\pyminv\pymin_django2\lib\site-packages\django\contrib\auth\templates\blog\post_detail.html (Source does not exist)
django.template.loaders.app_directories.Loader: C:\pyminv\pymin_django2\blog\templates\blog\post_detail.html (Source does not exist)
그런다음에 post_detail.html의 내용 중에서 for문을 제거하고 p.title을 post.title처럼 p->post로 변경한다.
<!-- main content -->
<section class="bg-light" style="padding: 10px 10px">
<div class="container">
<br>
<h1> Pymin Story </h1>
<br>
<ul class="list-unstyled">
<li class="media mb-1 mt-1">
{% if post.head_image %}
<img class="mr-3" style="width:150px; height:150px;" src="{{ post.head_image.url }}" alt="{{ post }} head image">
{% else %}
<img class="mr-3" style="width:150px; height:150px;" src="{% static 'blog/images/no_image.jpg' %}" alt="{{ post }} No image">
{% endif %}
<div class="media-body">
<h5 class="mt-0 mb-1">{{ post.title }}</h5>
<p>{{ post.content }}</p>
</div>
</li>
</ul>
</div>
</section>
그러고 다시 http://127.0.0.1:8000/blog/1/로 접속을하면 해당 pk를 가진 내용이 출력된다.
그럼 이제 뭐가 남았을까? 지금은 수동으로 URL뒤에 pk 넘버를 추가했지만 'More Info' 버튼을 누르면 detail 한 페이지로 이동되도록 해야 한다.
blog app의 models.py에 아래 get_absolute_url(self) 함수를 추가하자 이는 "More Info'버튼을 눌렀을 때 detail한 페이지로의 이동을 도와준다.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=30)
hook_text = models.CharField(max_length=100, blank=True)
content = models.TextField()
head_image = models.ImageField(upload_to='blog/images/%Y/%m/%d/', blank=True)
file_upload = models.FileField(upload_to='blog/files/%Y/%m/%d/', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'[{self.pk}] {self.title} - {self.hook_text}'
def get_absolute_url(self):
return f'/blog/{self.pk}/'
다음으로 base2.html의 button에 해당 주소로 link될 수있도록 onclick을 입력해주면 된다. p.get_absolute_url는 models.py에 추가한 주소를 나타내준다.
<button type="button" class="btn btn-outline-info" onclick="location.href='{{ p.get_absolute_url }}'">More Info</button>
이제 http://127.0.0.1:8000/blog/blog2/에 접속해서 More Info를 눌러보자
누르면 해당 페이지로 자동으로 넘어가는것을 알 수 있다. 다른 게시글을 누르면 해당 게시글로 이동이 된다.
이렇게 detail한 정보를 나타내는 별도의 URL 페이지로 연동하는 것이 끝났다. 여러가지를 추가하다 보니 html 파일이 base, base2, post_detail 3개가 되었다. 그런데 html에 Navigator나 footer같은 것들은 자꾸 중복이 된다. 다음 편에서는 중복되는 애들을 하나로 묶어보자.