본문 바로가기
Django(웹서비스 만들기)

[10주차] 프로젝트-2

by SOHEE- 2022. 1. 30.

상품 페이지에서 사용자 리뷰 및 별점 추가하기

사용자 리뷰 추가하기

사용자 리뷰용 모델 생성

content의 models.py 파일에 아래와 같이 필요한 정보를 수집할 테이블을 생성합니다.

class ProductReview(models.Model):
    review = models.TextField()
    nickname = models.TextField()
    product_id = models.IntegerField()
    star = models.IntegerField(default=0)

생성된 데이터베이스 테이블을 확인

 

사용자 리뷰 페이지 작성

사용자 리뷰 글씨 옆에 리뷰 등록을 위한 버튼을 놓고 모달로 동작하도록 설정하려고 합니다. 버튼을 누르면 'reviewModal' ID를 가진 모달이 실행되도록 data-bs-target의 값을 수정해 입력합니다.

<div style="margin-left: 40px; font-size: 26px">
    사용자 리뷰
    <button style="font-size: 18px" data-bs-toggle="modal" data-bs-target="#reviewModal">리뷰작성</button>
</div>

메인 페이지에서 상단 +버튼을 눌렀을때 동작하던 모달을 복사해서 ID를 'reviewModal'로 변경하고, 나눠져있던 div 칸을 없애고 텍스트 화면만 남겨놓습니다. 

<div class="modal fade" id="reviewModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="exampleModalLabel">리뷰 올리기</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <div class="mb-3">
                    <textarea id="input_content" class="form-control" id="exampleFormControlTextarea1"
                              style="height: 200px"></textarea>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                <button id="review_save_change" type="button" class="btn btn-primary">Save changes</button>
            </div>
        </div>
    </div>
</div>

 

사용자 리뷰 로직 작성

html 파일 하단에 모달 동작을 위해 자바스크립트를 추가합니다. 

$('#review_save_change').click(function () {
    const review = $('#input_content').val(); // 리뷰내용
    const nickname = "{{ user_info.nickname }}"; // 글작성자
    const star = rating.rate
    $.ajax({
        url: "{% url 'review' %}",
        method: "POST",
        data: {
            review: review,
            nickname: nickname,
            product_id: {{ product.id }},
            star: star
        },
        success: function (data) {
            console.log("성공");
            alert("리뷰 쓰기 성공");
        },
        error: function (request, status, error) {
            console.log("에러");
            alert("리뷰 쓰기 실패");
        },
        complete: function () {
            console.log("무조건실행");
            location.reload(true);
        }
    });
})

content의 views.py 파일로 가서 아래 코드를 추가 입력합니다.

class CreateReview(APIView):
    def post(self, request):
        review = request.data.get('review')
        nickname = request.data.get('nickname')
        product_id = request.data.get('product_id')
        star = request.data.get('star')

        ProductReview.objects.create(review=review, nickname=nickname,product_id=product_id,star=star)

        return Response(status=200)

코드를 실행하고 웹 페이지에서 데이터가 잘 저장되는지 확인합니다. 

 

 

별점 추가하기

별점 처리 코드는 apost님의 블로그를 참조했습니다.

위 블로그에 첨부된 별 그림 파일을 다운로드 받아 파이참의 media 폴더에 옮겨 놓고, 헤더 안에 아래 스타일 코드를 추가합니다.

<style>
.rating .rate_radio {
    position: relative;
    display: inline-block;
    z-index: 20;
    opacity: 0.001;
    width: 40px;
    height: 40px;
    background-color: #fff;
    cursor: pointer;
    vertical-align: top;
    display: none;
}

.rating .rate_radio + label {
    position: relative;
    display: inline-block;
    margin-left: -4px;
    z-index: 10;
    width: 40px;
    height: 40px;
    background-image: url("{% get_media_prefix %}starrate.png");
    background-repeat: no-repeat;
    background-size: 40px 40px;
    cursor: pointer;
    background-color: #f0f0f0;
}

.rating .rate_radio:checked + label {
    background-color: red;
}
</style>

사용자 리뷰를 작성하는 모달 안에 아래 코드를 textarea 아래에 붙여 넣습니다.

<div class="review_rating">
    <div class="warning_msg">별점을 선택해 주세요.</div>
    <div class="rating">
        <!-- 해당 별점을 클릭하면 해당 별과 그 왼쪽의 모든 별의 체크박스에 checked 적용 -->
        <input type="checkbox" name="rating" id="rating1" value="1" class="rate_radio" title="1점">
        <label for="rating1"></label>
        <input type="checkbox" name="rating" id="rating2" value="2" class="rate_radio" title="2점">
        <label for="rating2"></label>
        <input type="checkbox" name="rating" id="rating3" value="3" class="rate_radio" title="3점">
        <label for="rating3"></label>
        <input type="checkbox" name="rating" id="rating4" value="4" class="rate_radio" title="4점">
        <label for="rating4"></label>
        <input type="checkbox" name="rating" id="rating5" value="5" class="rate_radio" title="5점">
        <label for="rating5"></label>
    </div>
</div>

별점 동작을 위해 모달 실행 자바스크립트 내에 아래 코드를 추가로 입력합니다.

function Rating() {
};
Rating.prototype.rate = 0;
Rating.prototype.setRate = function (newrate) {
    //별점 마킹 - 클릭한 별 이하 모든 별 체크 처리
    this.rate = newrate;
    let items = document.querySelectorAll('.rate_radio');
    items.forEach(function (item, idx) {
        if (idx < newrate) {
            item.checked = true;
        } else {
            item.checked = false;
        }
    });
}
let rating = new Rating();//별점 인스턴스 생성

document.addEventListener('DOMContentLoaded', function () {
    //별점선택 이벤트 리스너
    document.querySelector('.rating').addEventListener('click', function (e) {
        let elem = e.target;
        if (elem.classList.contains('rate_radio')) {
            rating.setRate(parseInt(elem.value));
        }
    })
});

 

리뷰 출력하기

메인페이지에서 상품 상세페이지로 이동할 때 동작하는 로직인 ProductDetail(APIView) 안에 해당 상품 ID와 일치하는 리뷰를 불러올 수 있도록 reviews 리스트를 만들고, 이를 return 값으로 내보내도록 추가합니다. 

reviews=ProductReview.objects.filter(product_id=product.id).order_by('-id')
return render(request, 'content/productdetail.html',
              context=dict(product=product, user_info=find_user, reviews=reviews))

리뷰 등록 아래에 카드 형태로 리뷰가 표시되도록 설정합니다.

<div style="display: flex; flex-direction: row; margin: 40px">
    {% for r in reviews %}
        <div style="border: 1px solid black; width: 200px; height: 200px; padding: 10px; margin: 0px 10px">
            {% for i in "x"|rjust:r.star %}
                <span class="material-icons-outlined" style="color: red">star</span>
            {% endfor %}
            <div style="margin-top: 30px">
                {{ r.review }}
            </div>
        </div>
    {% endfor %}
</div>

웹 상에서 잘 출력되는지 테스트 해봅니다.