Django-filter: contrib.postgres JSONField ν•„ν„°

에 λ§Œλ“  2016λ…„ 06μ›” 04일  Β·  16μ½”λ©˜νŠΈ  Β·  좜처: carltongibson/django-filter

이것은 Google ν† λ‘  κ·Έλ£Ήμ—μ„œ μ‹œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€.
https://groups.google.com/forum/#!topic/django -filter/RwNfoWsdeLQ

django-filterλ₯Ό μ‚¬μš©ν•˜μ—¬ contrib.postgres JSONFieldsλ₯Ό ν•„ν„°λ§ν•˜λŠ” 데 관심이 μžˆμŠ΅λ‹ˆλ‹€.

λͺ‡ 가지 μ˜ˆμ—μ„œ μž‘λ™ν•˜λŠ” ν•„ν„°κ°€ μžˆμŠ΅λ‹ˆλ‹€. 이것은 IntegerField와 같은 μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” λ°©μ‹μœΌλ‘œ JSON의 데이터 μœ ν˜•μ„ 미리 μ•Œμ§€ λͺ»ν•œλ‹€λŠ” μ μ—μ„œ 생각보닀 λ³΅μž‘ν•©λ‹ˆλ‹€. λ„ˆλ¬΄ λ³΅μž‘ν•˜κ²Œ λ§Œλ“€ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

λ‹€μŒμ€ λ‚΄ JSONFieldλ₯Ό μ‘°νšŒν•˜λŠ” ν•„ν„°μ˜ μ˜ˆμž…λ‹ˆλ‹€.
http://127.0.0.1 :8000/api/v1/craters?data= latitude:float :-57:lte~!@!~ age:str :PC

λͺ¨λΈ 및 ν•„ν„° μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.
https://gist.github.com/jzmiller1/627071f555186cd1a58bb8f065205ff7

λ‚˜λŠ” 그것을 κ³„μ†ν•΄μ„œ μ–΄μ§€λŸ½κ²Œ ν•  κ²ƒμž…λ‹ˆλ‹€. μƒκ°μ΄λ‚˜ ν”Όλ“œλ°±μ΄ μžˆλŠ” μ‚¬λžŒμ΄ 있으면 μ•Œλ €μ£Όμ„Έμš”...

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

jsonfield__a_random_key=value 와 같은 쿼리λ₯Ό κ°€λŠ₯ν•˜κ²Œ ν•˜λŠ” JSONFilterκ°€ μžˆλ‹€λŠ” 것은 정말 λ†€λΌμš΄ 일이라고 μƒκ°ν•©λ‹ˆλ‹€. objects.filter λ°©λ²•μœΌλ‘œ ν•  수 μžˆλ‹€λŠ” 것을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. μ•„λ§ˆλ„ νŠΈλ ˆμ΄λ“œ μ˜€ν”„κ°€ ν•„ν„° μœ νš¨μ„± 검사가 될 수 μžˆμŠ΅λ‹ˆκΉŒ?

λͺ¨λ“  16 λŒ“κΈ€

μ•ˆλ…•ν•˜μ„Έμš” @jzmiller1μž…λ‹ˆλ‹€. μ •ν™•νžˆ 무엇을 λ‹¬μ„±ν•˜λ €κ³  ν•©λ‹ˆκΉŒ? λ‹€μŒκ³Ό 같은 경우 μ•Œ 수 μ—†μŠ΅λ‹ˆλ‹€.

  • JSONField λ‚΄λΆ€μ˜ μž„μ˜μ˜ 속성을 쿼리할 수 μžˆλŠ” 일반 JSONFilterλ₯Ό λ§Œλ“€λ €κ³  ν•©λ‹ˆλ‹€. λ˜λŠ”,
  • data 뢄화ꡬ에 곡톡적인 νŠΉμ • 속성(μœ„λ„, μ—°λ Ή)을 λ…ΈμΆœν•˜λ €κ³  ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ 속성은 본질적으둜 data 의 μŠ€ν‚€λ§ˆμž…λ‹ˆλ‹€.

μ „μžλŠ” ν₯미둭지 만 JSONFieldκ°€ 본질적으둜 μŠ€ν‚€λ§ˆκ°€ μ—†λ‹€λŠ” λ³΅μž‘μ„±μ΄ μžˆλ‹€λŠ” 것을 μ•Œμ•˜μŠ΅λ‹ˆλ‹€. μŠ€ν‚€λ§ˆκ°€ μ—†μœΌλ©΄ ν•„ν„°λ₯Ό μžλ™μœΌλ‘œ μƒμ„±ν•˜λŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μ—†μŠ΅λ‹ˆλ‹€. MethodFilterλŠ” μž„μ˜μ˜ 속성 쑰회λ₯Ό ν—ˆμš©ν•˜μ§€λ§Œ μ΄λŸ¬ν•œ 쑰회의 μœ νš¨μ„± 을 검사할 수 μ—†λ‹€λŠ” μ μ—μ„œ μž‘λ™ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, ?data=latitude:char:PC:isnull λŠ” κ°€λŠ₯ν•˜μ§€λ§Œ μ˜λ―Έκ°€ μ—†μŠ΅λ‹ˆλ‹€. 여기에 μžˆλŠ” μ†”λ£¨μ…˜μ΄ 무엇이든 νŠΈλ ˆμ΄λ“œμ˜€ν”„κ°€ ν•„μš”ν•©λ‹ˆλ‹€. μ™„μ „νžˆ μž„μ˜μ˜ ν•„ν„°λŠ” 쑰회의 μœ νš¨μ„±μ„ 검사할 수 μ—†μœΌλ©° μœ νš¨μ„± 검사 ν•„ν„°μ—λŠ” μŠ€ν‚€λ§ˆλ₯Ό μ œκ³΅ν•˜λŠ” λͺ‡ 가지 방법이 ν•„μš”ν•©λ‹ˆλ‹€.

두 번째 경우 μ†”λ£¨μ…˜μ€ μž₯ν™©ν•˜κ³  μ§€λ£¨ν•˜μ§€λ§Œ κ°„λ‹¨ν•©λ‹ˆλ‹€.

class CratersFilter(filters.FilterSet):
    latitude = filters.NumberFilter(name='data__latitude', lookup_expr='exact')
    latitude__lt = filters.NumberFilter(name='data__latitude', lookup_expr='lt')
    latitude__gt = filters.NumberFilter(name='data__latitude', lookup_expr='gt')
    latitude__isnull = filters.BooleanFilter(name='data__latitude', lookup_expr='isnull')
    # not sure if 'isnull' is a valid lookup for JSONFields - just demonstrating that 
    # different lookups expect different value types.

    age = filters.CharFilter(name='data__age', lookup_expr='exact')
    ...

그러면 μΏΌλ¦¬λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

http://127.0.0.1:8000/api/v1/craters?latitude__lte=-57&age=PC

λ‚΄ λͺ©ν‘œλŠ” JSONField λ‚΄μ˜ μž„μ˜μ˜ 속성에 λŒ€ν•œ 쿼리λ₯Ό ν—ˆμš©ν•˜λŠ” 일반 JSONFilterλ₯Ό λ§Œλ“œλŠ” κ²ƒμž…λ‹ˆλ‹€. λ‚΄κ°€ μž‘μ—…ν•˜κ³  μžˆλŠ” 것에 λŒ€ν•΄μ„œλŠ” νŠΉμ • 뢄화ꡬ에 λŒ€ν•œ 데이터 내뢀에 무엇이 μžˆλŠ”μ§€ μ•Œμ§€ λͺ»ν•˜μ§€λ§Œ 거기에 λ‚΄κ°€ μ°Ύκ³  μžˆλŠ” ν‚€κ°€ μžˆλ‹€λ©΄ 쿼리할 수 있기λ₯Ό λ°”λžλ‹ˆλ‹€.

쑰회 μœ ν˜•μ˜ μœ νš¨μ„±μ„ 검사할 수 μ—†λŠ” ν•œ λ‚˜λŠ” 쿼리λ₯Ό λ§Œλ“œλŠ” μ‚¬μš©μžμ— 따라 쿼리가 λ¬΄μ˜λ―Έν•˜κ³  μ²˜μŒλΆ€ν„° μ‹œμž‘ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” 것을 κΉ¨λ‹«κ²Œ 될 것이라고 μƒκ°ν•©λ‹ˆλ‹€.

λ‚΄κ°€ ν•˜λ €λŠ” 일이 μ‹œκ°„ 낭비인지 μ•„λ‹Œμ§€ 잘 λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€. λ‚΄κ°€ λ‹¬μ„±ν•˜λ €λŠ” 것에 λŒ€ν•œ 더 λ‚˜μ€ μ†”λ£¨μ…˜μ΄μžˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. 이것이 κ°€λŠ₯ν•˜μ§€ λͺ»ν•˜κ²Œ ν•˜λŠ” μ£Όμš” 문제λ₯Ό λ³Έ μ‚¬λžŒμ΄ μžˆλŠ”μ§€ λ˜λŠ” 이것이 μœ μš©ν•  μ‚¬μš© 사둀가 μžˆλŠ” μ‚¬λžŒμ΄ μžˆλŠ”μ§€ κΆκΈˆν–ˆμŠ΅λ‹ˆλ‹€. λ΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€!

λ‚΄ 첫 번째 생각은 @rpkilby 의 μš”μ μ— 따라 μœ νš¨μ„± 검사에 λŒ€ν•œ κ²ƒμž…λ‹ˆλ‹€. Schema-lessλŠ” 개발자의 κ΄€μ μ—μ„œ λ³Ό λ•Œ ν›Œλ₯­ν•˜μ§€λ§Œ μ£Όμ†Œ 지정 κ°€λŠ₯ν•œ URL에 직접 μ—°κ²°λ˜κΈ°λ₯Ό μ›ν•˜λŠ”μ§€ 잘 λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€.

μ§€κΈˆμ€ 이것을 μ—΄μ–΄ 두도둝 ν•©μ‹œλ‹€. μΈκΈ°μžˆλŠ” μš”μ²­μž„μ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. (λ”°λΌμ„œ _"여기에 MethodFilter μ˜ˆμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. "_ μˆ˜μ€€μ—μ„œ 닀루더라도 κ°€μΉ˜κ°€ μžˆμ„ κ²ƒμž…λ‹ˆλ‹€.)

λ‚΄κ°€ μž‘μ—…ν•˜κ³  μžˆλŠ” 것에 λŒ€ν•΄μ„œλŠ” νŠΉμ • 뢄화ꡬ에 λŒ€ν•œ 데이터 내뢀에 무엇이 μžˆλŠ”μ§€ μ•Œμ§€ λͺ»ν•˜μ§€λ§Œ 거기에 λ‚΄κ°€ μ°Ύκ³  μžˆλŠ” ν‚€κ°€ μžˆλ‹€λ©΄ 쿼리할 수 있기λ₯Ό λ°”λžλ‹ˆλ‹€.

이것은... μΌμ’…μ˜ νŽ‘ν‚€ν•œ 것 κ°™μŠ΅λ‹ˆλ‹€. 뢄화ꡬ λ°μ΄ν„°μš© APIλ₯Ό μ œκ³΅ν•˜κ³  μžˆμ§€λ§Œ μ œκ³΅ν•˜λŠ” 데이터에 무엇이 λ“€μ–΄ μžˆλŠ”μ§€ λͺ¨λ₯΄μ‹­λ‹ˆκΉŒ? 일뢀 λ ˆμ½”λ“œμ— 곡톡 μŠ€ν‚€λ§ˆμ˜ 속성이 λˆ„λ½λ˜κ±°λ‚˜ κ°œλ³„ λ ˆμ½”λ“œκ°€ μ™„μ „νžˆ μž„μ˜μ μ΄λΌλŠ” 것을 μ˜λ―Έν•©λ‹ˆκΉŒ?

당뢄간은 λ²”μœ„λ₯Ό λ²—μ–΄λ‚¨μœΌλ‘œ μ’…λ£Œν•˜κ² μŠ΅λ‹ˆλ‹€. λ¬Έμ„œν™”λ˜κ³  ν…ŒμŠ€νŠΈλœ pull μš”μ²­μ„ κ³ λ €ν•˜κ²Œ λ˜μ–΄ κΈ°μ©λ‹ˆλ‹€. μš°λ¦¬λŠ” λ―Έλž˜μ— μž¬κ³ ν•  수 μžˆλŠ” λŠ₯λ ₯을 κ°€μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

jsonfield__a_random_key=value 와 같은 쿼리λ₯Ό κ°€λŠ₯ν•˜κ²Œ ν•˜λŠ” JSONFilterκ°€ μžˆλ‹€λŠ” 것은 정말 λ†€λΌμš΄ 일이라고 μƒκ°ν•©λ‹ˆλ‹€. objects.filter λ°©λ²•μœΌλ‘œ ν•  수 μžˆλ‹€λŠ” 것을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. μ•„λ§ˆλ„ νŠΈλ ˆμ΄λ“œ μ˜€ν”„κ°€ ν•„ν„° μœ νš¨μ„± 검사가 될 수 μžˆμŠ΅λ‹ˆκΉŒ?

Q 개체λ₯Ό μ‚¬μš©ν•˜μ—¬ QuerySet 필터에 λŒ€ν•œ 'μžμ—°' 쿼리 κ΅¬ν˜„μ„ μ™„λ£Œν–ˆμŠ΅λ‹ˆλ‹€. JsonFieldλ₯Ό μ‚¬μš©ν•˜μ—¬ ~1000개의 λ ˆμ½”λ“œκ°€ μžˆλŠ” 쿼리 μ„ΈνŠΈμ— λŒ€ν•΄ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό κ±°μ³€μŠ΅λ‹ˆλ‹€. κ΅¬ν˜„ μœ„μΉ˜:
https://github.com/shallquist/DJangoQuerySetFilter/blob/master/queryparser.py

@shallquist django-filter μ»¨ν…μŠ€νŠΈμ—μ„œ QuerySetFilter λ₯Ό μ‚¬μš©ν•˜λŠ” 방법을 잘 λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€. μ–΄λ”˜κ°€μ— μ‚¬μš©λ²•μ„ λ¬Έμ„œν™” ν–ˆμŠ΅λ‹ˆκΉŒ?

github의 readme에 λ‚˜μ™€ μžˆλŠ” κ²ƒμ²˜λŸΌ μ‚¬μš©ν•˜κΈ°κ°€ 맀우 κ°„λ‹¨ν•©λ‹ˆλ‹€. 일반 쿼리가 μ§€μ›λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
QuerySetFilter('friends').get_Query((person__address__city = Denver | person__address__city = Boulder) & person__address_state ~= CO)
Dever λ˜λŠ” Boulder μ½œλ‘œλΌλ„μ— μ‚¬λŠ” λͺ¨λ“  친ꡬλ₯Ό κ²€μƒ‰ν•˜λŠ” 쿼리λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ μΉœκ΅¬λŠ” jsonfieldμž…λ‹ˆλ‹€.

BTW 이것은 많이 ν…ŒμŠ€νŠΈλ˜μ§€ μ•Šμ•˜μœΌλ©° Django ν•„ν„°λŠ” ν¬ν•¨λœ λ°°μ—΄ 개체 쿼리λ₯Ό μ§€μ›ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 이 μ ‘κ·Ό 방식을 ν¬κΈ°ν–ˆμŠ΅λ‹ˆλ‹€.

https://github.com/carltongibson/django-filter/issues/426#issuecomment -380224133

jsonfield__a_random_key=value와 같은 쿼리λ₯Ό κ°€λŠ₯ν•˜κ²Œ ν•˜λŠ” JSONFilterκ°€ μžˆλ‹€λŠ” 것은 정말 λ†€λΌμš΄ 일이라고 μƒκ°ν•©λ‹ˆλ‹€. λ‚˜λŠ” 당신이 objects.filter λ©”μ†Œλ“œλ‘œ 그것을 ν•  수 μžˆλ‹€λŠ” 것을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. μ•„λ§ˆλ„ νŠΈλ ˆμ΄λ“œ μ˜€ν”„κ°€ ν•„ν„° μœ νš¨μ„± 검사가 될 수 μžˆμŠ΅λ‹ˆκΉŒ?

μ•ˆλ…•ν•˜μ„Έμš” @carltongibson , @rpkilby 이에 λŒ€ν•œ κ·€ν•˜μ˜ 생각을 λ“£κ³  μ‹ΆμŠ΅λ‹ˆλ‹€. my_field κ°€ postgres JSONField 이고 λ‹€μŒμ„ μ›ν•œλ‹€κ³  κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

  • my_field__etc=value λͺ¨μ–‘μ˜ REST ν•„ν„°λ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ etc λŠ” JSONField 및 value μ—μ„œ μ§€μ›ν•˜λŠ” 쿼리 쀑 ν•˜λ‚˜μ΄λ©° REST μ‚¬μš©μžκ°€ μ œκ³΅ν•˜λŠ” λͺ¨λ“  κ²ƒμž…λ‹ˆλ‹€.
  • 그런 λ‹€μŒ etc 및 value λ₯Ό MyModel.objects.filter(my_field__etc=value) ν˜•μ‹μœΌλ‘œ λͺ¨λΈ 개체 κ΄€λ¦¬μžμ— μ „λ‹¬ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.
  • λ§ˆμ§€λ§‰μœΌλ‘œ ν•„ν„°κ°€ λ°˜ν™˜ν•˜λŠ” λͺ¨λ“  것을 κ²€μƒ‰ν•©λ‹ˆλ‹€.

μ•„μ£Ό μ‚¬μ†Œν•œ κ²ƒμ²˜λŸΌ λ³΄μ΄μ§€λ§Œ λ‚˜λŠ” 이와 같은 일에 λŒ€ν•΄ μƒκ°ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€. λ„ˆν¬λ“€μ΄ λ‚˜μ—κ²Œ μ•½κ°„μ˜ μ‹€λ§ˆλ¦¬λ₯Ό μ€€λ‹€λ©΄ λ‚˜λŠ” 그것을 κ΅¬ν˜„ν•˜λ €κ³  ν•  수 μžˆλ‹€.

μ–΄λ–€ 생각이라도 λŒ€λ‹¨νžˆ κ°μ‚¬ν•˜κ² μŠ΅λ‹ˆλ‹€!

λ‹€μŒκ³Ό 같은 것이 μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆκΉŒ?

class MyFilter(FilterSet):
    my_field__etc = filters.NumberFilter(field_name='my_field', lookup_expr='etc')

일반적으둜 field_name λŠ” κΈ°λ³Έ λͺ¨λΈ ν•„λ“œ 이름과 μΌμΉ˜ν•΄μ•Ό ν•˜λ©° λ³€ν™˜ 및 쑰회(이 경우 ν‚€ λ³€ν™˜)λŠ” lookup_expr 에 ν¬ν•¨λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

@rpkilby λΉ λ₯Έ 응닡에 κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€ - λ„€ λ§žμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ μš”μ²­μ—μ„œ μ‚¬μš©μžκ°€ μ œκ³΅ν•œ etc 을(λ₯Ό) μ›ν•©λ‹ˆλ‹€... κ·Έλž˜μ„œ 필터에 ν•˜λ“œμ½”λ”©ν•  수 μ—†μ—ˆμŠ΅λ‹ˆλ‹€ πŸ’­

ν•„ν„°λŠ” λ‹€μŒκ³Ό κ°™μ•„μ•Ό ν•©λ‹ˆλ‹€.

class MyFilter(FilterSet):
    my_field = JSONFieldFilter(field_name='my_field')

λ”°λΌμ„œ ?my_field__etc=value 와 같은 μž„μ˜μ˜ 쿼리 λ§€κ°œλ³€μˆ˜λ₯Ό μ²˜λ¦¬ν•˜λŠ” 단일 JSON ν•„ν„°μž…λ‹ˆλ‹€.

두 가지 λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. 첫째, django-filter κ°’μ˜ μΌλΆ€λŠ” 쿼리 λ§€κ°œλ³€μˆ˜μ˜ μœ νš¨μ„±μ„ κ²€μ‚¬ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. JSONField μ—λŠ” μŠ€ν‚€λ§ˆκ°€ μ—†κΈ° λ•Œλ¬Έμ— μΈλ°”μš΄λ“œ 데이터λ₯Ό μ μ ˆν•˜κ²Œ κ²€μ¦ν•˜λŠ” ν•„ν„°λ₯Ό 생성할 수 μ—†μŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ JSON ν•„λ“œμ— "count" ν‚€κ°€ μžˆλŠ” 경우 μ–‘μˆ˜λ§Œ μœ νš¨ν•˜λ‹€λŠ” 것을 직관할 수 μ—†μŠ΅λ‹ˆλ‹€. ν•  수 μžˆλŠ” μ΅œμ„ μ€ 값이 μœ νš¨ν•œ JSONμž„μ„ 보μž₯ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. λ”°λΌμ„œ μΏΌλ¦¬λŠ” μ΅œμ†Œν•œ μœ νš¨ν•˜μ§€λ§Œ μ˜λ―Έκ°€ 없을 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€(예: data__count__gt='cat' ).

두 λ²ˆμ§ΈλŠ” 이 ν•„ν„°κ°€ MultiWidget 기반 필터와 λ™μΌν•œ μ œν•œμ„ κ°–λŠ”λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ˜¬λ°”λ₯Έ λ§€κ°œλ³€μˆ˜ 이름에 λŒ€ν•œ μœ νš¨μ„± 검사 였λ₯˜λ₯Ό μƒμ„±ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ 이에 λŒ€ν•΄ μ•Œμ•„λ³΄κΈ° 전에 ν•„ν„°λ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. λ‹€μŒμ΄ ν•„μš”ν•©λ‹ˆλ‹€.

  • μ—¬λŸ¬ λ§€κ°œλ³€μˆ˜λ₯Ό μ²˜λ¦¬ν•΄μ•Ό ν•˜λŠ” μ‹€μ œ 필터링을 μˆ˜ν–‰ν•˜λŠ” ν•„ν„° 클래슀
  • JSON λ°μ΄ν„°μ˜ μœ νš¨μ„±μ„ κ²€μ‚¬ν•˜λŠ” 양식 ν•„λ“œ
  • μž„μ˜μ˜ my_field__* λ§€κ°œλ³€μˆ˜μ— λŒ€ν•œ 데이터λ₯Ό κ°€μ Έμ˜€λŠ” μœ„μ ―μž…λ‹ˆλ‹€.
class JSONWidget(widgets.Textarea):
    """A widget that handles multiple parameters prefixed with the field name."""

    def value_from_datadict(self, data, files, name):
        prefix = f'{name}{LOOKUP_SEP}'

        # this is doing two things: 
        # - matches multiple params for the base field name
        # - in addition to returning the value, we also need the full parameter name
        #   for querying. otherwise, values will be filtered against the base `name`. 
        return {k: v for k, v in data.items() if k.startswith(prefix)}

    def get_context(self, name, value, attrs):
        # to support rendering the widget, you would need to generate subwidgets
        # similar to MultiWidget.get_context.
        pass

class JSONField(postgres.forms.JSONField):
    widget = JSONWidget

    def clean(self, value):
        # note that it's not possible to collect/reraise any validation errors under
        # their actual parameter names. `form.add_error` should be used here, however
        # the field class does not have access to the form instance. raising 
        # ValidationError({k: str(original_exc)}) also does not work. 

        # clean/convert each value
        return {k: super().clean(v) for k, v in value.items()}

class JSONFilter(filters.Filter):
    field_class = JSONField

    def filter(self, qs, value):
        if value in EMPTY_VALUES:
            return qs
        return qs.filter(**value)

μœ„μ˜ ν…ŒμŠ€νŠΈλŠ” 해보지 μ•Šμ•˜μ§€λ§Œ λŒ€λž΅μ μœΌλ‘œ μ •ν™•ν•  κ²ƒμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ λ‹€μŒκ³Ό 같은 μ œν•œ 사항이 μžˆμŠ΅λ‹ˆλ‹€.

  • λ‚΄κ°€ 말할 수 μžˆλŠ” ν•œ λ§€κ°œλ³€μˆ˜λ‹Ή ValidationError sλ₯Ό μ˜¬λ°”λ₯΄κ²Œ μ²˜λ¦¬ν•  방법이 μ—†μŠ΅λ‹ˆλ‹€.
  • OpenAPI/CoreAPI μŠ€ν‚€λ§ˆ 지원이 쒋지 μ•ŠμŠ΅λ‹ˆκΉŒ? 이것이 μ–΄λ–»κ²Œ μƒκ²ΌλŠ”μ§€ ν™•μ‹€ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • djangorestframework-filters λŠ” MultiWidget 와 ν˜Έν™˜λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ . 이 ν•„ν„°/μœ„μ ―μ€ λ™μΌν•œ 이유둜 λ™μΌν•œ λ¬Έμ œμ— μ§λ©΄ν•©λ‹ˆλ‹€.

@rpkilby μ² μ €ν•œ λ‹΅λ³€ κ°μ‚¬ν•©λ‹ˆλ‹€.

JSON ν•„λ“œμ— "count" ν‚€κ°€ μžˆλŠ” 경우 μ–‘μˆ˜λ§Œ μœ νš¨ν•˜λ‹€λŠ” 것을 직관할 수 μ—†μŠ΅λ‹ˆλ‹€.

MyModel.objects.filter(data__count="1") κ°€ MyModel.objects.filter(data__count=1) 와 같은 값을 λ°˜ν™˜ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 쿼리 κ°’μ˜ μœ ν˜•μ„ 확인할 수 μ—†λ‹€λŠ” 사싀이 맀우 μ€‘μš”ν•©λ‹ˆλ‹€. λ§μ”€ν•˜μ‹ λŒ€λ‘œ 쿼리 λ§€κ°œλ³€μˆ˜μ—μ„œ κ°’μ˜ μœ ν˜•μ„ μΆ”μΈ‘ν•  수 μžˆλŠ” 방법은 μ—†μŠ΅λ‹ˆλ‹€.

λ”°λΌμ„œ 쿼리 값에 μœ ν˜• 정보λ₯Ό ν¬ν•¨ν•˜λŠ” μ˜΅μ…˜λ§Œ 남겨두고 ?data__count=1:int 와 같은 μž‘μ—…μ„ μˆ˜ν–‰ν•˜μ—¬ μ •μˆ˜λ₯Ό κ²€μƒ‰ν•˜κ³  ?data__count=1:str λ¬Έμžμ—΄μ„ κ²€μƒ‰ν•˜λŠ” λ“±μ˜ μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ—¬κΈ° 에 μ œμ•ˆλœ λŒ€λ‘œ 이것은 ꢌμž₯λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

이제 ν•„ν„°λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ •μ˜ν•˜λŠ” 것이 μ™œ κ·Έλ ‡κ²Œ μ€‘μš”ν•œμ§€ μ΄ν•΄ν•©λ‹ˆλ‹€. κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³ , λ‚˜λŠ” λ‹Ήμ‹ μ˜ μ œμ•ˆμ„ μ‹œλ„ν•  κ²ƒμž…λ‹ˆλ‹€! λ‹€μ‹œ ν•œ 번 κ°μ‚¬ν•©λ‹ˆλ‹€

@rpkilby , λ‚˜λ„ λΉ„μŠ·ν•œ ν•„μš”κ°€ μžˆμŠ΅λ‹ˆλ‹€.

두 개의 열이 μžˆλŠ” 이와 같은 ꡬ성 ν…Œμ΄λΈ”μ΄ μžˆμŠ΅λ‹ˆλ‹€.

meta_structure of type jsonb (This column has info like key1 of type string, key2 of type integer)

3개의 열이 μžˆλŠ” config_dataλΌλŠ” λ‹€λ₯Έ ν…Œμ΄λΈ”μ΄ μžˆμŠ΅λ‹ˆλ‹€.

config_id -> Foreign key to config table
meta_info -> jsonb type

μ°Έκ³ : μœ„μ— μ–ΈκΈ‰λœ ν‘œλŠ” μ •ν™•ν•œ ν‘œκ°€ μ•„λ‹™λ‹ˆλ‹€. λ©”μ‹œμ§€λ₯Ό μ „λ‹¬ν•˜κΈ° μœ„ν•œ λŒ€ν‘œμ μΈ 버전일 λΏμž…λ‹ˆλ‹€.

ν˜„μž¬ ꡬ성 ν…Œμ΄λΈ”μ—μ„œ μΌμΉ˜ν•˜λŠ”μ§€ ν™•μΈν•˜μ—¬ μ €μž₯ν•˜κΈ° 전에 meta_info ν…Œμ΄λΈ”μ˜ ν•„λ“œλ₯Ό κ²€μ¦ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

ν•„μš”ν•œ 것은 config_data ν…Œμ΄λΈ”μ˜ meta_info 열을 μ‚¬μš©ν•˜μ—¬ ν•„ν„°λ§ν•˜λ €λŠ” κ²ƒμž…λ‹ˆλ‹€. 예. meta_info__key1='abc'. (key1은 무엇이든 될 수 있음)

μœ„μ—μ„œ μ œκ³΅ν•œ μ ‘κ·Ό 방식을 μ‚¬μš©ν•˜λ €κ³  μ‹œλ„ν–ˆμ§€λ§Œ λ¬Έμ œλŠ” μœ„μ—μ„œ μƒμ„±ν•œ JSONFilter 클래슀λ₯Ό μ–΄λ–»κ²Œ μ‚¬μš©ν•˜λŠ”μ§€μž…λ‹ˆλ‹€.

예.

class ConfigDataFilterSet(django_filters.FilterSet):
    meta_info = JSONFilter(field_name='meta_info')

pp = ConfigDataFilterSet(data={'meta_info__key1': 'abc'})

이제 pp.qs λ˜λŠ” pp.filter_queryset() λ₯Ό μ‹€ν–‰ν•˜λ©΄ ConfigDataFilterSet ν΄λž˜μŠ€μ— ν• λ‹Ήλœ ν•„λ“œ 이름이 meta_info이기 λ•Œλ¬Έμ— μ‹€μ œλ‘œ meta_info ν•„λ“œμ— ν•„ν„°λ₯Ό μ μš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 이 λ‚œκ΄€μ„ 극볡할 수 μžˆλ„λ‘ λ„μ™€μ£Όμ‹œκ² μŠ΅λ‹ˆκΉŒ?

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰