ORM๐Ÿน: ์žฅ๊ณ  Many-to-one relationship

์ •์€๊ฒฝยท2020๋…„ 10์›” 16์ผ
0

๐ŸŽธ Play the Djangoย 

๋ชฉ๋ก ๋ณด๊ธฐ
37/57


ํ˜•์„ฑ๋œ ๊ด€๊ณ„์— ์˜ํ•ด์„œ
๊ด€๊ณ„๋ฅผ ๋งบ์€ ํ…Œ์ด๋ธ”์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ด์ง!

์˜ˆ์‹œ

Reporter : Article = 1 : M

1. reporter์—์„œ article ์ ‘๊ทผ ok

1-1. article_set

  • "article_set"์ด ์ƒ๊น€์„ ํ™•์ธ!!!
  • "article_set"๋Š” repoter์—์„œ article๋กœ ์ ‘๊ทผํ•˜๋Š” ๋ช…๋ น์–ด!
  • reporter์™€ article์€ 1:N๊ด€๊ณ„
  • ํ•œ๋ช…์˜ reporter๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ article์„ ์“ธ ์ˆ˜ ์žˆ์ง€
  • ๊ทธ๋ž˜์„œ article์—๋‹ค๊ฐ€ repotert ์†์„ฑ์„ ๋งŒ๋“ค์–ด์„œ repoter๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก (foreign key)ํ–ˆ์–ด
  • ํ•˜๋‚˜์˜ reporter๋ฅผ ๋ฐ”๋ผ๋ณด๋Š” article์ด ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ๋  ์ˆ˜ ์žˆ์ง€
  • repoter ์ž…์žฅ์—์„œ ๋‚˜๋ฅผ ๋ฐ”๋ผ๋ณด๋Š” article๋“ค์ด ๋ˆ„๊ฐ€ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•˜์ž–์•„
  • ๊ทธ๋ž˜์„œ ์žฅ๊ณ ๋Š” reporter ์ธ์Šคํ„ด์Šค์—์„œ article_set๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์ˆด!
  • repoter ์ธ์Šคํ„ด์Šค๋Š” article_set๋ฅผ ํ†ตํ•ด ์ž์‹ ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” article๋“ค์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—„! ์—ฌ๋Ÿฌ ๊ฐœ์ด๋‹ˆ๊น "set"๋ผ๋Š” ํ‘œํ˜„์„ ์ผ๋‚˜๋ถ€๋‹น
>>> r1 = Reporter.objects.get(id=1)
>>> r1
<Reporter: John Smith>
>>> 
>>> dir(r1)
['DoesNotExist', 'MultipleObjectsReturned', '__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', '_perform_date_checks', '_perform_unique_checks', '_save_parents', '_save_table', '_set_pk_val', '_state', 
'article_set', 
'check', 'clean', 'clean_fields', 'date_error_message', 
'delete',
'email', 'first_name', 
'from_db', 'full_clean', 'get_deferred_fields', 
'id', 'last_name', 
'objects', 'pk', 
'prepare_database_save', 'refresh_from_db', 
'save', 'save_base', 
'serializable_value', 'unique_error_message', 'validate_unique']
>>> 

article_set์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋“ค

>>> r1
<Reporter: John Smith>
>>> 
>>> dir(r1.article_set)
['__call__', '__class__', '__class_getitem__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slotnames__', '__str__', '__subclasshook__', '__weakref__', '_apply_rel_filters', '_constructor_args', '_db', '_get_queryset_methods', '_hints', '_insert', '_queryset_class', '_remove_prefetched_objects', '_set_creation_counter', '_update', 
'add', 'aggregate', 'all', 'annotate', 
'auto_created', 'bulk_create', 'bulk_update', 
'check', 'complex_filter', 'contribute_to_class', 
'core_filters', 'count', 
'create', 'creation_counter', 'dates', 'datetimes', 
'db', 'db_manager', 'deconstruct', 
'defer', 'difference', 
'distinct', 'do_not_call_in_templates', 
'earliest', 'exclude', 
'exists', 'explain', 'extra', 'field', 'filter', 
'first', 
'from_queryset', 
'get', 'get_or_create', 'get_prefetch_queryset', 'get_queryset', 
'in_bulk', 'instance', 'intersection', 'iterator', 
'last', 'latest', 'model', 
'name', 'none', 'only', 
'order_by', 'prefetch_related', 
'raw', 'reverse', 
'select_for_update', 'select_related', 
'set', 'union', 'update', 'update_or_create', 
'use_in_migrations', 'using', 
'values', 'values_list']
>>> 

2. article์—์„œ reporter ์ ‘๊ทผ ok

2-1. reporter_id, reporter

  • "reporter_id", "reporter"๊ฐ€ ์ƒ๊น€์„ ํ™•์ธ!!!
>>> a1 = Article.objects.get(id=1)
>>> dir(a1)
['DoesNotExist', 'MultipleObjectsReturned', '__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', '_perform_date_checks', '_perform_unique_checks', '_save_parents', '_save_table', '_set_pk_val', '_state', 
'check', 'clean', 'clean_fields', 'date_error_message', 
'delete', 
'from_db', 'full_clean', 
'get_deferred_fields', 'get_next_by_pub_date', 'get_previous_by_pub_date', 
'headline', 
'id', 'objects', 'pk', 
'prepare_database_save', 
'pub_date', 
'refresh_from_db', 
'reporter', 'reporter_id', 
'save', 'save_base', 
'serializable_value', 'unique_error_message', 'validate_unique']
>>> 

3. filter : ๊ด€๊ณ„๋งค๋‹ˆ์ €๋Š” ํ•„๋“œ๋ฃฉ์—…์„ ์ œ๊ณตํ•œ๋‹ค

  • double underscore๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ด€๊ณ„๋ฅผ ๋ถ„๋ฆฌ!
  • deepํ•œ ์ •๋„๋Š” ๋ฌดํ•œ๋Œ€!!!
>>> r1
<Reporter: John Smith>
>>> r1.article_set.filter(headline__startswith='This')
<QuerySet [<Article: This is a test>]>
>>> 

3-1. exact match

>>> Article.objects.filter(reporter__first_name='John')
<QuerySet [<Article: John's second story>, <Article: This is a test>]>
>>> 

3-2. WHERE์ ˆ์— AND์กฐ๊ฑด

>>> Article.objects.filter(reporter__first_name='John', reporter__last_name='Smith')
<QuerySet [<Article: John's second story>, <Article: This is a test>]>
>>> 

3-3. pk ๋˜๋Š” ๊ด€๊ณ„์˜ค๋ธŒ์ ํŠธ๋กœ ์กฐํšŒ

>>> Article.objects.filter(reporter__pk=1)
<QuerySet [<Article: John's second story>, <Article: This is a test>]>
>>> 
>>> Article.objects.filter(reporter=1)
<QuerySet [<Article: John's second story>, <Article: This is a test>]>
>>> 
>>> Article.objects.filter(reporter=r1)
<QuerySet [<Article: John's second story>, <Article: This is a test>]>
>>> r1
<Reporter: John Smith>
>>> 
>>> Article.objects.filter(reporter__in=[1,2]).distinct()
<QuerySet [<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]>
>>> 
>>> r1,r2
(<Reporter: John Smith>, <Reporter: Paul Jones>)
>>> 
>>> Article.objects.filter(reporter__in=[r1,r2]).distinct()
<QuerySet [<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]>
>>>
>>> Article.objects.filter(reporter__in=Reporter.objects.filter(first_name='John')).distinct()
<QuerySet [<Article: John's second story>, <Article: This is a test>]>
>>> 

3-4. ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ query

>>> Reporter.ojbects.filter(article__pk=1)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: type object 'Reporter' has no attribute 'ojbects'
>>> 
>>> 
>>> Reporter.objects.filter(article__pk=1)
<QuerySet [<Reporter: John Smith>]>
>>> Reporter.objects.filter(article=1)
<QuerySet [<Reporter: John Smith>]>
>>> Reporter.objects.filter(article=a1)
<QuerySet [<Reporter: John Smith>]>
>>> Reporter.objects.filter(article__headline__startswith='This')
<QuerySet [<Reporter: John Smith>]>
>>> Reporter.objects.filter(article__headline__startswith='This').distinct()
<QuerySet [<Reporter: John Smith>]>
>>> Reporter.objects.filter(article__headline__startswith='This').count()
1
>>> Reporter.objects.filter(article__headline__startswith='This').distinct().count()
1
>>> 

3-4. queries can go round in circles

>>> Reporter.objects.filter(article__reporter__first_name__startswith='John')
<QuerySet [<Reporter: John Smith>, <Reporter: John Smith>]>
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John').distinct()
<QuerySet [<Reporter: John Smith>]>
>>> Reporter.objects.filter(article__reporter=r1).distinct()
<QuerySet [<Reporter: John Smith>]>
>>> r1
<Reporter: John Smith>
>>> 

3-5. JOINํ•ด์„œ ์‚ญ์ œํ•˜๊ธฐ

>>> Reporter.objects.filter(article__headline__startswith='This')
<QuerySet [<Reporter: John Smith>]>
>>> 
>>> Reporter.objects.filter(article__headline__startswith='This').delete()
(3, {'practice.Article': 2, 'practice.Reporter': 1})
>>> 
>>> Reporter.objects.filter(article__headline__startswith='This')
<QuerySet []>
>>> 

์ฐธ๊ณ .

ํ”ผ์ž์™€ ํ† ํ•‘์€ M:N ๊ด€๊ณ„

ํ”ผ์ž ๋ชจ๋ธ์—๋‹ค๊ฐ€ ManyToManyField์ธ ์†์„ฑ์„ ๋„ฃ์–ด๋„ ๋˜๊ณ 
์•„๋‹ˆ๋ฉด, ํ† ํ•‘์—๋‹ค๊ฐ€ ManyToManyField์ธ ์†์„ฑ์„ ๋„ฃ์–ด์„œ
ํ”ผ์ž์™€ ํ† ํ•‘ ํ…Œ์ด๋ธ” ์‚ฌ์ด์˜ ๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉด ๋œ๋‹ค!

๊ทธ๋Ÿฌ๋‚˜, ์žฅ๊ณ ์— "form"์„ ์ด์šฉํ•œ๋‹ค๋ฉด!
ํ”ผ์ž๋ฅผ ์„ ํƒํ•˜๊ณ  ํ† ํ•‘์„ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ๋” ์ž์—ฐ์Šค๋Ÿฝ๊ธฐ์—!
ํ”ผ์ž์—๋‹ค๊ฐ€ ManyToManyField ์†์„ฑ์„ ๋„ฃ์–ด์„œ ํ† ํ•‘์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๊ฒƒ์ด ๋” ์ข‹์œผ๋‹น!

์ƒ์„ฑ๋œ ๋ฌผ๋ฆฌํ…Œ์ด๋ธ” ์•„๋ž˜์™€ ๊ฐ™๋‹น

๋‚˜:๋‚˜ = M:N์ธ ๊ฒฝ์šฐ

class HappyPerson(models.Model):
    friends = models.ManyToManyField("self")

CREATE TABLE "practice_happyperson" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT
)

CREATE TABLE "practice_happyperson_friends" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
"from_happyperson_id" integer NOT NULL REFERENCES 
"practice_happyperson" ("id") DEFERRABLE INITIALLY DEFERRED, 
"to_happyperson_id" integer NOT NULL REFERENCES 
"practice_happyperson" ("id") DEFERRABLE INITIALLY DEFERRED
)

Reference

profile
#์˜์‹์˜ํ๋ฆ„ #์ˆœ๊ฐ„์ˆœ๊ฐ„ #์ƒ๊ฐ์˜์Šค๋ƒ…์ƒท

0๊ฐœ์˜ ๋Œ“๊ธ€