이번 포스팅에서는 게임세부정보를 만들어볼것입니다.
게임 세부정보에서는 게임유튜브동영상, 출시일/사양/개발사 등의 정보, 유통사별 가격, 댓글 및 좋아요기능이 있습니다.
■ 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 %}
{{ 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>
<a href="{% url 'game:update' game.pk %}" class='btn btn-warning' style="font-weight: bold">수정</a>
{% endif %}
{% endblock content %}
■ 게임 상세정보 결과물
해당게임의 epic_games의 데이터에는 0이 들어있기 때문에 가격부분에서 표시되지 않는다.
'자격증 & 문제풀이 > Django-Project' 카테고리의 다른 글
[Django] 게임 목록(검색, 필터, 가격필터 기능 및 페이징 기능 구현 그리고 최저가 기능 구현) (0) | 2020.07.20 |
---|---|
[Django] Django와 데이터 연동 (0) | 2020.07.19 |
[Django] Game Update와 Delete (0) | 2020.07.19 |
[Django] Game Model과 Game 등록(create) (0) | 2020.07.19 |
[Django] Base.html (0) | 2020.07.19 |