Django를 공부하면서 여러 메소드 들을 알게 되었다. 기본적이지만 자주 사용되는 메소드에는 대표적으로 다음과 같은 것들이 있다.
- all() / values()
- get() / filter()
- create() / save()
비슷해 보이는 각 요소별로 차이점에 대해서 알아보자!
주 참고 정보 reference : Django 공식문서 https://docs.djangoproject.com/en/3.1/ref/models/querysets
영어문서 익숙해...ㅠ 전공을 바꿔도 벗어날수 없어
공통적인 것은 둘다 QuerySet의 형태로 출력된다는 것과, values()에 parameter가 입력되지 않을 경우 all()과 같이 모든 값을 출력한다는 것이다.
all()은 현재 Queryset의 copy를 return
한다고 한다.
Queryset
형식 즉, list와 같은 모양새로 QuerySet에는 각각의 Instance들이 들어있어 출력하면 다음과 같이 나온다.
>>> from products.models import Category
>>> Category.objects.all()
<QuerySet [<Category: Category object (1)>, <Category: Category object (2)>, <Category: Category object (3)>, <Category: Category object (4)>, <Category: Category object (5)>, <Category: Category object (6)>, <Category: Category object (7)>]>
__str__() 메소드 추가예시
>>> from products.models import Menu
>>> Menu.objects.all()
<QuerySet [<Menu: 음료>, <Menu: 푸드>, <Menu: 상품>, <Menu: 카드>]>
values의 입력형태는 values(*fields, **expressions)
이다.
이 메소드는 all()과 마찬가지로 QuerySet
을 반환하는데 all()과 달리 dictionary
형태로 변환한다. dictionary를 포함한 QuerySet
의 형태
Returns a QuerySet that returns dictionaries, rather than model instances, when used as an iterable.
글로썬 이해가 제대로 되지 않지만 출력 형태를 보면 all()과의 차이점을 볼 수 있다.
>>> from products.models import Drink
>>> Drink.objects.values()
<QuerySet [{'id': 1, 'name': '아인슈페너', 'name_en': 'Einspanner', 'category_id': 1}, {'id': 2, 'name': '아메리카노', 'name_en': 'Americano', 'category_id': 1}, {'id': 3, 'name': '딸기라떼', 'name_en': 'Strawberry latte', 'category_id': 2}, {'id': 4, 'name': '달고나 라떼', 'name_en': 'Dalgona Latte', 'category_id': 2}, {'id': 5, 'name': '석류애플라임', 'name_en': 'Pomegranate Apple Lime', 'category_id': 3}]>
>>> Drink.objects.values('id', 'name_en')
<QuerySet [{'id': 1, 'name_en': 'Einspanner'}, {'id': 2, 'name_en': 'Americano'}, {'id': 3, 'name_en': 'Strawberry latte'}, {'id': 4, 'name_en': 'Dalgona Latte'}, {'id': 5, 'name_en': 'Pomegranate Apple Lime'}]>
values()에 아무것도 입력하지 않는다면 마치 all() 처럼 모든 데이터를 출력
하되, 그 형태가 dictionary
일 것이고, values()안에 *fields 와 특정 **expression 을 지정하여 출력
할 수 있다.
//1번
>>> Drink.objects.filter(category_id=1)
<QuerySet [<Drink: Drink object (1)>, <Drink: Drink object (2)>]>
//2번
>>> Drink.objects.filter(category_id=1).values()
<QuerySet [{'id': 1, 'name': '아인슈페너', 'name_en': 'Einspanner', 'category_id': 1}, {'id': 2, 'name': '아메리카노', 'name_en': 'Americano', 'category_id': 1}]>
//3번
>>> Drink.objects.filter(category_id=1).values('id', 'name_en')
<QuerySet [{'id': 1, 'name_en': 'Einspanner'}, {'id': 2, 'name_en': 'Americano'}]>
get()과 filter()는 모두 Django 에서 database 에 들어있는 data를 READ할때 사용되는 메소드 들이다. 즉, client에서 get()과 filter()를 통해 요청
하면 과정을 거쳐 부합하는 data를 출력시키게 한다.
get() 메소드는 조건에 맞는 단 하나의 데이터
를 출력한다. 이때의 형태는 QuerySet이 아닌 단순히 object값 만을 출력한다.
>>> Drink.objects.get(id=1)
<Drink: Drink object (1)>
변수에 출력될 객체를 저장하여 변수.attribute
사용한다.
>>> drink=Drink.objects.get(id=1)
>>> drink.name
'아인슈페너'
>>> drink.name_en
'Einspanner'
>>> drink.category_id
1
>>> drink.category_id.name
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'int' object has no attribute 'name'
FK로 받는 category_id의 type이 int이기 때문에 이런식은 안된다
>>> drink.category.name
'에스프레소'
뒤에 _id
는 쓰지 않는다! FK의 FK는 안됨 (직접 연결된 FK만)
filter()는 말 그대로 걸러서 출력하게 한다.
때문에 불특정 다수의 갯수의 값을 출력시킬수 있어, 그 형태는 list와 같은 QuerySet
의 형태이다.
>>> Drink.objects.filter(id=1)
<QuerySet [<Drink: Drink object (1)>]>
>>> Drink.objects.filter(category_id=3)
<QuerySet [<Drink: Drink object (5)>, <Drink: Drink object (6)>]>
값이 1개이든지, 2개이상이든지 몇개와 상관없이 항상 QuerySet의 형태로 출력한다.
list에서는 뒤에서 앞으로 갯수를 셀때 -1부터 시작하지만, QuerySet은 -1번째 라는 개념을 인식하지 못한다.
def __str__(self):
return self.name #여기서 name 부분은 attribute
>>> Menu.objects.filter(id=1)
<QuerySet [<Menu: 음료>]>
두 메소드 모두 새롭게 값을 insert한다는 의미는 같다.
명령어의 형태는 다음과 같다. create(**kwargs)
create는 새로운 object를 만듬과 동시에 저장
하는 메소드 이다.
즉, table에 새로운 데이터를 추가(insert
)하는 메소드로, 생성된 instance를 반환해준다.
class명.objects.create(column명='넣을값')
Django 연습중 반복되는 형태로 data를 추가하면서 실행해 보았다.
어차피 Product 라는 class에는 name / category_id / name_en 이라는 attribute가 고정되어 있으니 create 명령어를 입력할때 굳이 key값을 안쓰고, value만 순서에 맞게 입력해도 되지 않을까? 라고 생각했다.
결론은 안된다. 에러난다.
>> Drink.objects.create('레몬에이드', 3, 'Lemon Ade') Traceback (most recent call last): File "<console>", line 1, in <module> File "/Users/생략/django/db/models/manager.py", line 85, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) TypeError: create() takes 1 positional argument but 4 were given
애초에 공식 document에서도 create의 parameter로 **kwargs의 형태를 받는다고 했던 것이 괜히 있는게 아니었다.
create 할때 attribute중 null 의 기본값이 No로 되어있음을 확인했는데 create 할때 값을 넣지 않아도 그냥 입력 된다...
<Drink: Drink object (16)> >> Drink.objects.create(category_id=1, name_en='water') <Drink: Drink object (17)>
흠,, 이건 알아봐야겠다!
save()는 insert
혹은 Update
시에 사용되는 메소드이다. 주로 단일 객체의 update 시에 많이 사용된다.
create() 메소드를 이용할땐 다음과 같다.
Drink.objects.create(name='물', category_id=1, name_en='water')
하지만이걸 save()를 통해 같게 기능하게 하려면 다음과 같이 하면 된다.
p = Drink(name='물', category_id=1, name_en='water')
p.save(force_insert=True)
아까 null 이 지정되지 않았을때 값을 입력하지 않으면 저장이 될까? 를 시도하다가 그냥 저장되버린 17번 id로 예시를 들어보았다.
>>> d = Drink.objects.get(id=17)
>>> d.name
''
>>> d.name='비타민워터'
>>> d.save()
>>> d.name
'비타민워터'
위같은 예시에서 database상에는 어떤 변화가 있는지 알아보았다.
>>> a = Drink.objects.get(id=16)
>>> a.name
''
>>> a.name='수돗물'
새로 '수돗물' 이라고 선언했을 때 까지 table 의 상태는 변함이 없다.
a.save()
save 를 한 순간 database에 적용된다.