장고

Django ManyToManyField

content0474 2024. 12. 17. 14:43

Django에서 ManyToManyField는 두 모델 간의 다대다(Many-to-Many) 관계를 설정할 때 사용
ex) 게시글-해시태그

하나의 게시글이 여러 해시태그를 가질 수 있고, 하나의 해시태그도 여러 게시글에 연결될수 있음

1. ManyToManyField 정의 방법
두 모델 사이에 ManyToManyField를 정의하면 Django가 자동으로 중계 테이블을 만들어 관계를 관리함

예시: Product와 Hashtag 모델

from django.db import models
from django.conf import settings

class Hashtag(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def __str__(self):
        return self.name

class Product(models.Model):
    title = models.CharField(max_length=50)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="products")
    hashtags = models.ManyToManyField(Hashtag, related_name="products")

    def __str__(self):
        return self.title


ManyToManyField(Hashtag): Product 모델과 Hashtag 모델 간의 다대다 관계를 설정함
related_name="products": 역참조를 위해 사용되며, 해시태그에서 연결된 게시글을 가져올 때 사용함

 

2. ManyToManyField 입력 및 저장
뷰 함수에서 ManyToManyField를 처리하려면 입력된 값을 나누고 해당 객체와 연결하는 로직이 필요함

예시: Product 작성 및 해시태그 추가

from django.shortcuts import render, redirect, get_object_or_404
from .models import Product, Hashtag
from .forms import ProductForm
from django.contrib.auth.decorators import login_required

@login_required
def new(request):
    if request.method == "POST":
        form = ProductForm(request.POST, request.FILES)
        if form.is_valid():
            product = form.save(commit=False)
            product.author = request.user
            product.save()

            # 쉼표로 구분된 해시태그 입력 처리
            hashtags_input = forhttp://m.cleaned_data.get('hashtags', '')
            if hashtags_input:
                hashtag_names = [tag.strip() for tag in hashtags_input.split(',') if tag.strip()]
                for tag_name in hashtag_names:
                    hashtag, _ = Hashtag.objects.get_or_create(name=tag_name)
                    product.hashtags.add(hashtag)

            return redirect("products:product_detail", product.pk)
    else:
        form = ProductForm()
    return render(request, "products/new.html", {"form": form})

 


3. 템플릿에서 ManyToManyField 출력
ManyToManyField에 연결된 객체들을 출력할 때는 all() 메서드를 사용함

예시: product_detail.html

<h3>해시태그:</h3>
<ul>
    {% for hashtag in product.hashtags.all %}
        <li>#{{ hashtag.name }}</li>
    {% empty %}
        <li>해시태그가 없습니다</li>
    {% endfor %}
</ul>


product.hashtags.all: 현재 게시글에 연결된 모든 해시태그를 가져옴
empty: 해시태그가 없을 경우 출력되는 내용

 

4. ManyToManyField 활용 
자동 중계 테이블 생성: Django가 다대다 관계를 위해 중간 테이블을 자동으로 생성해 관리

중복 방지: get_or_create()를 사용하면 중복된 객체 생성을 방지하고 기존 객체를 가져올 수 있믕

객체 추가 및 삭제: add()와 remove() 메서드를 사용해 관계를 동적으로 수정할 수 있음

 


5. 실전 예시: Product 수정 시 해시태그 업데이트

def edit(request, pk):
    product = get_object_or_404(Product, pk=pk)
    if request.method == "POST":
        form = ProductForm(request.POST, request.FILES, instance=product)
        if form.is_valid():
            product = form.save(commit=False)
            product.save()

            # 기존 해시태그 삭제 후 새로 추가
            product.hashtags.clear()
            hashtags_input = forhttp://m.cleaned_data.get('hashtags', '')
            if hashtags_input:
                hashtag_names = [tag.strip() for tag in hashtags_input.split(',') if tag.strip()]
                for tag_name in hashtag_names:
                    hashtag, _ = Hashtag.objects.get_or_create(name=tag_name)
                    product.hashtags.add(hashtag)

            return redirect("products:product_detail", product.pk)
    else:
        existing_hashtags = ', '.join(product.hashtags.values_list('name', flat=True))
        form = ProductForm(instance=product, initial={"hashtags": existing_hashtags})
    return render(request, "products/edit.html", {"form": form, "product": product})



요약
Django ManyToManyField는 다대다 관계를 간편하게 설정하고 관리할 수 있는 강력한 기능
add(), remove(), clear() 메서드를 활용하면 동적으로 객체를 연결하거나 제거할 수 있음
get_or_create()를 사용하면 중복된 데이터를 방지하면서 관계를 생성할 수 있어 유용함