코딩공부/홈페이지 만들기

Django로 홈페이지 만들기 - #9 hashtag, serach

integerJI 2020. 5. 4. 23:34


참고 사이트

보고 배운 곳

https://wayhome25.github.io/django/2017/06/22/custom-template-filter/

 

오늘 사용한 App

myApp

 

사용한 명령어

python manage.py makemigrations

python manage.py migrate

 

생성한 파일 & 폴더

myApp/tamplates/search.html - 파일 생성

myApp/templatetags - 폴더 생성

myApp/templatetags/post_extras.py - 파일 생성

 

포인트

사용자 정의 필터를 사용하여 DB에 영향을 안 끼치게 해시태그를 만들 수 있었다.

초보몽키님의 블로그에서 많이 배웠다.

 

완성된 git ( 2020.06.18 키 노출로 인한 git 비공개 )

https://github.com/integerJI/int_1

 

aws key 노출되다.

https://integer-ji.tistory.com/200

 

aws key 생성시 주의 할점

https://integer-ji.tistory.com/208


사용한 디렉터리 계층구조 및 파일


myApp

# myProject/myApp/templatetags/post_extras.py

from django.conf.urls import url
from django import template
import re

register = template.Library()

@register.filter
def add_link(value):
    main_text = value.main_text
    tags = value.tag_set.all()

    for tag in tags:
        main_text = re.sub(r'\#'+tag.tag_name+r'\b', '<a href="/myApp/explore/tags/'+tag.tag_name+'">#'+tag.tag_name+'</a>', main_text)
    return main_text
# myProject/myApp/admin.py

from django.contrib import admin
from .models import Post, Comment, Tag

# Register your models here.
admin.site.register(Post)
admin.site.register(Comment)
admin.site.register(Tag)
# myProject/myApp/models.py


class Post(models.Model):


    tag_set = models.ManyToManyField('Tag', blank=True)

	# ... 생략
    
    def tag_save(self):
        tags = re.findall(r'#(\w+)\b', self.main_text)

        if not tags:
            return

        for t in tags:
            tag, tag_created = Tag.objects.get_or_create(tag_name=t)
            self.tag_set.add(tag)

class Tag(models.Model):
    tag_name = models.CharField(max_length=140, unique=True)

    def __str__(self):
        return self.tag_name
# myProject/myApp/views.py

# ... 생략

def post(request):
    if request.method == "POST":
        form = PostForm(request.POST, request.FILES)
        
        if form.is_valid(): 
            post = form.save(commit = False)
            post.create_user = User.objects.get(username = request.user.get_username())
            post.save()
#★         post.tag_save()
        return redirect(reverse('index'))
    else:
        form = PostForm() 
        return render(request, 'post.html', {'form' : form})  


def update(request, post_id):
    post = Post.objects.get(id = post_id)
    conn_profile = User.objects.get(username = request.user.get_username())
        
    if request.method == 'POST':
        form = PostForm(request.POST, request.FILES, instance=post)

        if form.is_valid():
            if conn_profile == post.create_user:
                post = form.save(commit=False)
                post.save()
#★             post.tag_save()

                context = {'post': post, 'form': form}
                content = request.POST.get('content')
                        
                messages.info(request, '수정 완료')
                return render(request, 'detail.html', context=context)
                
            else:
                messages.info(request, '수정할 수 없습니다.')
                return render(request, 'detail.html', {'post': post})
    else:
        if conn_profile == post.create_user:
            form = PostForm(instance = post)
            return render(request, 'update.html', {'post': post, 'form': form})
        else:
            messages.info(request, '수정할 수 없습니다.')
            return redirect(reverse('index'), post_id)
            
            
# ... new            
            
def search(request, tag=None):
    posts = Post.objects.all().order_by('-id')
    conn_user = request.user
    conn_profile = Profile.objects.get(user=conn_user)

    if not conn_profile.profile_image:
        pic_url = ""
    else:
        pic_url = conn_profile.profile_image.url

    q = request.POST.get('q', "") 

    if tag:
        posts = Post.objects.filter(tag_set__tag_name__iexact=tag).prefetch_related('tag_set').select_related('create_user')
        return render(request, 'search.html', {'posts' : posts, 'pic_url' : pic_url, 'q' : q, 'tag': tag})

    if q:
        posts = posts.filter(main_text__icontains=q)
        return render(request, 'search.html', {'posts' : posts, 'pic_url' : pic_url, 'q' : q})
    
    else:
        return render(request, 'search.html')
# myProject/myApp/urls.py

path('search', views.search, name='search'),
path('explore/tags/<str:tag>/', views.search, name='search'),
<!--myApp/templates/index.html-->

{% extends 'base.html' %}
#★ {% load post_extras %}
{% block content %}

# ... 생략

{% if post.post_likes.all %}
<a class="like" name="{{ post.pk }}"><img id="like-img{{ post.pk }}" src="/static/like_after.png" alt="클릭시 좋아요 취소"
    style="width: 2rem; height:2rem;"></a>
{% else %}
<a class="like" name="{{ post.pk }}"><img id="like-img{{ post.pk }}" src="/static/like_before.png" alt="클릭시 좋아요"
    style="width: 2rem; height:2rem;"></a>
{% endif %}

<a href="{% url 'detail' post.id %}"><img src="/static/detail.png" alt="detail 이동"
    style="width: 2rem; height:2rem;"></a>
<div class="group">
  <br>
  <small class="text-muted"><span class="like_count" id="count{{ post.pk }}">{{ post.total_likes }}
      like</span></small>
  <small class="text-muted">/</small>
  <small class="text-muted">{{post.comments.count}} comment</small>
  <br>
  <a class="my-0 font-weight-normal" href="{% url 'userpage' post.create_user %}">{{ post.create_user }}</a>
#★  <a class="card-title pricing-card-title">{{ post|add_link|safe|linebreaksbr }}</a>
  <br>
  {% for comment in post.comments.all %}
  
# ... 생략
<!--myApp/templates/detail.html-->

{% extends 'base.html' %}
#★ {% load post_extras %}
{% block content %}

# ... 생략

{% if post.post_likes.all %}
<a class="like" name="{{ post.pk }}"><img id="like-img{{ post.pk }}" src="/static/like_after.png" alt="클릭시 좋아요 취소"
        style="width: 2rem; height:2rem;"></a>
{% else %}
<a class="like" name="{{ post.pk }}"><img id="like-img{{ post.pk }}" src="/static/like_before.png" alt="클릭시 좋아요"
        style="width: 2rem; height:2rem;"></a>
{% endif %}

<a href="{% url 'detail' post.id %}"><img src="/static/detail.png" alt="detail 이동"
        style="width: 2rem; height:2rem;"></a>

<br>
<small class="text-muted"><span class="like_count" id="count{{ post.pk }}">{{ post.total_likes }}
        like</span></small>
<small class="text-muted">/</small>
<small class="text-muted">{{post.comments.count}} comment</small>
<hr>
#★ <a class="card-title pricing-card-title">{{ post|add_link|safe|linebreaksbr }}<small
        class="text-muted">&nbsp;&nbsp;{{ post.create_date }}</small></a>
<hr>
{% for comment in post.comments.all %}

# ... 생략
<!-- myProject/myProject/templates/base.html -->

# ... 생략

<body>
    <a class="hidden"><span id="user_name">{{ user.username }}</span></a>
    <div class="container">
        <header class="blog-header py-3">
            <div
                class="container d-flex flex-column flex-md-row justify-content-between bg-white border-bottom p-2 mb-6">

                <h4 class="my-0 font-weight-normal"><a class="text-muted" href="{% url 'index' %}">Eomu Hae</a></h4>

#★                <form class="form-inline my-2 my-lg-0" action="{% url 'search' %}" method="post">
                    {% csrf_token %}
                    <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" name="q"
                        value="{{q}}">
                </form>

                <div class="btn-group">
                    <a class="dropdown-item" href="{% url 'signout'%}">Sign Out</a>
                </div>

            </div>
        </header>
        <br>
        {% block content %}
        {% endblock %}

    </div>
</body>

# ... 생략
<!--myApp/templates/search.html-->

{% extends 'base.html' %}
{% load post_extras %}
{% block content %}

<head>
    <style>
        .hidden {
            display: none
        }
    </style>
</head>

<body>
    <main role="main" class="container">
        <div class="row justify-content-center">
            <div class="col-md-7 blog-main">
                <div class="container">
                    {% for post in posts.all %}
                    <div class="card-deck mb-3">
                        <div class="card mb-4 shadow-sm">
                            <div class="card-header">

                                <!-- 접속자와 글쓴이가 같으면 -->
                                <div class="btn-group" style="float: left;">
                                    <a class="my-0 font-weight-normal"
                                        href="{% url 'userpage' post.create_user %}"><span
                                            class="writer_name">{{ post.create_user }}</span></a>
                                </div>

                                <!-- 점속자와 글쓴이가 같지 않으면 사라짐 -->
                                <div class="btn-group" style="float: right;">
                                    <span class="hidden" id="control_id">
                                        <a href="{% url 'update' post.id %}"><img src="/static/update.png" alt="update"
                                                style="width: 1.5rem; height:1.5rem;">&nbsp;</a>
                                        <a href="{% url 'delete' post.id %}"><img src="/static/delete.png" alt="delete"
                                                style="width: 1.5rem; height:1.5rem;"></a>
                                    </span>
                                </div>

                            </div>
                            {% if post.create_img %}
                            <div class="text-center">
                                <img src="{{post.create_img.url}}" class="rounded" alt="..." width="585.5dp"
                                    height="585.5dp">
                            </div>
                            {% else %}
                            <a>no</a>
                            {% endif %}
                            <div class="card-body">
                                {% if post.likes.all %}
                                <a class="like" name="{{ post.pk }}"><img id="like-img{{ post.pk }}"
                                        src="/static/like_after.png" alt="클릭시 좋아요 취소"
                                        style="width: 2rem; height:2rem;"></a>
                                {% else %}
                                <a class="like" name="{{ post.pk }}"><img id="like-img{{ post.pk }}"
                                        src="/static/like_before.png" alt="클릭시 좋아요"
                                        style="width: 2rem; height:2rem;"></a>
                                {% endif %}

                                <a href="{% url 'detail' post.id %}"><img src="/static/detail.png" alt="detail 이동"
                                        style="width: 2rem; height:2rem;"></a>
                                <div class="group">
                                    <br>
                                    <small class="text-muted"><span class="like_count"
                                            id="count{{ post.pk }}">{{ post.total_likes }} like</span></small>
                                    <small class="text-muted">/</small>
                                    <small class="text-muted">{{post.comments.count}} comment</small>
                                    <br>
                                    <a class="my-0 font-weight-normal"
                                        href="{% url 'userpage' post.create_user %}">{{ post.create_user }}</a>
                                    <a class="card-title pricing-card-title">{{ post|add_link|safe|linebreaksbr }}</a>
                                    <br>
                                    {% for comment in post.comments.all %}
                                    <a class="card-text" href="{% url 'userpage' comment.comment_user %}"><span
                                            class="comment_writer_name">{{comment.comment_user}}</span></a>
                                    <a class="card-text">{{comment.comment_text}}
                                        <span class="control hidden" id="control_id1{{ forloop.counter0 }}">
                                            <form action="{% url 'c_delete' post.pk comment.pk %}" method="POST"
                                                style="display:inline-block">
                                                {% csrf_token %}
                                                <input type="`" name="app_url" id="app_url" value="{{app_url}}" />
                                                <button type="submit">삭제</button>
                                            </form>
                                        </span>
                                        <br>
                                        {% endfor %}
                                        <small class="text-muted">{{ post.create_date }}</small>
                                </div>
                                <br>
                                <div class="group">

                                    <form action="{% url 'c_post' post.id %}" method="POST">
                                        {% csrf_token %}
                                        <div class="input-group input-group-sm mb-3">
                                            <input type="text" class="form-control" aria-label="Sizing example input"
                                                aria-describedby="inputGroup-sizing-sm" name="comment_text"
                                                id="comment_text" placeholder="댓글 달기 ..." style="display:inline-block">
                                            <input type="hidden" name="app_url" id="app_url" value="{{app_url}}" />
                                            <div class="input-group-prepend">
                                                <button class="input-group-text" id="inputGroup-sizing-sm"
                                                    type="submit">게시</button>
                                            </div>
                                        </div>
                                    </form>
                                </div>
                            </div>
                        </div>
                    </div>
                    {% endfor %}
                </div>
            </div>

            <aside class="col-md-3 blog-sidebar">
                <div class="card mb-3 bg-light rounded" style="max-width: 540px;" bg-light rounded>
                    <div class="row no-gutters">
                        <a></a>
                        <div class="col-md-5 d-flex align-items-center d-flex justify-content-center">
                            <img src="{{ pic_url }}" alt="..." class="rounded-circle" width="60" height="60">
                        </div>
                        <div class="col-md-6">
                            <div class="card-body">
                                <a><span id="user_name">{{user.username}}</span></a>
                                <br>
                                <a href="{% url 'mypage' %}">My Profile</a>
                                <br>
                                <a href="{% url 'post' %}">New Post</a>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="p-3">
                    <h4 class="font-italic">Producer</h4>
                    <ol class="list-unstyled">
                        <li><a href="https://github.com/integerJI">GitHub</a></li>
                        <li><a href="https://www.instagram.com/integer_ji/">Instagram</a></li>
                    </ol>
                    <footer class="my-2 pt-2 text-muted text-center text-small">
                        <p class="mb-1">&copy; 2019-2020 Integer Ji</p>
                    </footer>
                </div>
            </aside>

        </div>
    </main>



    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script type="text/javascript">

        $("#user_name").text()
        $(".writer_name").text()

        $(".writer_name")
        $(".writer_name")[0].innerHTML

        for (i = 0; i < $(".writer_name").length; i++) {
            if ($("#user_name").text() == $(".writer_name")[i].innerHTML) {
                $("#control_id").removeClass("hidden");
            }
        }


        $('.like').click(function () {
            var pk = $(this).attr('name')

            $.ajax({
                type: "POST",
                url: "{% url 'like' %}",
                data: { 'pk': pk, 'csrfmiddlewaretoken': '{{ csrf_token }}' },
                dataType: "json",

                success: function (response) {
                    if (response.likes_count == 1) {
                        $('#count' + pk).html(response.likes_count + ' like ');
                        $('#like-img' + pk).attr('src', '/static/like_after.png');
                    } else if (response.likes_count == 0) {
                        $('#count' + pk).html('0 like');
                        $('#like-img' + pk).attr('src', '/static/like_before.png');
                    } else {
                        $('#count' + pk).html(response.likes_count + ' like');
                        $('#like-img' + pk).attr('src', '/static/like_after.png');
                    }
                    alert(response.message);
                },
                error: function (request, status, error) {
                    alert('로그인이 필요합니다.')
                }
            });
        })

    </script>

</body>
{% endblock %}

마무리