Django ManyToManyField
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()를 사용하면 중복된 데이터를 방지하면서 관계를 생성할 수 있어 유용함