django portfolio project2

양승천·2022년 12월 12일
0

Django

목록 보기
12/12

4-1Blog앱 만들기

  • blog 앱 만들기
python manage.py startapp blog
INSTALLED_APPS = [
	... 생략 ...
    'blog',
]
  • blog 앱 모델 만들기
from django.db import models

# Create your models here.
class Post(models.Model):
    title = models.CharField(max_length=30)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True) # add 할 때의 시간
    updated_at = models.DateTimeField(auto_now=True)     # update 할 때의 사간

def __str__(self):
    return f'[{self.pk}]--{self.title}'
  • Post DB에 적용하기
python manage.py makemigrations
python manage.py migrate
  • .gitignore 파일에 migrations/ 파일 등록
migrations/
  • 관리자 페이지에 Post 등록 하기 ( blog/admin.py)

4-2CBV로 Blog post list 페이지 구현

Blog 페이지 계획

페이지페이지URL
HomePageOne page도메인/
블로그 페이지포스트 목록도메인/blog
포스트 상세도메인/blog/포스트pk
ML 배포배포 페이지도메인/ml_deploy

URLConf 작성

  • icandoit/urls.py path 추가
from django.urls import path, include
... 생략 ...
	  # /blog/
    path('blog/', include('blog.urls')),
... 생략 ...
  • blog/urls.py path 추가
from django.urls import path
from . import views

app_name="blog"
urlpatterns = [
    # ''
    path('', views.PostList.as_view(), name='post_list'),

]

View 작성

from django.views.generic import ListView
from .models import Post

# Create your views here.
class PostList(ListView):
    model = Post
    # template_name = 'blog/post_list.html'  
    ordering = '-pk'
  • CBV 방식 view 작성에서 ListView를 상속 받기 때문에 template 파일 이름을 post_list.html로 하면, template_name 변수는 생략해도 됨

Template 작성

  • blog/templates/blog/post_list.html
{% extends 'base.html' %}
{% load static %}

{% block style%}
<link rel="stylesheet" href="{% static 'css/my_styles.css' %}">
{% endblock style %}

{% block title %}
    Blog 목록
{% endblock title %}

{% block main_area %}
<section class="container">
    <h2>Blog</h2>
    {% for p in post_list %}
        <hr />
        <h2> <a href="{{ **p.get_absolute_url** }}">{{ p.title }}</a> </h2>
        <h4>{{ p.created }}</h4>
        <p>{{ p.content }}</p>
    {% endfor %}
</section>
{% endblock main_area %}
  • navbar는 살리고 main_area부분에 들어갈 내용은 별개의 style을 적용하기 위해
    • static/css/styles.css 복사 하여 파일이름을 my_styles.css로 이름 변경

    • my_styles.css 내용을 추가해 줌

      /*1. 다음 부분을 찾아서 background-color 부분 추가  */
      @media (min-width: 992px) {
        #mainNav {
          ... 생략 ...
          /* add my style */
          background-color: #212529;   
        }
      
          ... 생략 ...
      
      /*2. 파일 제일 끝부분에 추가해 주기 */
      /* add my style */
      section {
          /* 상하, 좌우*/
          padding: 8rem 0;
      }
  • padding 설정 방법(참고)
section {
    /* 상하, 좌우*/
    padding: 8rem 0;

		/* 상하좌우 */
    padding: 8rem

    /* 상우하좌 */  
    padding: 2rem 3rem 4rem 5rem
}

  • blog 앱 폴더 및 파일 구조

4-3CBV 포스트 목록 페이지 만들기

URLConf

# blog/1/
    path('<int:pk>/', views.PostDetail.as_view(), name='post_deatil'),

View 작성

from django.views.generic import ListView, **DetailView

class PostDetail(DetailView):
    model = Post**

Post 모델의 레코드별 URL 생성 규칙 정의

  • blog/models.py 파일 함수 추가
  • get_absolute_url() 함수 정의
class Post(models.Model):
	... 생략 ...
	def get_absolute_url(self):
      return f'/blog/{self.pk}/'

template 파일 작성

  • templates/blog/post_detail.html
{% extends 'base.html' %}
{% load static %}

{% block style%}
<link rel="stylesheet" href="{% static 'css/my_style.css' %}">
{% endblock style %}

{% block title %}
    {{ post.title }}
{% endblock title %}

{% block contents %}
<section class="container">
    <h1>Blog - {{ post.title }}</h1>
    <h4>{{ post.created_at }}</h4>
    <p>{{ post.content }}</p>

</section>
{% endblock contents %}

더미 데이터 입력하기

로렘 입숨(Lorem Ipsum) 영어

https://www.lipsum.com/

로렘 입숨 한글

http://guny.kr/stuff/klorem/

**로렘 픽숨 (Lorem Picsum)**

https://picsum.photos/

4-4blog bootstrap 템플릿 적용하기

blog_list, blog_detail 템플릿 적용

blog 목록 페이지

{% for p in post_list %}
    <div class="card mb-4">
        {% if p.head_image %}
        <img class="card-img-top" src="{{ p.head_image.url }}" alt="..." />
        {% else %}
        <img class="card-img-top" src="https://picsum.photos/id/{{p.id}}/800/200" alt="..." />
        {% endif %}
        <div class="card-body">
            <h2 class="card-title">{{ p.title }}</h2>
            <p class="card-text">{{ p.content | truncatewords:45}}</p>
            <a class="btn btn-primary" href="{{ p.get_absolute_url }}">Read more →</a>
        </div>
        <div class="card-footer text-muted"> 
            Posted on {{ p.created_at }}
            <a href="#"> 작성자명 쓸 위치(개발예정)</a>
        </div>
    </div>
    
{% endfor %}

blog 상세 페이지

<article class="mb-3">
	  <!-- Post header-->
    <header class="mb-4">
        <!-- Post title-->
        <h1 class="fw-bolder mb-1">Blog - {{ post.title }}</h1>

        <!-- Author -->
        <p class="lead">by <a href="#">작성자 명 쓸 위치(개발예정)</a></p>
        <!-- Post meta content-->
        <div class="text-muted fst-italic mb-2">{{ post.created_at }}</div>
        <!-- Post categories-->
        <a class="badge bg-secondary text-decoration-none link-light" href="#!">Web Design</a>
        <a class="badge bg-secondary text-decoration-none link-light" href="#!">Freebies</a>
    </header>

    <!-- Preview image figure-->
    <img class="img-fluid rounded" src="https://picsum.photos/id/{{post.id}}/900/200" alt="random_image" />

    <!-- Post content-->
    <div class="mb-5">
        <p class="fs-5 mb-4">{{ post.content }}</p>
    </div>
</article>

4-5미디어 파일 관리

포스트에 이미지 올리기

  • 이미지 폴더 지정하기
    • icandoit/settings.py
import os

... 생략 ...

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, '_media')
  • 모델 파일에 head_image 필드 추가하기
    • blog/models.py
class Post(models.Model):
	... 생략 ...
	head_image = models.ImageField(upload_to = 'blog/images/%Y/%m/%d/', blank=True)
  ... 생략 ...
  • Pillow 라이브러리 설치하고 마이그레이션 하기
pip install Pillow

python manage.py makemigrations
python manage.py migrate
  • 미디어 파일을 위한 URL 지정하기
    • icandoit/urls.py
... 생략 ...
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
... 생략 ...
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  • 목록 페이지에 대표 이미지가 보이도록 수정하기
    • blog/templates/blog/post_list.html
{% if p.head_image %}
	<img class="card-img-top" src="{{ p.head_image.url }}" alt="{{ p }} head image" />
{% else %}
	<img class="card-img-top" src="https://picsum.photos/id/{{p.id}}/800/200" alt="random_image" />
{% endif %}
  • 상세 페이지에 대표 이미지가 보이도록 수정하기
    • blog/templates/blog/post_detail.html
{% if p.head_image %}
  <img class="card-img-top" src="{{ p.head_image.url }}" alt="..." />
{% else %}
  <img class="card-img-top" src="https://picsum.photos/id/{{p.id}}/800/200" alt="..." />
{% endif %}

미디어 파일 .gitignore에서 제외

# .gitignore

_media/

4-6첨부 파일 기능 구현하기

  • 첨부파일 업로드를 위한 model 필드 추가
    • blog/models.py
import os

class Post(models.Model):
	... 생략 ...

	file_upload = models.FileField(upload_to = 'blog/files/%Y/%m/%d/', blank=True)

	... 생략 ...

	def get_file_name(self):
		return os.path.basename(self.file_upload.name)
	
	def get_file_ext(self):
		return self.get_file_name().split('.')[-1]
  • 첨부 파일 다운로드 버튼 만들기
    • blog/templates/blog/post_detail.html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css">

{% if post.file_upload %}
  <a href="{{ post.file_upload.url }}" class="btn btn-outline-dark" role="button" download>
      Download:
      {% if post.get_file_ext == 'csv' %}
          <i class="bi bi-filetype-csv"></i>
      {% elif post.get_file_ext == 'xlsx' or post.get_file_ext == 'xls' %}
          <i class="bi bi-filetype-xlsx"></i>
      {% elif post.get_file_ext == 'docx' or post.get_file_ext == 'doc' %}
          <i class="bi bi-filetype-docx"></i>
      {% elif post.get_file_ext == 'pdf' %}
          <i class="bi bi-filetype-pdf"></i>
      {% else %}
          <i class="bi bi-file-earmark-font"></i>
      {% endif %}
			{{ post.get_file_name }}
  </a>
{% endif %}
  • 주의 사항 : == 과 ‘xlsx’ 사이는 1칸 떨어져야 함
    • {% elif post.get_file_ext == 'xlsx' or post.get_file_ext == 'xls' %}
  • getbootstrap icon 사용

https://icons.getbootstrap.com/

4-7템플릿 필터 구현, hook fields 추가

목록의 포스트 미리보기 구현

출력 글자 수 제한 하기

  • blog/templates/blog/post_list.html
<p class="card-text">{{ p.content | truncatewords:45 }}</p>

요약문 필드 만들기

  • hook_text fields 추가
  • blog/models.py
class Post(models.Model):
    title = models.CharField(max_length=30)
    **hook_text = models.CharField(max_length=100, blank=True)**

		... 생략 ...
  • blog/templates/blog/post_list.html
<div class="card-body">
    <h2 class="card-title">{{ p.title }}</h2>
    **{% if p.hook_text %}
        <h5 class="text-muted">{{ p.hook_text }}</h5>
    {% endif %}**
    <p class="card-text">{{ p.content | truncatewords:45}}</p>
    <a class="btn btn-primary" href="{{ p.get_absolute_url }}">Read more →</a>
</div>
  • blog/templates/blog/post_detail.html
<header class="mb-4">
    <!-- Post title-->
    <h1 class="fw-bolder mb-1">Blog - {{ post.title }}</h1>
    {% if p.hook_text %}
        <h5 class="text-muted">{{ p.hook_text }}</h5>
    {% endif %}
    <!-- Author -->
    <p class="lead">by <a href="#">작성자 명 쓸 위치(개발예정)</a></p>
    <!-- Post meta content-->
    <div class="text-muted fst-italic mb-2">{{ post.created_at }}</div>
    <!-- Post categories-->
    <a class="badge bg-secondary text-decoration-none link-light" href="#!">Web Design</a>
    <a class="badge bg-secondary text-decoration-none link-light" href="#!">Freebies</a>
</header>

4-8 paginations 기능

  • page 사용 설정 추가
    • blog/view.py
class PostList(ListView):
    model = Post
    # template_name = 'blog/post_list.html'
    **paginate_by = 3   # pagination 기능 활성화, page 당 3개** 
    ordering = '-pk'
<!-- Pagination-->
{% if is_paginated %}
<nav aria-label="Pagination">
    <hr class="my-0" />
    <ul class="pagination justify-content-center my-4">
        <!-- 이전 페이지로 돌아가기 -->
        {% if page_obj.has_previous %}
        <!-- 이전 페이지 객체가 있으면 [prev]버튼 활성화 -->
        <li class="page-item">
            <a class="page-link" href="{% url 'blog:post_list' %}?page={{ page_obj.previous_page_number }}" tabindex="-1">Prev</a>
        </li>
        {% else %}
        <!-- 이전페이지 객체가 없으면 [prev]버튼 비활성화 -->
        <li class="page-item disabled">
            <a class="page-link" href="#" tabindex="-1">Prev</a>
        </li>
        {% endif %}
        <!-- 페이지를 번호로 출력하기 -->
        {% for page in page_obj.paginator.page_range %}
            <!-- 현재 선택한 페이지 번호는 비활성화 시킴 -->
            {% if page_obj.number == forloop.counter %}
            <li class="page-item disabled" aria-current="page">
                <a class="page-link" style="background-color: #7eacf6;" href="{{ request.path }}?page={{ forloop.counter }}">{{ forloop.counter }}</a>
            </li>
            {% else %}
            <!-- 현재 선택한 페이지 번호는 활성화 시킴 -->
            <li class="page-item" aria-current="page">
                <a class="page-link" href="{{ request.path }}?page={{ forloop.counter }}">{{ forloop.counter }}</a>
            </li>
            {% endif %}
        {% endfor %}
        <!-- 다음 페이지로 가기 -->
        {% if page_obj.has_next %}
        <!-- 이전페이지 객체가 있으면 [Next]버튼 활성화 -->
        <li class="page-item">
            <a class="page-link" href="{% url 'blog:post_list' %}?page={{ page_obj.next_page_number }}">Next</a>
        </li>
        {% else %}
        <li class="page-item disabled">
            <a class="page-link" href="#">Next</a>
        </li>
        {% endif %}
        
    </ul>
</nav>
{% endif %}
profile
되고싶다 직무전환 성공하자!

0개의 댓글