자격증 & 문제풀이/Django-Project

[Django] 게임세부정보(game_detail)[댓글, 좋아요 기능]

YSY^ 2020. 7. 20. 00:13

이번 포스팅에서는 게임세부정보를 만들어볼것입니다.

게임 세부정보에서는 게임유튜브동영상, 출시일/사양/개발사 등의 정보, 유통사별 가격, 댓글 및 좋아요기능이 있습니다.

 

■ models.py

댓글기능을 하는 class를 추가합니다.

CustomUser를 accounts에서 가지고 와서 댓글을 누가썼는지 알수있기 합니다.

post는 게임detail페이지이며 date는 작성일, content는 내용, writer는 로그인한 작성자입니다.

마지막으로 먼저 쓴 글이 밑으로 내려가도록 '-'를 이용하여 역순으로 배열합니다.

#  댓글
from accounts.models import CustomUser
class Comment(models.Model):
    post= models.ForeignKey(Game, on_delete=models.CASCADE, null=True, related_name='comments')
    comment_date= models.DateTimeField(auto_now_add=True)
    comment_contents= models.CharField(max_length=200)
    comment_writer= models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, related_name = 'game_comment_writer')

    class Meta:
        ordering= ['-id']

 

■ views.py

DetailView를 이용하여 세부정보를 볼 수 있습니다.

from django.views.generic import DetailView, CreateView, UpdateView, DeleteView


class GameDetailView(DetailView):
    template_name = 'game/game_detail.html'
    model = Game

 

다음은 댓글추가를 하는 코드입니다.

get_object_or_404 함수는 Django 모델을 첫번째 인자로 받고, 몇개의 키워드 인수를 모델 관리자의 get() 함수에 넘깁니다. 만약 객체가 존재하지 않을 경우, Http404 예외가 발생합니다.

get_object_or_404를 이용해서 post(게임세부정보)를 받아오고 get를 이용해서 유저가 입력한(댓글내용)을 가지고 옵니다. 수집된 정보로 댓글(comment)를 만들고 다시 게임세부정보(detail)로 돌아갑니다.

 

from django.shortcuts import  redirect, render, get_object_or_404
from django.views.generic import DetailView, CreateView, UpdateView, DeleteView

from django.urls import reverse, reverse_lazy

#로그인
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator

from .models import Game, Genre_list, Comment
from .forms import GameCreateForm


@login_required
def comment_write(request, pk):
    if request.method == 'POST':
        post = get_object_or_404(Game, pk=pk)
        content = request.POST.get('content')
        conn_user = request.user
        Comment.objects.create(post=post, comment_writer=conn_user, comment_contents=content)
        return redirect(reverse('game:detail', args=[pk]))
        

 

다음은 댓글을 삭제하는 코드입니다.

여기서는 pk가 두개필요합니다. 게임세부정보(post)의 pk, 그리고 댓글의pk입니다. 게임세부정보의 pk는 game_pk로 따로 지정하겠습니다.

만약 댓글을 쓴 유저와 로그인하고 있는 유저가 같다면 댓글을 삭제하고 다시 게임세부정보(detail)로 돌아갑니다.

그렇지 않다면 auth_error을 띄우고 게임세부정보(detail)로 돌아갑니다.

# 댓글 삭제
@login_required
def comment_delete(request, pk):

    comment = Comment.objects.get(pk=pk)
    game_pk = comment.post.pk
        
    if comment.comment_writer == CustomUser.objects.get(username = request.user.get_username()):
        comment.delete()
        return redirect(reverse('game:detail',  args=[game_pk]))
    else:
        return render(request, 'game/game_detail.html', {'comment':comment, "auth_error":"'해당댓글에 대한 삭제 권한이 없습니다.'"})

 

다음은 좋아요 기능 구현입니다.

def like(request, pk): 
    game = get_object_or_404(Game, id=pk)
    
    if request.user in game.like_users.all():
        #좋아요 취소
        game.like_users.remove(request.user)
    else:
        game.like_users.add(request.user)
    
    return redirect('game:detail', pk = pk)

 

■ urls.py

from django.urls import path

from . import views

app_name = 'game'
urlpatterns = [
    path('<int:pk>/detail/', views.GameDetailView.as_view(), name='detail'),
    path('create/', views.GameCreateView.as_view(), name='create'),
    path('<int:pk>/update/',views.GameUpdateView.as_view(), name = 'update'),
    path('<int:pk>/delete/', views.game_delete, name='delete'),
    path('<int:pk>/comment/write/', views.comment_write, name="comment_write"),
    path('<int:pk>/comment_delete/', views.comment_delete, name="comment_delete"),
    path('<int:pk>/like/', views.like, name='like'),
]

 

■ game_detail.html

좋아요 기능구현 -> 유튜브동영상 로드 -> 게임세부정보표시 -> 유통사별 가격표시 -> 댓글표시 -> 게임정보 수정 및 삭제(로그인 했을 경우) 순서로 html이 짜져있습니다.

이전포스팅인 데이터연동 포스팅에서 언급했듯이 price가 0이라면 게임이 없다는 의미이므로 표시하지 않게 조치를 취하였습니다.

그리고 게임유통사를 누르면 스팀은 app_id가 있기때문에 게임정보페이지로 바로가지만, 다른 사이트들은 app_id가 없기때문에 해당게임을 검색한 목록까지만 보여주도록 하였습니다.

{% extends 'base.html' %}

{% block title %} GAME_DETAIL {% endblock title %}

{% block content %}

<div class="container">
	<div class="row">
		<div class="col-md-12">
			<br>
			<h6 style="color: #808080">모든 게임 > {{game.genre}}</h6> 
			<h2 style="color: #FFC107">{{game.title}}</h2>
				<p style="color: #CDCDCD">좋아요 :  <!--좋아요 기능-->
				<a href="{% url 'game:like' game.id %}"></a>
					{% if user in game.like_users.all %}
						<a href="{% url 'game:like' game.id %}"><i class="fas fa-heart"></i></a>
					{% else %}
						<a href="{% url 'game:like' game.id %}"><i class="far fa-heart"></i></a>
					{% endif %}
				&nbsp;&nbsp;&nbsp;&nbsp; {{ game.like_users.count }} 명이 좋아합니다.</p>
			<br>
		</div>
		<div class="col-md-7">
			{% load embed_video_tags %} 
			{% video game.video '100% x 100%' %}
		</div>
		<!-- 탭 -->
		<div class="col-md-5">
			<div class="tabbable" id="tabs-442371">
				<ul class="nav nav-tabs">
					<li class="nav-item">
						<a class="nav-link active" href="#tab1" data-toggle="tab" style="color: #FFC107">개요</a>
					</li>
					<li class="nav-item">
						<a class="nav-link" href="#tab2" data-toggle="tab" style="color: #FFC107">최소사양</a>
					</li>
					<li class="nav-item">
						<a class="nav-link" href="#tab3" data-toggle="tab" style="color: #FFC107">권장사양</a>
                    </li>
                    <li class="nav-item">
						<a class="nav-link" href="#tab4" data-toggle="tab" style="color: #FFC107">추가정보</a>
					</li>
				</ul>
				<div class="tab-content">
					<div class="tab-pane fade show active" id="tab1">
						<p>
							<dt>출시일</dt>
							<dd>{{game.release_at}}</dd>
							<dt>개발사</dt>
							<dd>{{game.developer}}</dd>
							<dt>장르</dt>
							<dd>{{game.genre}}</dd>

						</p>
					</div>
				<div class="tab-pane fade" id="tab2">
						<p>
							<dt>PC 최소사양</dt>
							<dd>{{game.pc_requirements_minimum}}</dd>
						</p>
				</div>
				<div class="tab-pane fade" id="tab3">
					<p>
						<dt>PC 권장사양</dt>
						<dd>{{game.pc_requirements_recommended}}</dd>
					</p>
			</div>
                <div class="tab-pane fade" id="tab4">
					<br>
					<p>추가정보</p>
				</div>
				</div>
			</div>
		</div>
		<div class="col-md-12">
			<br>
			<br>
			<h4 style="color: #CDCDCD">게임 소개</h4>
			<p style="color: #CDCDCD">
				{{game.info}}
			</p>
		</div>
		<!-- 테이블 -->
		<div class="col-md-12">
			<br>
			<br>
			<table class = table>
				<thead  align = "center" style="background-color: #000000; color: #CDCDCD">
						<th>구매 가능한 에디션</th>
						<th>가격</th>
						<th>판매처</th>
					</tr>
				</thead>
				<tbody align = "center" style="background-color: #353535; color: #CDCDCD">
					{% if game.steam != 0 %}
					<tr class="table-active" >
						<td> <!--구매가능한 에디션-->
							{{game.title}}
						</td>
						<td> <!--최저가-->
							₩ {{game.steam}}
						</td>
						<td> <!--최저가 판매처-->
							<a style="color: #FFC107" href="https://store.steampowered.com/app/{{game.app_id}}" onclick="window.open(this.href); return false;">Steam</a>
						</td>
					</tr>
					{%endif%}

					{% if game.origin != 0 %}
					<tr class="table-active">
						<td> <!--구매가능한 에디션-->
							{{game.title}}
						</td>
						<td> <!--최저가-->
							₩ {{game.origin}}
						<td> <!--최저가 판매처-->
							<a style="color: #FFC107" href="https://www.origin.com/kor/ko-kr/search?searchString={{game.title}}" onclick="window.open(this.href); return false;">Origin</a>
						</td>
					</tr>
					{%endif%}
					
					{% if game.uplay != 0 %}
					<tr class="table-active">
						<td> <!--구매가능한 에디션-->
							{{game.title}}
						</td>
						<td> <!--최저가-->
							₩ {{game.uplay}}
						<td> <!--최저가 판매처-->
							<a style="color: #FFC107" href="https://store.ubi.com/kr/search?q={{game.title}}" onclick="window.open(this.href); return false;">Uplay</a>
						</td>					
					</tr>
					{%endif%}

					{% if game.epic_games != 0 %}
					<tr class="table-active">
						<td> <!--구매가능한 에디션-->
							{{game.title}}
						</td>
						<td> <!--최저가-->
							₩ {{game.epic_games}}
						<td> <!--최저가 판매처-->
							<a style="color: #FFC107" href="https://www.epicgames.com/store/ko/browse?q={{game.title}}&sortBy=releaseDate&sortDir=DESC&pageSize=30" onclick="window.open(this.href); return false;">Epic Games</a>
						</td>
					</tr>
					{%endif%}

					{% if game.drmfree != 0 %}
					<tr class="table-active">
						<td> <!--구매가능한 에디션-->							
							{{game.title}}
						</td>
						<td> <!--최저가-->
							₩ {{game.drmfree}}
						<td> <!--최저가 판매처-->
							<a style="color: #FFC107" href="https://directg.net/game/game_search.html?searchValue={{game.title}}" onclick="window.open(this.href); return false;">Direct Games</a>
						</td>
					</tr>
					{%endif%}
				</tbody>
            </table>
		</div>
		<div align="left" class="col-md-12 offset-md-0">    
			<br>
			<br>
			<h4>댓글</h4>
			<br>
		</div>
		{% for comment in game.comments.all %}
		<div class="col-md-12 offset-md-0" style="border-radius: 10px; margin-bottom: 20px; padding: 40px; background-color: #101010">      
    		<div class="comment">
        	    <h5 style="color: #CDCDCD">작성자: {{ comment.comment_writer }}</h5>
        		<div class="date" style="color: #606060">{{ comment.comment_date }} </div>
				<br>
				<p style="color: #CDCDCD">{{ comment.comment_contents }}</p>
				<a href="{% url 'game:comment_delete' comment.pk %}" style="font-weight: bold" class='btn btn-warning' onclick="return confirm('이 글을 삭제 하겠습니까?')">삭제</a>
    		</div>
			{% empty %}
			<div class="col-md-12 offset-md-0">
				<p style="color: #FFC107">첫 번째 댓글을 작성해 보세요.</p>
			</div>
			{% endfor %}
			<div class="col-md-12 offset-md-0"><br></div>
			<div class="col-md-12 offset-md-0" style="border-radius: 10px; padding: 40px; background-color: #101010">
				<h6 style="color: #CDCDCD">새 댓글 등록</h6>
				<br>
				<form action="{% url 'game:comment_write' game.pk %}" method="POST">
					{% csrf_token %}
					<input type="text" class="form-control" name="content" placeholder="댓글을 입력해 주세요.">
					<br>
					<input style="font-weight: bold" class="btn btn-warning" type="submit" value="등록">
    			</form>
			</div>
		</div>
	</div>
</div>
<br>
<br>
<br>
<br>
        {% if user.is_authenticated %}
            <a href="{% url 'game:delete' game.pk %}" class='btn btn-warning' onclick="return confirm('이 글을 삭제 하겠습니까?')" style="font-weight: bold">삭제</a>&nbsp;
            <a href="{% url 'game:update' game.pk %}" class='btn btn-warning' style="font-weight: bold">수정</a>&nbsp;
        {% endif %}
{% endblock content %}  

 

■ 게임 상세정보 결과물

해당게임의 epic_games의 데이터에는 0이 들어있기 때문에 가격부분에서 표시되지 않는다.

 

728x90
반응형