23.02.08-django & HTML-Django

류소리·2023년 2월 13일
0

장고

목록 보기
7/9

도움: https://velog.io/@sawol/Django-%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC

amdin

urls.py


추가해줌.

    path('tag/<str:slug>/',views.tag_page),
    path('<int:pk>/new_comment/',views.new_comment),

views.py


추가해줌.

def tag_page(request, slug):
    tag = Tag.objects.get(slug=slug)
    post_list = tag.post_set.all()

    return render(
        request,
        'blog/post_list.html',
        {
            'post_list': post_list,
            'tag': tag,
            'categories': Category.objects.all(),
            'no_category_post_count': Post.objects.filter(category=None).count(),
        }
    ) 
    
def new_comment(request, pk):
    if request.user.is_authenticated:
        post = get_object_or_404(Post, pk=pk)

        if request.method == 'POST':
            comment_form = CommentForm(request.POST)
            if comment_form.is_valid():
                comment = comment_form.save(commit=False)
                comment.post = post
                comment.author = request.user
                comment.save()
                return redirect(comment.get_absolute_url())
        else:
            return redirect(post.get_absolute_url())
    else:
        raise PermissionDenied

blog-forms.py 만들어줌.

from .models import Comment
from django import forms

class Commentform(forms.ModelForm):
    
    class Meta:
        model = Comment
        fields = ("content",)

blog-templates-base.html 내용 추가

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block head_title %}Blog{% endblock %}</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css">
    <script src="https://kit.fontawesome.com/164c3bc1c6.js" crossorigin="anonymous"></script>
    {% comment %} https://fontawesome.com/ 에서 이메일로 받은 링크 클릭하여 회원 가입 후 kit code를 script안에 입력{% endcomment %} 
</head>
<body>
    {% include 'blog/navbar.html'%}
    <div class="container my-3">
        <div class="row">
            <div class="col-md-8 col-lg-9" id="main-area">
                {% block main_area %}
                {% endblock %}
            </div>
    
            <div class="col-md-4 col-lg-3">
                <!-- Search Widget -->   ### -> 이부분 추가 됨!!!
                <div class="card my-4">
                    <h5 class="card-header">Search</h5>
                    <div class="card-body">
                        <div class="input-group">
                            <input type="text" class="form-control" placeholder="Search for...">
                            <span class="input-group-btn">
                                    <button class="btn btn-secondary" type="button">Go!</button>
                                </span>
                        </div>
                    </div>
                </div>
    
                <!-- Categories Widget -->
                <div class="card my-4" id="categories-card">
                    <h5 class="card-header">Categories</h5>
                    <div class="card-body">
                        <div class="row">
                            <ul> 
                                {% for category in categories %}
                                <li>
                                    <a href="{{ category.get_absolute_url }}">{{ category }} ({{ category.post_set.count }})</a>
                                </li>
                                {% endfor %}
                                <li>
                                    <a href="/blog/category/no_category/">미분류 ({{ no_category_post_count }})</a>
                                </li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    {% block main_area %} {%endblock%}
    <div clas="fixed-bottom">

    {% include 'blog/footer.html'%}
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js"></script>
</body>
</html>

blog-templates-navbar

{% load socialaccount %}
  • 모달 추가
<!-- Modal -->
<div class="modal fade" id="loginModal" tabindex="-1" role="dialog" aria-labelledby="logInModalLabel"
     aria-hidden="true">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="loginModalLabel"> <i class="fas fa-sign-in-alt"></i>&nbsp Log In</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <div class="row">
                    <div class="col-md-6">
                        <a role="button" class="btn btn-outline-dark btn-block btn-sm " href="{% provider_login_url 'google' %}"><i class="fab fa-google"></i>&nbsp&nbsp Log in with Google</a>
                        <a role="button" class="btn btn-outline-dark btn-block btn-sm" href="/accounts/login/"><i class="far fa-envelope"></i>&nbsp&nbsp Log in with Username</a>
                    </div>
                    <div class="col-md-6">
                        <a role="button" class="btn btn-outline-dark btn-block btn-sm" href="/accounts/signup/"><i class="far fa-envelope"></i>&nbsp&nbsp Sign Up with E-mail</a>
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div

post_list

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

{% block main_area %}

    {% if user.is_authenticated%}
        {% if user.is_superuser or user.is_staff %}
            <a class="btn btn-info btn-sm float-right" href="/blog/create_post/" role="button"><i class="fas fa-pen"></i>&nbsp;&nbsp;New Post</a>
        {% endif %}
    {% endif %}

    <h1>Blog
        {% if search_info %}<small class="text-muted">{{ search_info }}</small>{% endif %}
        {% if category %}<span class="badge badge-secondary">{{ category }}</span>{% endif %}
        {% if tag %}<span class="badge badge-light"><i class="fas fa-tags"></i>{{ tag }} ({{ tag.post_set.count }})</span>{% endif %}
    </h1>
    {% if post_list.exists %}

    {% for p in post_list %}
    <!-- Blog Post -->
    <div class="card mb-4" id="post-{{p.pk}}">
        {% 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/seed/%7B%7B p.id }}/800/200" alt="random_image">
        {% endif %}
        <div class="card-body">
            {% if p.category %}
                <span class="badge badge-secondary float-right">{{ p.category }}</span>
            {% else %}
                <span class="badge badge-secondary float-right">미분류</span>
            {% endif %}

            <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.get_content_markdown | truncatewords_html:45 | safe }}</p>

            {% if p.tags.exists %}
                        <i class="fas fa-tags"></i>
                        {% for tag in p.tags.iterator %}
                            <a href="{{ tag.get_absolute_url }}"><span class="badge badge-pill badge-light">{{ tag }}</span></a>
                        {% endfor %}
                        <br/>
                        <br/>
                    {% endif %}

            <a href="{{ p.get_absolute_url }}" class="btn btn-primary">Read More &rarr;</a>
        </div>
        <div class="card-footer text-muted">
            Posted on {{ p.created_at}} by
            <a href="#">{{p.author | upper}}</a>
        </div>
    </div>
{% endfor %}

    {% else %}
        <h3>아직 게시물이 없습니다.</h3>
    {% endif %}

    {% if is_paginated %}

     <!-- Pagination -->
     <ul class="pagination justify-content-center mb-4">
        {% if page_obj.has_next %}
                <li class="page-item">
                    <a class="page-link" href="?page={{ page_obj.next_page_number }}">&larr; Older</a>
                </li>
            {% else %}
                <li class="page-item disabled">
                    <a class="page-link" href="#">&larr; Older</a>
                </li>
            {% endif %}

            {% if page_obj.has_previous %}
                <li class="page-item">
                    <a class="page-link" href="?page={{ page_obj.previous_page_number }}">Newer &rarr;</a>
                </li>
            {% else %}
                <li class="page-item disabled">
                    <a class="page-link" href="#">Newer &rarr;</a>
                </li>
            {% endif %}
    </ul>
    {% endif %}
{% endblock %}

Base-full 파일 만들기

<!DOCTYPE html>
<html>
{% load static %}
<head>
    <title>{% block head_title %}Blog{% endblock %}</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css%22%3E
    <script src="https://kit.fontawesome.com/193173e32d.js%22 crossorigin="anonymous"></script>
    
</head>

<body>
{% include 'blog/navbar.html' %}

<div class="container my-3">
    <div class="row">
        <div class="col-12" id="main-area">
            {% block main_area %}
            {% endblock %}
        </div>
    </div>
</div>
{% include 'blog/footer.html' %}

<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js%22%3E%3C/script%3E
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js%22%3E%3C/script%3E
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js%22%3E%3C/script%3E

</body>

</html>

templates/blog-post_form.html 생성

{% extends 'blog/base_full_width.html' %}
{% load crispy_forms_tags %}
{% block head_title %}Create Post - Blog{% endblock %}

{% block main_area %}
    <h1>Create New Post</h1>
    <hr/>
    <form method="post" enctype="multipart/form-data">{% csrf_token %}
        {{ form | crispy }} 
        <!-- 적용하고 싶은 폼에 아래와 같이 '| crispy' 붙이기 -->
        <div id="div_id_tags_str">
            <label for="id_tags_str">Tags:</label>
            <input type="text" name="tags_str" id="id_tags_str" class="textinput textInput form-control">
        </div>
        <br/>

        <button type="submit" class="btn btn-primary float-right">Submit</button>
    </form>
    {{form.media}}
{% endblock %}

templates/blog-comment_form.html 생성

{% extends 'blog/base_full_width.html' %}
{% load crispy_forms_tags %}
{% block head_title %}Edit Comment - Blog{% endblock %}

{% block main_area %}
    <h1>Edit Comment</h1>
    <hr/>
    <form method="post" id="comment-form">{% csrf_token %}
        {{ form | crispy }}
        <br/>
        <button type="submit" class="btn btn-primary float-right">Submit</button>
    </form>
{% endblock %}

templates/blog-post_update.html 생성

{% extends 'blog/base_full_width.html' %}
{% load crispy_forms_tags %}
{% block head_title %}Edit Post - Blog{% endblock %}

{% block main_area %}
    <h1>Edit Post</h1>
    <hr/>
    <form method="post" enctype="multipart/form-data">{% csrf_token %}
        {{ form | crispy }} 
        <!-- 적용하고 싶은 폼에 아래와 같이 '| crispy' 붙이기 -->
        <div id="div_id_tags_str">
            <label for="id_tags_str">Tags:</label>
            <input type="text" name="tags_str" id="id_tags_str" class="textinput textInput form-control">
        </div>
        <br/>

        <button type="submit" class="btn btn-primary float-right">Submit</button>
    </form>
    {{form.media}}
{% endblock %}

blog-url.py

path('delete_comment/<int:pk>/', views.delete_comment),
    path('update_comment/<int:pk>/', views.CommentUpdate.as_view()),
    path('update_post/<int:pk>/', views.PostUpdate.as_view()),
    path('create_post/', views.PostCreate.as_view()),

blog-views.py

from django.shortcuts import render, redirect
from django.views.generic import ListView, DetailView, CreateView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.utils.text import slugify
from django.shortcuts import get_object_or_404
from .models import Post, Category, Tag , Comment
from. forms import Commentform 
from django.core.exceptions import PermissionDenied
from django.db.models import Q
  • 추가해주기
class PostList(ListView):
    model = Post
    ordering = '-pk'

    def get_context_data(self, **kwargs):
        context = super(PostList, self).get_context_data()
        context['categories'] = Category.objects.all()
        context['no_category_post_count'] = Post.objects.filter(category=None).count()
        return context

class PostDetail(DetailView):
    model = Post
    def get_context_data(self, **kwargs):
        context = super(PostDetail, self).get_context_data()
        context['categories'] = Category.objects.all()
        context['no_category_post_count'] = Post.objects.filter(category=None).count()
        return context

class PostCreate(LoginRequiredMixin, UserPassesTestMixin, CreateView):
    model = Post
    fields = ['title', 'hook_text', 'content', 'head_image', 'file_upload', 'category']
    
    def test_func(self):
        return self.request.user.is_superuser or self.request.user.is_staff
    
    def form_valid(self, form):
        current_user = self.request.user
        if current_user.is_authenticated and (current_user.is_staff or current_user.is_superuser):
            form.instance.author = current_user
            response = super(PostCreate, self).form_valid(form)

            tags_str = self.request.POST.get('tags_str')
            if tags_str:
                tags_str = tags_str.strip()

                tags_str = tags_str.replace(',', ';')
                tags_list = tags_str.split(';')

                for t in tags_list:
                    t = t.strip()
                    tag, is_tag_created = Tag.objects.get_or_create(name=t)
                    if is_tag_created:
                        tag.slug = slugify(t, allow_unicode=True)
                        tag.save()
                    self.object.tags.add(tag)

            return response

        else:
                return redirect('/blog/')

class PostUpdate(LoginRequiredMixin, UpdateView):
    model = Post
    fields = ['title', 'hook_text', 'content', 'head_image', 'file_upload', 'category', 'tags']

    template_name = 'blog/post_update_form.html'

    def get_context_data(self, **kwargs):
        context = super(PostUpdate, self).get_context_data()
        if self.object.tags.exists():
            tags_str_list = list()
            for t in self.object.tags.all():
                tags_str_list.append(t.name)
            context['tags_str_default'] = '; '.join(tags_str_list)

        return context

    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated and request.user == self.get_object().author:
            return super(PostUpdate, self).dispatch(request, *args, **kwargs)
        else:
            raise PermissionDenied

    def form_valid(self, form):
        response = super(PostUpdate, self).form_valid(form)
        self.object.tags.clear()

        tags_str = self.request.POST.get('tags_str')
        if tags_str:
            tags_str = tags_str.strip()
            tags_str = tags_str.replace(',', ';')
            tags_list = tags_str.split(';')

            for t in tags_list:
                t = t.strip()
                tag, is_tag_created = Tag.objects.get_or_create(name=t)
                if is_tag_created:
                    tag.slug = slugify(t, allow_unicode=True)
                    tag.save()
                self.object.tags.add(tag)

        return response        

def category_page(request, slug):    # slug는 일반적으로 이미 얻은 데이터를 사용하여 유효한 url을 생성하는 방법
    if slug == 'no category':
        category = '미분류'
        post_list = Post.objects.filter(category=None)
    else:
        category = Category.objects.get(slug=slug)
        post_list = Post.objects.filter(category=category)

    return render(
        request,
        'blog/post_list.html',
        {
            'post_list': post_list,
            'categories': Category.objects.all(),
            'no_category_post_count': Post.objects.filter(category=None).count(),
            'category':category,
        }
    )

def tag_page(request, slug):
    tag = Tag.objects.get(slug=slug)
    post_list = tag.post_set.all()

    return render(
        request,
        'blog/post_list.html',
        {
            'post_list': post_list,
            'tag': tag,
            'categories': Category.objects.all(),
            'no_category_post_count': Post.objects.filter(category=None).count(),
        }
    ) 
    
def new_comment(request, pk):
    if request.user.is_authenticated:
        post = get_object_or_404(Post, pk=pk)

        if request.method == 'POST':
            comment_form = CommentForm(request.POST)
            if comment_form.is_valid():
                comment = comment_form.save(commit=False)
                comment.post = post
                comment.author = request.user
                comment.save()
                return redirect(comment.get_absolute_url())
        else:
            return redirect(post.get_absolute_url())
    else:
        raise PermissionDenied


class CommentUpdate(LoginRequiredMixin, UpdateView):
    model = Comment
    form_class = CommentForm

    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated and request.user == self.get_object().author:
            return super(CommentUpdate, self).dispatch(request, *args, **kwargs)
        else:
            raise PermissionDenied


def delete_comment(request, pk):
    comment = get_object_or_404(Comment, pk=pk)
    post = comment.post
    if request.user.is_authenticated and request.user == comment.author:
        comment.delete()
        return redirect(post.get_absolute_url())
    else:
        raise PermissionDenied


class PostSearch(PostList):
    paginate_by = None

    def get_queryset(self):
        q = self.kwargs['q']
        post_list = Post.objects.filter(
            Q(title__contains=q) | Q(tags__name__contains=q)
        ).distinct()
        return post_list

    def get_context_data(self, **kwargs):
        context = super(PostSearch, self).get_context_data()
        q = self.kwargs['q']
        context['search_info'] = f'Search: {q} ({self.get_queryset().count()})'

        return context        

내일 예습
로그인

  • 라이브러리 설치 : pip install django-allauth
    INSTALLED_APPS 추가
    AUTHENTICATION_BACKENDS 설정 SITE_ID=1 추가
  • 이메일 받는 것으로 설정
  • url 경로 추가
  • 구글 개발자 콘솔에서 새 프로젝트와 클라이언트 만들기 - console.developers.google.com 에 접속
    새 프로젝트 생성 > 만들기 > OAuth 동의화면 외부 선택 > 앱이름
    사용자 인증 정보 > 사용자 인증 정보 만들기 > OAuth 클라이언트 ID > 만들기 (유형, 이름, URL, URI 입력)
    admin 사이트 > SITES (로컬서버경로 127.0.0.1:8000 local development)
    navbar {% load socialaccount %}
    SOCIAL ACCOUNTS > social applications
    settings.py > LOGIN_REDIRECT_URL = '/blog/'
profile
새싹 빅테이터 개발자

0개의 댓글