▶️ Django 

[▶️Front-end] Bootstrap으로 화면 구성(회원 가입, 로그인, 로그아웃)

main 페이지에 추후 수정이 필요한 내용 중, 좋아요&싫어요 버튼 기능 구현 과정을 담은 글이에요.

 

Article 모델만 완성한 글입니다!


main page
구현할 article 목록들

지금의 메인 페이지예요.
폴라로이드 사진으로 된 곳에 유저가 만든 모델을 불러오도록 하고, 좋아요&싫어요 버튼이 추가될 거예요.

이번 글에는 게시글 겉에 표현될 생성된 모델 사진, 유저 id, 작업 id, 좋아요&싫어요를 구현하는
Back-end 로직 내용을 다룰 거예요.

구현 시작!

구현하기에 앞서서, 좋아요 기능은 M:N이기 때문에 DB 관리를 어떻게 할 것이고,
좋아요 수는 어떻게 카운팅 할지, 모델은 어떻게 구성할지 고민을 했어요. 🤔

⚙️ branch 생성

# article branch 생성
$ git branch article
# article branch로 이동
$ git switch article

⚙️ articles app 생성

# articles app
$ python manage.py startapp articles

⚙️ core/settings.py

# core/settings.py
INSTALLED_APPS = [
    ...
    
    # Create app list
    ...
    "articles", # 메인 화면에 있는 글 관리 앱
]

⚙️ core/urls.py

# core/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    ...
    path("articles/", include('articles.urls')),
]

⚙️ articles/models.py

더보기
# articles/models.py
from django.db import models
from django.conf import settings
from workspace.models import MeshModel
import random
"""
3월 5일까지
게시글 겉에 보일 것들 : 
- 생성된 모델 사진 
- 유저 id
- 작업 id
- 좋아요, 싫어요 버튼

3월 5일 이후로 할 것들 ⬇️
게시글 상세 내용 :
(왼편)
- 생성된 모델 사진
- 타이틀 (모델 프롬프트)
- 태그
- 모델 시드 (pk)
- 모델 프롬프트 (복사 버튼)
- 텍스쳐 프롬프트 (복사 버튼)

(오른편)- 추후 추가할지 말지 결정하자.
- 유저 프로필
- 팔로우 버튼
- 유저의 다른 모델 작품 리스트(미리보기, 모델 프롬프트) -> 추후
"""

# article 모델
"""
아래 내용을 포함하고 있어요.
user id, job id, title, model&texture_prompt, model_seed, image, likes, dislikes, created_at, tag

model_seed : 랜덤으로 1 ~ 2147483648 숫자 범위 내에서 부여됩니다.
-> def save, def generate_unique_model_seed로 저장 및 유니크 관리

"""
class Article(models.Model):
    user_id = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="articles"
        ) # 유저 아이디
    job_id = models.ForeignKey(MeshModel, on_delete=models.CASCADE) # 작업 아이디
    title = models.CharField(max_length=255, default="model_prompt") # 게시글 제목
    model_prompt = models.TextField()
    texture_prompt = models.TextField()
    model_seed = models.IntegerField(unique=True)
    image = models.ImageField(upload_to="article/image/") # 모델 이미지
    likes = models.ManyToManyField(
        settings.AUTH_USER_MODEL, related_name="like_articles"
    ) # 좋아요
    dislikes = models.ManyToManyField(
        settings.AUTH_USER_MODEL, related_name="dislike_articles"
    ) # 싫어요
    created_at = models.DateTimeField(auto_now_add=True) # 생성 시간
    tags = models.CharField
    
    # model_seed는 정한 범위값 내에서 랜덤으로 부여(중복 허용 X)
    def save(self, *args, **kwargs):
        if not self.model_seed:  # 값이 없을 때만 생성
            self.model_seed = self.generate_unique_model_seed()
        super().save(*args, **kwargs)

    def generate_unique_model_seed(self):
        while True:
            number = random.randint(1, 2147483648)  # 원하는 범위 설정
            if not Article.objects.filter(model_seed=number).exists():
                return number

⚙️ articles/urls.py

더보기
# articles/urls.py
from django.urls import path
from .views import ArticleList

app_name = "articles"
urlpatterns = [
    path("articlelist/", ArticleList, name="articlelist"), # 게시물 목록
]

⚙️ articles/views.py

더보기
# articles/views.py
from django.shortcuts import render

# 게시글 목록 보기
def ArticleList(request):
    return render(request, "main.html")

마무리

Front는 이전에 만들었던 main.html을 쓸 거라서 추가하진 않았어요.

하지만 수정은 하였죠!

 

<수정한 내용>

<!-- Image Gallery (Right) -->
<div class="col-md-9">
    <div class="row">
        {% for article in article_list %}
            <div class="col-md-4">
                <div class="card">
                    <div class="card-img-top">
                        <i class="fas fa-image fa-5x" style="color: lightgray;"></i>
                    </div>
                    <div>
                        <form method="post" action="{% url "articles:articlelike" article.id "like" %}">
                            {% csrf_token %}
                                <button type="submit">❤️</button>
                        </form>
                        <form method="post" action="{% url "articles:articlelike" article.id "dislike" %}">
                            {% csrf_token %}
                            <button type="submit">🤨</button>
                        </form>
                    </div>
                    <div class="card-body">
                        <p class="card-content">User ID : {{ article.user_id }}
                            Job ID : {{ article.job_id}}</p>
                    </div>
                </div>
            </div>
        {% endfor %}
    </div>
</div>

 

<main.html>

더보기
{% extends "base.html" %}

{% block content %}

<div class="wrapper">
    <div class="container-fluid">
        <div class="row">
            <!--Left button-->
            <div class="col-md-3">
                <button class="btn-custom w-100" onclick="location.href='/workspace/create/'">
                    Create to text ✏️ <i class="fas fa-file"></i>
                </button>
                
                <button class="btn-custom w-100">Create to image 📷<i class="fas fa-image"></i></button>
            </div>
    
            <!-- Image Gallery (Right) -->
            <div class="col-md-9">
                <div class="row">
                    {% for article in article_list %}
                    <div class="col-md-4">
                        <div class="card">
                            <div class="card-img-top">
                                <i class="fas fa-image fa-5x" style="color: lightgray;"></i>
                            </div>
                            <div>
                                <form method="post" action="{% url "articles:articlelike" article.id "like" %}">
                                    {% csrf_token %}
                                    <button type="submit">❤️</button>
                                </form>
                                
                                <form method="post" action="{% url "articles:articlelike" article.id "dislike" %}">
                                    {% csrf_token %}
                                    <button type="submit">🤨</button>
                                </form>
                            </div>
                            <div class="card-body">
                                <p class="card-content">User ID : {{ article.user_id }}
                                    Job ID : {{ article.job_id}}
                                </p>
                            </div>
                        </div>
                    </div>
                    {% endfor %}
                </div>
            </div>
        </div>
    </div>
</div>

<style>
    .container {
        margin-top: 20px; 
    }
    .card {
        margin-top: 15px;
        margin-bottom: 20px;
        padding: 10px;
    }
    .card-img-top {
        background-color: #f0f0f0; /* Placeholder gray color */
        height: 200px;
        display: flex;
        justify-content: center;
        align-items: center;
        margin-bottom: 10px;
    }
    .card-title {
        font-size: 16px;
        text-align: left;
    }
    /* 왼쪽 버튼 설정*/
    .btn-custom {
        background-color: white;
        color: black;
        border: 1px solid #ced4da;
        border-radius: 5px;
        margin-top: 15px;
        margin-bottom: 0px;
        padding: 8px 12px;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }

    .btn-custom:hover {
        background-color: #e9ecef; /* Light gray on hover */
    }

    .btn-custom i {
        margin-left: 5px; /* Space between text and icon */
    }
    .placeholder-image {
        width: 100%;
        height: 200px; /* Adjust as needed */
        background-color: #f0f0f0;
        border: 1px solid #ddd;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #aaa;
        margin-bottom: 10px; /* Added spacing between images */
    }

    .placeholder-image svg {
        width: 50%; /* Adjust size as needed */
        height: 50%;
    }

</style>
{% endblock %}

 

🐾Recent posts