본문 바로가기

Python/Django

Django 템플릿 상속 / 게시판 만들기

1. 템플릿 상속

공통적으로 쓰는 코드를 하나 만들어 놓고 상속받아서 쓰는 것

바뀌는 부분만 block이라는 부분에 싸서 사용

 

- blog/templates/blog/base.html

<!DOCTYPE html>
{% load static %}
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Blog List</title>
    {% load static %}
    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
    <!-- Optional theme -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css" integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">
    <!-- google lobster font-->
    <link rel="stylesheet"
    href="https://fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext"
    type="text/css">
    <!-- 개발자 정의 CSS-->
    <link rel="stylesheet" href="{% static 'css/blog.css' %}">
    <!-- favicon 적용하기 -->
    <link rel="shortcut icon" href="{%static 'favicon.ico'%}">
</head>
<body>
    <div class="page-header">
        <h1><a href="/">Django Blog</a></h1>
    </div>
    <div class="content container">
        <div class="row">
            <div class="col-md-8">
            {% block content %}
            {% endblock %}
            </div>
        </div>
    </div>


    {% comment "Optional note" %}
        Server comment
    {% endcomment %}
    <!-- html Client Comment -->
</body>
</html>

 

- blog/templates/blog/post_edit.html

{% extends 'blog/base.html' %}

{% block content%}
    {% for post in posts %}
        <div class="post">
            <div class="date">
                <p>published : {{post.published_date}}</p>
            </div>
                <h1><a href="https://naver.com">{{post.title|title}}</a></h1>
                <p>{{post.text|linebreaksbr}}</p>
        </div>
        {% empty %}
            <div>요청하신 Post가 존재하지 않습니다. </div>
    {% endfor %}
{% endblock %}

 

2. 상세페이지

- post_detail.html

{% extends 'blog/base.html' %}

{% block content%}
    <div class="post">
        {% if post.published_date%}
        <div class="date">
            {{post.published_date}}
        </div>
        {% endif %}
        <h1>{{post.title|title}}</h1>
        <p>{{post.text|linebreaksbr}}</p>
    </div>
{% endblock %}

 

- view.py

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from django.utils import timezone
from .models import Post

# Post 목록
def post_list(request):
    #name = 'Django'
    # return HttpResponse('''
    #     <h2>Post List</h>
    #     <p>웰컴 {name}!!!</p>
    #     <p>{content}<p/>'''.format(name=name, content=request.content_type))
    posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
    return render(request, 'blog/post_list.html', {'posts': posts})

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/post_detail.html', {'post': post})

 

- urls.py

from django.urls import path
from . import views


urlpatterns = [
    # loacalhost:8080/
    path('', views.post_list, name='post_list'),
    # localhost:8080/post/5
    path('post/<int:pk>/', views.post_detail, name='post_detail_test')
]

 

- post_list.html

{% extends 'blog/base.html' %}

{% block content%}
    {% for post in posts %}
        <div class="post">
            <div class="date">
                <p>published : {{post.published_date}}</p>
            </div>
                <h1><a href="{% url 'post_detail_test' pk=post.pk %}">{{post.title|title}}</a></h1>
                <p>{{post.text|linebreaksbr}}</p>
        </div>
        {% empty %}
            <div>요청하신 Post가 존재하지 않습니다. </div>
    {% endfor %}
{% endblock %}

 

- 결과확인

 

3. Django Form

 

https://www.w3schools.com/Bootstrap/bootstrap_ref_comp_glyphs.asp

 

Bootstrap Glyphicon Components

Bootstrap Glyphicon Components Glyphicons Bootstrap includes 260 glyphs from the Glyphicon Halflings set. Glyphicons Halflings are normally not available for free, but their creator has made them available for Bootstrap free of cost. As a thank you, you sh

www.w3schools.com

- blog/froms.py

from django import forms
from .models import Post



# validator 함수 정의
# title 입력필드의 길이 체크 < 3
def min_length_3_validator(value):
    if len(value) < 3:
        raise forms.ValidationError('title은 3글자 이상 입력해주세요')


# PostFrom 클래스 선언
class PostForm(forms.Form):
    # title = forms.CharField()
    title = forms.CharField(validators=[min_length_3_validator])
    text = forms.CharField(widget=forms.Textarea)


# PostModelFrom 클래스 선언
class PostModelForm(forms.ModelForm):
    # title = forms.CharField()
    # title = forms.CharField(validators=[min_length_3_validator])
    # text = forms.CharField(widget=forms.Textarea)

    class Meta:
        model = Post
        fields = ('title', 'text',)

 

- blog/models.py

from django.db import models
from django.utils import timezone
from django import forms

def min_length_3_validator(value):
    if len(value) < 3:
        raise forms.ValidationError('제목은 3글자 이상 입력해주세요')


# Create your models here.
class Post(models.Model):
    # 작성자
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    # 제목
    title = models.CharField(max_length=200, validators=[min_length_3_validator])
    # 내용
    text = models.TextField()
    # 작성일자
    created_date = models.DateTimeField(default=timezone.now)
    # 게시일자
    published_date = models.DateTimeField(blank=True, null=True)
    # 필드 추가 - 삭제할 예정
    #test = models.TextField()

    # 게시 일자에 현재 날짜를 대입해주는 함수
    def publish(self):
        self.published_date = timezone.now()
        self.save()

    # 객체 주소 대신 글제목을 반한해주는 toString()함수
    def __str__(self):
        return self.title

 

 

- blog/views.py

from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse
from django.utils import timezone
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required

from .models import Post
from .forms import PostForm
from .forms import PostModelForm


# Post 목록
def post_list(request):
    #name = 'Django'
    # return HttpResponse('''
    #     <h2>Post List</h>
    #     <p>웰컴 {name}!!!</p>
    #     <p>{content}<p/>'''.format(name=name, content=request.content_type))
    posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
    return render(request, 'blog/post_list.html', {'posts': posts})

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/post_detail.html', {'post': post})

@login_required
def post_new(request):
    if request.method == "POST":
        # Form 데이터를 입력하고 등록요청 시 보여주는 부분
        form = PostForm(request.POST)
        # Form 데이터가 clean 한 상태
        if form.is_valid():
            # PostForm 으로 저장하는 법
            print(form.cleaned_data)
            post = Post.objects.create(author=User.objects.get(username=request.user),
                                       published_date=timezone.now(),
                                       title=form.cleaned_data['title'],
                                       text=form.cleaned_data['text'])
            # Post ModelForm 으로 저장하는 법
            # title, text 필드의 값이 저장된다.
            # post = form.save(commit=False)
            # post.author = User.objects.get(username=request.user)
            # post.published_date = timezone.now()
            # DB에 등록됨
            # post.save()
            return redirect('post_detail_test', pk=post.pk)
    else:
        # 등록 Form 보여주는 부분
        form = PostForm()
    return render(request, 'blog/post_edit.html', {'form': form})

@login_required
def post_edit(request, pk):
    post = get_object_or_404(Post, pk=pk)
    if request.method == "POST":
        form = PostModelForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = User.objects.get(username=request.user)
            post.published_date = timezone.now()
            post.save()
            return redirect('post_detail_test', pk=post.pk)
    else:
        form = PostModelForm(instance=post)
    return render(request, 'blog/post_edit.html', {'form': form})

@login_required
def post_delete(request, pk):
    post = get_object_or_404(Post, pk=pk)
    post.delete()
    return redirect('post_list')

 

- blog/templates/blog/base.html

<!DOCTYPE html>
{% load static %}
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Blog List</title>
    {% load static %}
    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
    <!-- Optional theme -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css" integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">
    <!-- google lobster font-->
    <link rel="stylesheet"
    href="https://fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext"
    type="text/css">
    <!-- 개발자 정의 CSS-->
    <link rel="stylesheet" href="{% static 'css/blog.css' %}">
    <!-- favicon 적용하기 -->
    <link rel="shortcut icon" href="{%static 'favicon.ico'%}">
</head>
<body>
    <div class="page-header">
        {% if user.is_authenticated %}
            <a href="{% url 'post_new' %}" class="top-menu">
                <span class="glyphicon glyphicon-plus"></span>
            </a>
            <p class="top-menu">Hello {{user.username}}
            <small>(<a href="{% url 'logout' %}?next={{request.path}}">Log out</a> )</small></p>
        {% else %}
            <a href="{% url 'post_new' %}" class="top-menu">
                <span class="glyphicon glyphicon-plus"></span>
            </a>
        {% endif %}
        <h1><a href="/">Django Blog</a></h1>
    </div>
    <div class="content container">
        <div class="row">
            <div class="col-md-8">
            {% block content %}
            {% endblock %}
            </div>
        </div>
    </div>


    {% comment "Optional note" %}
        Server comment
    {% endcomment %}
    <!-- html Client Comment -->
</body>
</html>

 

- blog/templates/blog/post_edit.html

{% extends 'blog/base.html' %}

{% block content %}
    <h1>New Post</h1>
    <form method="POST" class="post-form">
        {% csrf_token %}
        {{form.as_p}}
        <button type="submit" class="save btn btn-default">Save</button>
    </form>
{% endblock %}

 

- blog/templates/blog/post_detail.html

{% extends 'blog/base.html' %}

{% block content%}
    <div class="post">
        {% if post.published_date%}
        <div class="date">
            {{post.published_date}}
        </div>
        {% endif %}
        {% if user.is_authenticated %}
            <a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}">
                <span class="glyphicon glyphicon-pencil"></span>
            </a>
            <a class="btn btn-default" href="{% url 'post_delete' pk=post.pk %}">
                <span class="glyphicon glyphicon-remove"></span>
            </a>
        {% endif %}
        <h1>{{post.title|title}}</h1>
        <p>{{post.text|linebreaksbr}}</p>
    </div>
{% endblock %}

 

- blog/templates/registration/login.html

{% extends "blog/base.html" %}

{% block content %}
    {% if form.errors %}
        <p>이름과 비밀번호가 일치하지 않습니다. 다시 시도해주세요.</p>
    {% endif %}

    <form method="post" action="{% url 'login' %}">
        {% csrf_token %}
        <table class="table table-bordered table-hover">
            <tr>
                <td>{{form.username.label_tag}}</td><td>{{form.username}}</td>
                <td>{{form.password.label_tag}}</td><td>{{form.password}}</td>
            </tr>
        </table>

        <input type="submit" value="login" class="btn btn-primary btn-lg"/>
        <input type="hidden" name="next" value="{{next}}"/>
    </form>
{% endblock %}

 

- blog/urls.py

from django.urls import path
from . import views


urlpatterns = [
    # loacalhost:8080/
    path('', views.post_list, name='post_list'),
    # localhost:8080/post/5
    path('post/<int:pk>/', views.post_detail, name='post_detail_test'),
    # localhost:8080/post/new
    path('post/new/', views.post_new, name='post_new'),
    path('post/<int:pk>/edit', views.post_edit, name='post_edit'),
    path('post/<int:pk>/delete/', views.post_delete, name='post_delete'),
]

 

- /urls.py

from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
    path('accounts/login/', auth_views.LoginView.as_view(template_name="registration/login.html"), name="login"),
    path('account/logout/', auth_views.LogoutView.as_view(), {'next_page': None}, name="logout"),
]

 

- /settings.py

 

 

 

 

https://docs.djangoproject.com/en/3.0/topics/auth/default/

 

Using the Django authentication system | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com