블로그 앱을 추가하도록 하겠습니다. 블로그 앱에서는 메인인 Home.html에서 포스트 클릭 시 포스트를 출력해주는 기능입니다.
1. blog app 추가
블로그 앱을 생성해줍니다.
django-admin startapp blog
2. mysite/settings.py 수정
블로그 앱을 등록해줍니다.
INSTALLED_APPS = [
'blog.apps.BlogConfig', # 추가
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
3. blog/models.py 구성
블로그 앱의 모델을 작성해 줍니다. 1:N의 관계에서 1은 N의 테이블(클래스)에서 ForeignKey로 지정해줍니다. 예를 들어 Category와 Post는 1:N의 관계입니다. 이 경우 Post 테이블에 Category 필드를 지정해주며, ForeignKey 속성을 부여합니다.
on_delete=models.SET_NULL은 해당 필드가 삭제될 경우 null로 세팅하라는 의미이며,
on_delete=model.CASCADE 해당 필드가 삭제될 경우 해당 값을 모두 삭제하라는 의미입니다.
from django.db import models
# Create your models here.
class Post(models.Model):
category = models.ForeignKey('Category', on_delete=models.SET_NULL, blank=True, null=True)
tags = models.ManyToManyField('Tag', blank=True)
title = models.CharField('TITLE', max_length=50)
description = models.CharField('DESCRIPTION', max_length=100, blank=True, help_text='simple one-line text.')
image = models.ImageField('IMAGE', upload_to='blog/%Y/%m/', blank=True, null=True)
content = models.TextField('CONTENT')
create_dt = models.DateTimeField('CREATE DT', auto_now_add=True)
update_dt = models.DateTimeField('UPDATE DT', auto_now=True)
like = models.PositiveSmallIntegerField('LIKE', default=0)
def __str__(self):
return self.title
class Category(models.Model):
name = models.CharField(max_length=50, unique=True)
description = models.CharField('DESCRIPTION', max_length=100, blank=True, help_text='simple one-line text.')
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, blank=True, null=True)
content = models.TextField('CONTENT')
create_dt = models.DateTimeField('CREATE DT', auto_now_add=True)
update_dt = models.DateTimeField('UPDATE DT', auto_now=True)
@property # property 문법: 메서드를 필드로 정의
def short_content(self):
return self.content[:10]
def __str__(self):
return self.short_content
4. blog/admin.py 구성
models.py에서 구성한 각 테이블(클래스)를 admin사이트에 등록합니다.
from django.contrib import admin
from blog.models import Post, Category, Tag, Comment
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('id', 'category', 'tag_list', 'title', 'description', 'image', 'create_dt', 'update_dt', 'like')
def tag_list(self, obj):
return ','.join([t.name for t in obj.tags.all()]) # post:tag = 1:N이므로, 태그들을 전부 콤마로 이어붙임
def get_queryset(self, request):
return super().get_queryset(request).prefetch_related('tags') # 관련된 태그 테이블도 같이 가져오도록
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'description')
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ('id', 'name')
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('id', 'post', 'short_content', 'create_dt', 'update_dt')
5. Table Migration
model 변경사항을 반영합니다.
Python manage.py makemigrations
python manage.py migrate
6. mysite/urls.py 수정
blog url을 가져올 수 있도록 등록합니다. 또한 이미지 파일이 불려와질 수 있도록 urlpatterns 구문을 추가합니다.
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
from mysite.views import HomeView
urlpatterns = [
path('admin/', admin.site.urls),
path('', HomeView.as_view(), name='home'),
path('blog/', include('blog.urls')),
]
# 이미지 url 접근 시 이미지 파일이 접근 가능하도록 설정
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
7. blog/urls.py 작성
실제 blog의 세부 url을 작성합니다. url에 따라 PostDV클래스를 찾습니다.
from django.urls import path
from blog import views
app_name = 'blog'
urlpatterns = [
# /blog/post/99/
path('post/<int:pk>/', views.PostDV.as_view(), name='post_detail'),
]
8. blog/views.py 작성
PostDV를 정의합니다. 모델은 Post모델을 사용하며, 사용할 template을 정의합니다.
from django.views.generic import DetailView
from blog.models import Post
class PostDV(DetailView):
model = Post
template_name = 'blog/post_detail.html'
9. templates/blog/post_detail.html 작성
보여질 post_detail.html파일을 작성합니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>post_detail.html</title>
</head>
<body>
This is post_detail.html...
</body>
</html>
아직은 해당 경로로 이동해도 보여지지 않습니다. 포스트를 생성해야합니다.
10. admin site로 이동하여 포스트 생성
포스트를 몇개 등록합니다.(아래 이미지에서는 첫번째 등록한 포스트가 지워졌습니다.)
11. url 입력
위에서 입력한 포스트의 id를 유념하여 url을 입력합니다. 아래와 같이 작성된 post_detail.html 이 나타나면 정상입니다.
12. home.html 수정
url을 매번 입력하여 이동할 수는 없으므로, home.html파일의 첫번째 이미지 클릭 시 포스트가 나타나도록 수정하겠습니다.
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>Home.html</title>
<!-- Favicon-->
<link rel="icon" type="image/x-icon" href="{% static 'assets/favicon.ico' %}" />
<!-- Font Awesome icons (free version)-->
<script src="https://use.fontawesome.com/releases/v5.15.4/js/all.js" crossorigin="anonymous"></script>
<!-- Google fonts-->
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700" rel="stylesheet" type="text/css" />
<!-- Core theme CSS (includes Bootstrap)-->
<link href="{% static 'css/styles.css' %}" rel="stylesheet" />
</head>
<body id="page-top">
<!-- Navigation-->
<nav class="navbar navbar-expand-lg navbar-dark fixed-top" id="mainNav">
<div class="container">
<div class="navbar-brand">Vue - Django Web Programming</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i class="fas fa-bars ms-1"></i>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav text-uppercase ms-auto py-4 py-lg-0">
<li class="nav-item"><a class="nav-link" href="#page-top">Home</a></li>
<li class="nav-item"><a class="nav-link" href="#portfolio">Blog</a></li>
<li class="nav-item"><a class="nav-link" href="#team">Iam</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'admin:index' %}">Admin</a></li>
</ul>
</div>
</div>
</nav>
<!-- Masthead-->
<header class="masthead">
<div class="container">
<div class="masthead-subheading">Welcome To My Blog !</div>
<div style="margin-bottom: 230px;">일상 생활과 관련사항을 기록하고 있습니다.</div>
</div>
</header>
<!-- Blog Grid-->
<section class="page-section bg-light" id="portfolio">
<div class="container">
<div class="text-center">
<h2 class="section-heading text-uppercase">Blot List</h2>
<h3 class="section-subheading text-muted">Lorem ipsum dolor sit amet consectetur.</h3>
</div>
<div class="row">
<div class="col-lg-4 col-sm-6 mb-4">
<!-- Portfolio item 1-->
<div class="portfolio-item">
<a class="portfolio-link" href="{% url 'blog:post_detail' 1 %}"> <!--이부분 수정 -->
<div class="portfolio-hover">
<div class="portfolio-hover-content"><i class="fas fa-plus fa-3x"></i></div>
</div>
<img class="img-fluid" src="{% static 'assets/img/portfolio/1.jpg' %}" alt="..." />
</a>
<div class="portfolio-caption">
<div class="portfolio-caption-heading">Threads</div>
<div class="portfolio-caption-subheading text-muted">Illustration</div>
</div>
</div>
</div>
<div class="col-lg-4 col-sm-6 mb-4">
<!-- Portfolio item 2-->
<div class="portfolio-item">
<a class="portfolio-link" data-bs-toggle="modal" href="#portfolioModal2">
<div class="portfolio-hover">
<div class="portfolio-hover-content"><i class="fas fa-plus fa-3x"></i></div>
</div>
<img class="img-fluid" src="{% static 'assets/img/portfolio/2.jpg' %}" alt="..." />
</a>
<div class="portfolio-caption">
<div class="portfolio-caption-heading">Explore</div>
<div class="portfolio-caption-subheading text-muted">Graphic Design</div>
</div>
</div>
</div>
<div class="col-lg-4 col-sm-6 mb-4">
<!-- Portfolio item 3-->
<div class="portfolio-item">
<a class="portfolio-link" data-bs-toggle="modal" href="#portfolioModal3">
<div class="portfolio-hover">
<div class="portfolio-hover-content"><i class="fas fa-plus fa-3x"></i></div>
</div>
<img class="img-fluid" src="{% static 'assets/img/portfolio/3.jpg' %}" alt="..." />
</a>
<div class="portfolio-caption">
<div class="portfolio-caption-heading">Finish</div>
<div class="portfolio-caption-subheading text-muted">Identity</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Iam-->
<section class="page-section bg-light" id="team">
<div class="container">
<div class="text-center">
<h2 class="section-heading text-uppercase">홍 길 동</h2>
<h3 class="section-subheading text-muted">Lorem ipsum dolor sit amet consectetur.</h3>
</div>
<div class="row">
<div class="col-lg-4">
<div class="team-member">
<img class="mx-auto rounded-circle" src="{% static 'assets/img/team/1.jpg' %}" alt="..." />
<h4>Creative Company</h4>
<p class="text-muted">Python Web Programmer</p>
<a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-twitter"></i></a>
<a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-facebook-f"></i></a>
<a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-linkedin-in"></i></a>
</div>
</div>
<div class="col-lg-8">
내 소개 내용...
</div>
</div>
</div>
</section>
<!-- Footer-->
<footer class="footer py-4">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-4 text-lg-start">Copyright © Your Website 2021</div>
<div class="col-lg-4 my-3 my-lg-0">
<a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-twitter"></i></a>
<a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-facebook-f"></i></a>
<a class="btn btn-dark btn-social mx-2" href="#!"><i class="fab fa-linkedin-in"></i></a>
</div>
<div class="col-lg-4 text-lg-end">
<a class="link-dark text-decoration-none me-3" href="#!">Privacy Policy</a>
<a class="link-dark text-decoration-none" href="#!">Terms of Use</a>
</div>
</div>
</div>
</footer>
<!-- Bootstrap core JS-->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js"></script>
<!-- Core theme JS-->
<script src="{% static 'js/scripts.js' %}"></script>
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *-->
<!-- * * SB Forms JS * *-->
<!-- * * Activate your form at https://startbootstrap.com/solution/contact-forms * *-->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *-->
<script src="https://cdn.startbootstrap.com/sb-forms-latest.js"></script>
</body>
</html>
'Programming > Python_Web' 카테고리의 다른 글
Python - Django 시작하기 4 - oracle DB ORM Join (0) | 2021.11.18 |
---|---|
Python Open API(XML) 활용하기 - 부동산 매매가격 (0) | 2021.11.08 |
Python - Django 시작하기 2 - start bootstrap template 적용 (0) | 2021.10.13 |
Python - Django 시작하기1 - 설치 (0) | 2021.10.13 |
FastAPI - Jinja2, Form, Redirect 적용 (0) | 2021.09.11 |