ManyToManyField in Django

Kay·2020년 3월 1일
0

하나의 상품이 여러 개의 카테고리에 속할 수 있다면, 아래와 같이 상품 테이블과 카테고리 테이블 이외에 이 둘의 관계를 저장할 중간 테이블이 필요하다.

class Category(models.Model):
    name             = models.CharField(max_length = 100)
   
    class Meta:
        db_table = 'categories'

class Product(models.Model):
    name        = models.CharField(max_length = 100)
    price       = models.DecimalField(max_digits = 10, decimal_places = 2, null=True)
    category    = models.ManyToManyField(Category, through = 'ProductCategory')
    created_at  = models.DateTimeField(auto_now_add = True)
    updated_at  = models.DateTimeField(auto_now = True)

    class Meta:
        db_table = 'products'

class ProductCategory(models.Model):
    product  = models.ForeignKey(Product, on_delete = models.CASCADE, null=True)
    category = models.ForeignKey(Category, on_delete = models.CASCADE, null=True)
    
    class Meta:
    db_table = 'products_categories'

✔️ 굳이 models.py에 중간테이블을 만들고 through로 지정하지 않아도 many to many 관계의 경우 'products_categories'와 같이 첫번째 두번째 테이블 이름을 따서 데이터 베이스에 자동으로 중간 테이블이 생성되기는 한다.

models.py 작성한 후, makemigratios 와 migrate를 완료하면 DB에 빈 테이블이 생성된다.

python manage.py shell 명령어로 인터렉티브 쉘에서 직접 데이터를 입력할 수도 있지만 입력해야하는 데이터의 양이 많은 경우 csv파일을 이용해서 데이터를 bulk로 업로드하고, foreign key도 한 번에 지정해줄 수 있다.

참고 :
https://velog.io/@swhybein/Django-bulkcreate%EC%9C%BC%EB%A1%9C-csv%ED%8C%8C%EC%9D%BC-%EC%98%AC%EB%A6%AC%EA%B8%B0

https://velog.io/@swhybein/django-ForeignKey%ED%95%84%EB%93%9C-csv%ED%8C%8C%EC%9D%BC%EB%A1%9C-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0

데이터의 입력까지 마쳤으면 이제 내가 원하는 데이터를 어떤 방식으로 가져와서 뿌려줄지를 고민해야할 단계. 바로 views.py에서 로직을 작성하고 url을 할당해서 httpie로 테스트 해볼 수도 있지만, 먼저 python manage.py shell에서 간편하게 테스트 해볼 수도 있다.

아래와 같이, Product.objects.values()의 괄호 안에 원하는 값을 지정해서 가져올 수 도 있다. 여기에서 바로 'category'를 지정해주면 product 테이블에 없는 칼럼이라는 오류가 뜨지만, __(언더스코어 두 개)를 이용하면 many to many 관계로 연결되어있는 카테고리 테이블에 있는 정보룰 가져올 수 있다.

a = Product.objects.values('name','category__name') #프로덕트 테이블의 네임과, 카테고리 테이블의 네임을 가져와라!
print(a)

<QuerySet [{'name': '[오늘회] 떠먹는 성게알', 'category__name': '성게알'}, {'name': '[문경] 송어회 세트', 'category__name': '제철회'}, {'name': '썰어먹는 자숙 통문어', 'category__name': '오징어•조개•해산물'}, {'name': '[나래수산] 활광돔연회', 'category__name': '수산시장 회'}, {'name': '순 노란 날치알', 'category__name': '소스·곁들임'}, {'name': '순 노란 날치알', 'category__name': '수산가공품'}, {'name': '순 노란 날치알', 'category__name': '밥도둑 반찬'}, {'name': '순살고등어', 'category__name': '수산가공품'}, {'name': '순살고등어', 'category__name': '밥도둑 반찬'}, {'name': '순살삼치', 'category__name': '수산가공품'}, {'name': '순살삼치', 'category__name': '밥도둑 반찬'}, {'name': '순살가자미', 'category__name': '수산가공품'}, {'name': '순살가자미', 'category__name': '밥도둑 반찬'}, {'name': '광동 夜시장 당면개조개', 'category__name': '오징어•조개•해산물'}, {'name': '광동 夜시장 당면개조개', 'category__name': '수산가공품'}, {'name': '한끼 오징어 젓갈', 'category__name': '오징어•조개•해산물'}, {'name': '한끼 오징어 젓갈', 'category__name': '수산가공품'}, {'name': '한끼 오징어 젓갈', 'category__name': '밥도둑 반찬'}, {'name': '한끼 가리비 젓갈', 'category__name': '오징어•조개•해산물'}, {'name': '한끼 가리비 젓갈', 'category__name': '수산가공품'}, '...(remaining elements truncated)...']>

values() 앞에 filter를 이용해서 원하는 값들을 필터링해서 가져올 수도 있고, get을 이용해서 하나만 가져올 수도 있다. 그리고, 아래와 같이 order_by를 이용하면 원하는 순서대로 정렬 해서 가져올 수도 있다. (최고....👍👍)

Product.objects.order_by('price').values() #가격 오름차순
Product.objects.order_by(-'price').values() #가격 내림차순
Product.objects.order_by(-'updated_at').values() #최신순

order_by() 안에 -를 붙이면 descending, 안 붙이면 ascenging으로 정렬되니 이를 활용하면 다양한 칼럼을 기준으로 정렬할 수 있다. 🤩

profile
new blog✨ https://kay-log.tistory.com/

1개의 댓글

comment-user-thumbnail
2022년 8월 17일

감사합니다

답글 달기