Django에서는 DB를 SQL 문으로 직접 조작하지 않습니다. Django ORM(Object Relational Mapping)을 사용하죠.
(물론 하는 경우도 있습니다. 제가 지금 하는 업무에서는 ORM으로 하기 보다는 SQL Developer로 직접 조작을 하죠, 하지만 이 경우에도 사용자 딴에서 Data를 보여줄 땐 Django ORM이 필수적으로 사용된답니다)
예를 들어서 obj.amenities.count() / obj.amenities.all() 이런 것들이 Django ORM Query가 되겠죠.
콘솔로 들어가서 확인을 해볼까요?
User라는 클래스를 만들었다고 가정합시다.
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser): # AbstractUser를 상속하면, User id, admin 여부 등 다양한 것을 사용할 수 있다.
""" Custom User Model """
...
...
여기서 우리가 주목해야하는 부분은 AbstractUser에서 상속을 받았다는 겁니다. 이 점을 기억하고 아래를 보도록 하겠습니다.
>>> from users.models import User
>>> User
<class 'users.models.User'>
User가 클래스인 것을 확인했죠? 그러면 여기서 사용가능한 메소드, 인자가 무엇인지 봅시다.
>>> dir(User)
['CURRENCY_CHOICES', 'CURRENCY_KRW', 'CURRENCY_USD', 'DoesNotExist', 'EMAIL_FIELD', 'GENDER_CHOICES', 'GENDER_FEMALE', 'GENDER_MALE', 'GENDER_OTHER', 'LANGUAGE_CHOICES', 'LANGUAGE_ENGLISH', 'LANGUAGE_KOREAN', 'Meta', 'MultipleObjectsReturned', 'REQUIRED_FIELDS', 'USERNAME_FIELD', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_column_name_clashes', '_check_constraints', '_check_field_name_clashes', '_check_fields', '_check_id_field', '_check_index_together', '_check_indexes', '_check_local_fields', '_check_long_column_names', '_check_m2m_through_same_relationship', '_check_managers', '_check_model', '_check_model_name_db_lookup_clashes', '_check_ordering', '_check_property_name_related_field_accessor_clashes', '_check_single_primary_key', '_check_swappable', '_check_unique_together', '_do_insert', '_do_update', '_get_FIELD_display', '_get_next_or_previous_by_FIELD', '_get_next_or_previous_in_order', '_get_pk_val',
'_get_unique_checks', '_meta', '_password', '_perform_date_checks', '_perform_unique_checks', '_save_parents', '_save_table', '_set_pk_val', 'avatar', 'bio',
'birthdate', 'check', 'check_password', 'clean', 'clean_fields', 'conversation_set', 'currency', 'date_error_message', 'date_joined', 'delete', 'email', 'email_user', 'first_name', 'from_db', 'full_clean', 'gender', 'get_all_permissions', 'get_currency_display', 'get_deferred_fields', 'get_email_field_name', 'get_full_name', 'get_gender_display', 'get_group_permissions', 'get_language_display', 'get_next_by_date_joined', 'get_previous_by_date_joined', 'get_session_auth_hash', 'get_short_name', 'get_username', 'groups', 'has_module_perms', 'has_perm', 'has_perms', 'has_usable_password', 'id', 'is_active', 'is_anonymous', 'is_authenticated', 'is_staff', 'is_superuser', 'language', 'last_login', 'last_name', 'list_set', 'logentry_set', 'message_set', 'natural_key', 'normalize_username', 'objects', 'password', 'pk', 'prepare_database_save', 'refresh_from_db', 'reservation_set', 'review_set', 'room_set', 'save', 'save_base', 'serializable_value', 'set_password', 'set_unusable_password', 'superhost', 'unique_error_message', 'user_permissions', 'username', 'username_validator', 'validate_unique']
>>> vars(User)
mappingproxy({'__module__': 'users.models', '__doc__': ' Custom User Model ', 'GENDER_MALE': 'male', 'GENDER_FEMALE': 'female', 'GENDER_OTHER': 'other', 'GENDER_CHOICES': (('male', 'Male'), ('female', 'Female'), ('other', 'Other')), 'LANGUAGE_ENGLISH': 'en', 'LANGUAGE_KOREAN': 'kr', 'LANGUAGE_CHOICES': (('en', 'English'), ('kr', 'Korean')), 'CURRENCY_USD': 'usd', 'CURRENCY_KRW': 'krw', 'CURRENCY_CHOICES': (('usd', 'USD'), ('krw', 'KRW')), '_meta': <Options for User>, 'DoesNotExist': <class 'users.models.User.DoesNotExist'>, 'MultipleObjectsReturned': <class 'users.models.User.MultipleObjectsReturned'>, 'avatar': <django.db.models.fields.files.ImageFileDescriptor object at 0x042FCFB8>, 'gender': <django.db.models.query_utils.DeferredAttribute object at 0x042FCFE8>, 'get_gender_display': functools.partialmethod(<function Model._get_FIELD_display at 0x03FDDE80>, , field=<django.db.models.fields.CharField: gender>), 'bio': <django.db.models.query_utils.DeferredAttribute object at 0x042FCE98>, 'birthdate': <django.db.models.query_utils.DeferredAttribute object at 0x042FCDC0>, 'language': <django.db.models.query_utils.DeferredAttribute object at 0x04306B50>, 'get_language_display': functools.partialmethod(<function Model._get_FIELD_display at 0x03FDDE80>, , field=<django.db.models.fields.CharField: language>), 'currency': <django.db.models.query_utils.DeferredAttribute object at 0x04306BB0>, 'get_currency_display': functools.partialmethod(<function Model._get_FIELD_display at 0x03FDDE80>, , field=<django.db.models.fields.CharField: currency>), 'superhost': <django.db.models.query_utils.DeferredAttribute object at 0x04306C10>, 'get_next_by_date_joined': functools.partialmethod(<function Model._get_next_or_previous_by_FIELD at 0x03FDDEC8>, , field=<django.db.models.fields.DateTimeField: date_joined>, is_next=True), 'get_previous_by_date_joined': functools.partialmethod(<function Model._get_next_or_previous_by_FIELD at 0x03FDDEC8>, , field=<django.db.models.fields.DateTimeField: date_joined>, is_next=False), 'groups': <django.db.models.fields.related_descriptors.ManyToManyDescriptor object at 0x043100E8>, 'user_permissions': <django.db.models.fields.related_descriptors.ManyToManyDescriptor object at 0x04310598>, 'id': <django.db.models.query_utils.DeferredAttribute object at 0x04310718>, 'logentry_set': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x043107F0>, 'room_set': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x045D67A8>, 'review_set': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x045DD2C8>, 'reservation_set': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x045DDA30>, 'list_set': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x045DDC28>, 'conversation_set': <django.db.models.fields.related_descriptors.ManyToManyDescriptor object at 0x045E8610>, 'message_set': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x045E8B80>})
vars는 __dict__, dictionary 또는 클래스 리스트안의 나타내는 것을 리턴합니다. dir는 클래스 안의 names 리스트들을 리턴합니다.
뭐가 엄청 많죠? 저는 이 것을 생성하지 않았지만, AbstractUser, AbstractBasicUser를 상속받았기에 사용할 수 있습니다. 그 중 하나의 기능에 Manager가 있습니다.
한 번 확인해봅시다.
>>> User.objects
<django.contrib.auth.models.UserManager object at 0x04310790>
UserManager가 있죠? manager는 데이터베이스로부터 elements를 들고올 수 있게 해주는 애입니다. SQL을 안 쓰고 Python으로 Data를 가져올 수 있는 이유입니다.
만약 User 모두를 가지고 오고 싶다면, 이 Manager한테 말하면 됩니다. 마치 API 사용하는 거 같네요.
>>> User.objects.all()
<QuerySet [<User: ddong.han>]>
manager한테 User의 모두를 가져오라고 했습니다. 그런데 QuerySet을 리턴 받았습니다. QuerySet이 뭘까요? Object 리스트입니다. 단순한 리스트가 아니고 DataBase로부터 온 장고 Ojbects 리스트입니다. 매우 똑똑한 리스트죠.
그래서 이런 것도 가능합니다.
>>> all_user = User.objects.all()
>>> all_user.filter(superhost=False)
<QuerySet [<User: ddong.han>]>
확인 했듯이 리스트는 다양한 기능을 가지고 있습니다.
>>> dir(all_user)
['__and__', '__bool__', '__class__', '__deepcopy__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__or__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_add_hints', '_batched_insert', '_chain', '_clone', '_combinator_query', '_create_object_from_params', '_db', '_earliest', '_extract_model_params', '_fetch_all', '_fields', '_filter_or_exclude', '_for_write', '_has_filters', '_hints', '_insert',
'_iterable_class', '_iterator', '_known_related_objects', '_merge_known_related_objects', '_merge_sanity_check', '_next_is_sticky', '_populate_pk_values', '_prefetch_done', '_prefetch_related_lookups', '_prefetch_related_objects', '_raw_delete', '_result_cache', '_sticky_filter', '_update', '_validate_values_are_expressions', '_values', 'aggregate', 'all', 'annotate', 'as_manager', 'bulk_create', 'bulk_update', 'complex_filter', 'count', 'create', 'dates', 'datetimes',
'db', 'defer', 'delete', 'difference', 'distinct', 'earliest', 'exclude', 'exists', 'explain', 'extra', 'filter', 'first', 'get', 'get_or_create', 'in_bulk',
'intersection', 'iterator', 'last', 'latest', 'model', 'none', 'only', 'order_by', 'ordered', 'prefetch_related', 'query', 'raw', 'resolve_expression', 'reverse', 'select_for_update', 'select_related', 'union', 'update', 'update_or_create', 'using', 'values', 'values_list']
그쵸?
# 원하는 정보를 가지고 오고 싶을 때
>>> ddong_han = User.objects.get(username="ddong.han")
>>> print(ddong_han)
ddong.han
# 만약 없는거 가져온다면?
>>> User.objects.get(username="gg")
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Users\HAN\.virtualenvs\airbnb-clone-azKDMdi5\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\HAN\.virtualenvs\airbnb-clone-azKDMdi5\lib\site-packages\django\db\models\query.py", line 406, in get
raise self.model.DoesNotExist(
users.models.User.DoesNotExist: User matching query does not exist.
보다 자세한 내용은 Django Documentation QuerySet 파트에서 볼 수 있습니다.
Django의 대단한 점은 user에 연결된 foreign key set도 다 가져온다는 점입니다.
>>> dir(ddong_han)
['CURRENCY_CHOICES', 'CURRENCY_KRW', 'CURRENCY_USD', 'DoesNotExist', 'EMAIL_FIELD', 'GENDER_CHOICES', 'GENDER_FEMALE', 'GENDER_MALE', 'GENDER_OTHER', 'LANGUAGE_CHOICES', 'LANGUAGE_ENGLISH', 'LANGUAGE_KOREAN', 'Meta', 'MultipleObjectsReturned', 'REQUIRED_FIELDS', 'USERNAME_FIELD', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_column_name_clashes', '_check_constraints', '_check_field_name_clashes', '_check_fields', '_check_id_field', '_check_index_together', '_check_indexes', '_check_local_fields', '_check_long_column_names', '_check_m2m_through_same_relationship', '_check_managers', '_check_model', '_check_model_name_db_lookup_clashes', '_check_ordering', '_check_property_name_related_field_accessor_clashes', '_check_single_primary_key', '_check_swappable', '_check_unique_together', '_do_insert', '_do_update', '_get_FIELD_display', '_get_next_or_previous_by_FIELD', '_get_next_or_previous_in_order', '_get_pk_val',
'_get_unique_checks', '_meta', '_password', '_perform_date_checks', '_perform_unique_checks', '_save_parents', '_save_table', '_set_pk_val', '_state', 'avatar', 'bio', 'birthdate', 'check', 'check_password', 'clean', 'clean_fields', 'conversation_set', 'currency', 'date_error_message', 'date_joined', 'delete', 'email', 'email_user', 'first_name', 'from_db', 'full_clean', 'gender', 'get_all_permissions', 'get_currency_display', 'get_deferred_fields', 'get_email_field_name', 'get_full_name', 'get_gender_display', 'get_group_permissions', 'get_language_display', 'get_next_by_date_joined', 'get_previous_by_date_joined', 'get_session_auth_hash', 'get_short_name', 'get_username', 'groups', 'has_module_perms', 'has_perm', 'has_perms', 'has_usable_password', 'id', 'is_active', 'is_anonymous', 'is_authenticated', 'is_staff', 'is_superuser', 'language', 'last_login', 'last_name', 'list_set', 'logentry_set', 'message_set', 'natural_key', 'normalize_username', 'objects', 'password', 'pk', 'prepare_database_save', 'refresh_from_db', 'reservation_set', 'review_set', 'room_set', 'save', 'save_base', 'serializable_value', 'set_password', 'set_unusable_password', 'superhost', 'unique_error_message', 'user_permissions', 'username', 'username_validator', 'validate_unique']
dir 배열 안을 보면 'room_set', 'message_set', 'conversation_set', 'list_set' 이런 것들을 찾을 수 있습니다. 즉, user를 통해서 room 데이터에 접근할 수 있다는 점이 Django의 대단한 점입니다.
'room_set'으로 접근하기 싫을 땐 related_name을 포함해서 원하는 이름으로 바꿀 수 있습니다.
>>> ddong_han.room_set
<django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager object at 0x04623088>
>>> ddong_han.room_set.all()
<QuerySet [<Room: Ddong.han's House>]>
즉, _set은 ddong_han 같은 element가 그들의 foreign keys에 접근할 수 있는 방법입니다. User model엔 set을 만들지 않았음에도 말이죠 !
이걸 왜 쓸까요? 인스타그램을 생각해봅시다. 글(사진)을 model로 삼고 user를 model로 삼을게 분명한데, 사용자 프로필로가서, 사용자에 의해 생성된 모든 글(사진)을 가져오게끔 할 때 위와 같은 방법을 쓸 수 있겠죠 !
이상 Managers와 QuerySets의 이해였습니다. ^_^
'Web + APP > Django' 카테고리의 다른 글
Get, Post 요청을 모두 알아서 처리하는 FormView ! (4) | 2020.05.30 |
---|---|
Class Based View (0) | 2020.05.10 |
추상 모델 (0) | 2020.04.29 |
커스텀 유저 모델 (0) | 2020.04.29 |
Django Setting 하기 ! (0) | 2020.04.18 |