django의 ORM을 사용하면서 __
를 접하게 되었다.
정리를 하지 않고 계속 마주치니 __
가 어떤 기능을 하는 것인지 매우 헷갈렸다. 나와 같은 상황인 개발자들이 많을 것이라 생각되어, 한 번 제대로 정리하고 넘어가고자 한다.
django ORM의 장점 중 하나는 서로 다른 테이블을 쉽게 넘어다닐 수 있다는 것이다.
테이블을 쉽게 넘어다닐 수 있게 하는 수단이 바로 __
이다.
아래 예시로 이해해보자.
아래는 models.py에 있는 class들이다.
class Category(models.Model):
name = models.CharField(max_length=100)
class Meta:
db_table = 'categories'
class SubCategory(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey('Category', on_delete=models.CASCADE)
class Meta:
db_table = 'sub_categories'
class Product(models.Model):
name = models.CharField(max_length=100)
stock = models.IntegerField()
sub_category = mfodels.ForeignKey('SubCategory', on_delete=models.CASCADE)
class Meta:
db_table = 'products'
위의 코드를 보면 Product가 SubCategory를 참조하고, SubCategory가 Category를 참조하는 것을 볼 수 있다.
하지만 django의 ORM에선, 참조와 역참조는 중요하지 않다. 중요한 것은 두 테이블이 서로 연결되어 있다는 것이다. 두 테이블 중 하나의 column이 참조를 위한 column이라면 두 테이블은 서로 연결되어, __
를 이용해 서로의 테이블을 호출을 할 수 있다.
category의 id가 4인 product를 알고싶다면, __
를 어떻게 사용하면 될까?
Product 테이블은 SubCategory와 연결되어 있고, SubCategory 테이블은 Category테이블과 연결되어 있다.
고로, 아래의 코드처럼 Product 테이블은 SubCategory 테이블을 __
를 이용하여 호출할 수 있고, SubCategory 테이블은 Category 테이블을 __
를 이용하여 호출할 수 있다.
>>> Product.objects.filter(subcategory__category__id = 4)
<QuerySet [<Product: Product object (10)>, <Product: Product object (15)>,
<Product: Product object (16)>, <Product: Product object (17)>,
<Product: Product object (19)>]>
어떤 model에서 자신을 foreign key로 가지고 있는 모델에 접근하기위해선
_set
을 붙여주면 된다.b = Category.objects.filter(id=1) b.subcategory_set.all()
조건을 통해 데이터를 조회할 때 또한, __
가 사용된다.
필드명__조건 = 조건값
을 filter 의 인자로 넘겨주어 조건에 부합되는 데이터를 return 받아 조회할 수 있다.
조건들과 함께 예시로 이해해보자.
필드명__lt = 조건값 : 필드명 < 조건값
Product.objects.filter(is_published__lt = date(1961,1,1))
필드명__lte = 조건값 : 필드명 <= 조건값
필드명__gt = 조건값 : 필드명 < 조건값
필드명__gte = 조건값 : 필드명 < 조건값
예시는 전체적으로 비슷하다고 생각되어 첫번째 에서만 기재하였다.
각 알파벳은 의미하는 함축어가 존재하는데, l 은 less 를 g는 greater 를t 는 than 을 e 는 equal 을 의미한다.
# 문자열 필드
필드명__startswith
Post.objects.filter(title__startswich="Django")
필드명__startswith = "Django"
을 통해 'Django'로 시작하는 객체를 모두 가져와 반환한다.
필드명__endswith
필드명__endswith = 조건값
을 통해 조건값으로 끝나는 데이터를 모두 가져온다.
필드명__contains
필드명__contains = 조건값
을 통해 조건값이 포함되는 데이터를 모두 가져온다.
필드명__istartswith
필드명__istartswith = 조건값
, startswith 와 동일하지만 대소문자를 구분하지 않는다.
필드명__iendswith
필드명__iendswith = 조건값
, endswith 와 동일하지만 대소문자를 구분하지 않는다.
필드명__icontains
필드명__icontains = 조건값
, contains 와 동일하지만 대소문자를 구분하지 않는다.
필드명__in = iterable 객체
Product.objects.filter(id__in=[1, 3, 4])
iterable 객체에 해당 필드가 존재하는 데이터를 모두 반환한다.
즉, Product의 id가 1 또는 3 또는 4 인 객체들을 리턴한다.
category의 id가 3 또는 4인 Product 객체들을 모두 반환하기 위해선 어떻게 해야할까?
category에 접근하기 위해서 __
를 이용하여 Product에서 SubCategory에 접근하고, SubCategory에서 Category에 접근하고, Category에서 id를 접근한다.
그리고 그 id가 3 또는 4 여야한다는 조건이 존재하기에 __in
을 이용하여 조건을 붙여준다. 그렇게 작성한 코드가 바로 아래 코드이다.
>>> Product.objects.filter(sub_category__category_id__in= [3,4])
<QuerySet [<Product: Product object (10)>,
<Product: Product object (15)>, <Product: Product object (16)>]>