ããã¯ãGoogleãã£ã¹ã«ãã·ã§ã³ã°ã«ãŒãã§å§ãŸããŸããã
https://groups.google.com/forum/#!topic/django -filter / RwNfoWsdeLQ
django-filterã䜿çšããŠcontrib.postgresJSONFieldsããã£ã«ã¿ãªã³ã°ã§ããããã«ããããšã«èå³ããããŸãã
ããã€ãã®äŸã§æ©èœãããã£ã«ã¿ãŒããããŸãã ããã¯ã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
ç§ã¯ããããããç¶ããã€ããã§ãã 誰ããäœãèãããã£ãŒãããã¯ãããã°ç§ã«ç¥ãããŠãã ãã...
ããã«ã¡ã¯@ jzmiller1ã æ£ç¢ºã«äœãéæããããšããŠããŸããïŒ ããªããããã§ãããã©ããããããªãïŒ
data
ã«å
±éããç¹å®ã®å±æ§ïŒç·¯åºŠã幎霢ïŒãå
¬éããããšããŠããŸãã ãããã®å±æ§ã¯ãåºæ¬çã«data
ã®ã¹ããŒãã«ãªããŸããåè
ã¯èå³æ·±ããã®ã§ããããåç¥ã®ããã«ãJSONFieldã¯æ¬è³ªçã«ã¹ããŒãã¬ã¹ã§ãããšããè€éãããããŸãã ã¹ããŒãããªããšããã£ã«ã¿ãŒãèªåçã«çæããã³ãŒããèšè¿°ã§ããŸããã MethodFilterã¯ãä»»æã®å±æ§ã«ãã¯ã¢ãããèš±å¯ãããšããç¹ã§æ©èœããŸããããããã®ã«ãã¯ã¢ãããæ€èšŒããããšã¯ã§ããŸããã ããšãã°ã ?data=latitude:char:PC:isnull
ã¯å¯èœã§ãããç¡æå³ã§ãã ããã§ã®è§£æ±ºçã«ã¯ããã¬ãŒããªããå¿
èŠã«ãªããŸãã å®å
šã«ä»»æã®ãã£ã«ã¿ãŒã¯ã«ãã¯ã¢ãããæ€èšŒã§ããŸãããæ€èšŒãã£ã«ã¿ãŒã«ã¯ãã¹ããŒããæäŸããäœããã®æ¹æ³ãå¿
èŠã«ãªããŸãã
2çªç®ã®ã±ãŒã¹ã§ã¯ã解決çã¯åé·ã§é¢åã§ãããç°¡åã§ãã
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ã®ææã«ãããšæ€èšŒã«ã€ããŠã§ãã ã¹ããŒãã¬ã¹ã¯éçºè ã®èŠ³ç¹ããã¯åªããŠããŸãããã¢ãã¬ã¹å¯èœãªURLã«çŽæ¥æ¥ç¶ããå¿ èŠããããã©ããã¯ããããŸããã
ä»ã®ãšãããããéãããŸãŸã«ããŠãããŸãããã 人æ°ã®ãªã¯ãšã¹ãã ãšæããŸãã ïŒãããã£ãŠãããã¥ã¡ã³ãã®_ "ããã«äŸMethodFilter
" _ã®ã¬ãã«ã§å¯ŸåŠããããšã䟡å€ããããŸããïŒ
ç§ãåãçµãã§ããããšã«ã€ããŠã¯ãç¹å®ã®ã¯ã¬ãŒã¿ãŒã®ããŒã¿ã®å 容ãããããããŸããããæ¢ããŠããããŒãããã«ããå Žåã¯ããããç §äŒã§ããããã«ããããšæããŸãã
ããã¯...ã¡ãã£ãšãã¡ã³ããŒãªããã§ãã ã¯ã¬ãŒã¿ãŒããŒã¿çšã®APIãæäŸããŠããŸãããæäŸããŠããããŒã¿ã«äœãå«ãŸããŠããã®ãããããŸãããïŒ äžéšã®ã¬ã³ãŒãã§å ±éã¹ããŒãã®å±æ§ãæ¬ èœããŠãããšããããšã§ããããããšãåã ã®ã¬ã³ãŒããå®å šã«ä»»æã§ãããšããããšã§ããïŒ
ãšããããããããç¯å²å€ãšããŠéããŸãã ææžåããããã¹ãããããã«ãªã¯ãšã¹ããæ€èšããŠãã ããã å°æ¥ãåèããèœåããããããããŸããã
jsonfield__a_random_key=value
ãªã©ã®ã¯ãšãªãæå¹ã«ããJSONFilterããããšæ¬åœã«çŽ æŽããããšæããŸãã objects.filter
ã¡ãœããã§ãããã§ããããšãç§ã¯ç¥ã£ãŠããŸãã ããããããã¬ãŒããªãã¯ãã£ã«ã¿ãŒã®æ€èšŒã§ããå¯èœæ§ããããŸããïŒ
Qãªããžã§ã¯ãã䜿çšããQuerySetãã£ã«ã¿ãŒã®ãèªç¶ãªãã¯ãšãªã®å®è£
ãå®äºããŸããã JsonFieldã䜿çšããŠãæ倧1000ã¬ã³ãŒãã®ã¯ãšãªã»ããã«å¯ŸããŠåäœãã¹ããè¡ââãããŠããŸãã å®è£
ã¯æ¬¡ã®ãšããã§ãã
https://github.com/shallquist/DJangoQuerySetFilter/blob/master/queryparser.py
ã¡ãã£ãš@shallquistim django-filterã®ã³ã³ããã¹ãã§QuerySetFilter
ã䜿çšããæ¹æ³ãããããŸããã ã©ããã§äœ¿çšæ³ãææžåããŸãããïŒ
githubã®readmeã«ç€ºãããŠããããã«ã䜿ãæ¹ã¯éåžžã«ç°¡åã§ãã éåžžã®ã¯ãšãªããµããŒããããŠããå¿
èŠããããŸãã
QuerySetFilterïŒ 'friends'ïŒãget_QueryïŒïŒperson__address__city = Denver | person__address__city = BoulderïŒïŒperson__address_stateã = COïŒ
ããã¯ãå人ãjsonfieldã§ããDeverãŸãã¯Boulderã³ãã©ãã«äœããã¹ãŠã®å人ãååŸããããã®ã¯ãšãªãäœæããŸãã
ãšããã§ãããã¯ããŸããã¹ããããŠãããã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
ã¯ãRESTãŠãŒã¶ãŒãæäŸããJSONField
ããã³value
ã§ãµããŒããããã¯ãšãªã®ããããã§ãã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ãã£ã«ã¿ãŒã
2ã€ã®åé¡ããããŸãã ãŸããdjango-filterã®å€ã®äžéšã¯ãã¯ãšãªãã©ã¡ãŒã¿ãŒãæ€èšŒããããšã§ãã JSONField
ã«ã¯ã¹ããŒãããªããããåä¿¡ããŒã¿ãé©åã«æ€èšŒãããã£ã«ã¿ãŒãçæããããšã¯ã§ããŸããã ããšãã°ãJSONãã£ãŒã«ãã«ãã«ãŠã³ããããŒãããå Žåãæ£ã®æ°ã®ã¿ãæå¹ã§ãããšçŽæçã«ç解ããããšã¯ã§ããŸããã å®è¡ã§ããæåã®æ¹æ³ã¯ãå€ãæå¹ãªJSONã§ããããšãä¿èšŒããããšã§ãã ãããã£ãŠãã¯ãšãªã¯å°ãªããšãæå¹ã§ãããç¡æå³ã§ããå¯èœæ§ããããŸãïŒããšãã°ã data__count__gt='cat'
ïŒã
2ã€ç®ã¯ããã®ãã£ã«ã¿ãŒã«ã¯MultiWidget
ããŒã¹ã®ãã£ã«ã¿ãŒãšåãå¶éããããšããããšã§ãã ããšãã°ãæ£ãããã©ã¡ãŒã¿åã®æ€èšŒãšã©ãŒã¯çæãããŸããã ããããããã«é£ã³èŸŒãåã«ããããç§ããããããã£ã«ã¿ãŒãå®è£
ããæ¹æ³ã§ãã å¿
èŠãªãã®ïŒ
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
ãæ£ããåŠçããæ¹æ³ã¯ãããŸãããMultiWidget
ãšäºææ§ããããŸããã ãã®ãã£ã«ã¿ãŒ/ãŠã£ãžã§ããã¯ãåãçç±ã§åãåé¡ã«ééããŸãã@rpkilbyã¯ããã®åŸ¹åºçãªå¯Ÿå¿ã«æè¬ããŸãã
JSONãã£ãŒã«ãã«ãã«ãŠã³ããããŒãããå Žåãæ£ã®æ°ã®ã¿ãæå¹ã§ãããšçŽæçã«ç解ããããšã¯ã§ããŸããã
ããã¯çŽ æŽãããç¹ã§ããã¯ãšãªå€ã®ã¿ã€ããæ€èšŒã§ããªããšããäºå®ã¯ã MyModel.objects.filter(data__count="1")
ãMyModel.objects.filter(data__count=1)
ãšåããã®ãè¿ããªããããéåžžã«å°é£ã§ãã ããªããèšãããã«ãã¯ãšãªãã©ã¡ãŒã¿ããå€ã®ã¿ã€ããæšæž¬ããæ¹æ³ã¯ãããŸããã
ãããã£ãŠãã¿ã€ãæ
å ±ãã¯ãšãªå€ã«åã蟌ããªãã·ã§ã³ã®ã¿ãæ®ãã ?data__count=1:int
ã®ããã«æŽæ°ãæ€çŽ¢ãã ?data__count=1:str
ã®ããã«æååãæ€çŽ¢ããŸãã ãã ããããã§ææ¡ãããŠããããã«ãããã¯ãå§ãããŸããã
ããã§ããã£ã«ã¿ãŒãæ瀺çã«å®çŸ©ããããšãéåžžã«éèŠã§ããçç±ãããããŸããã ããã§ããç§ã¯ããªãã®ææ¡ãè©ŠããŠã¿ãŸãïŒ å床ãæè¬ããŸã
@rpkilby ãç§ã«ãåæ§ã®ããŒãºããããŸãã
ç§ã¯2ã€ã®åãæã€ãã®ãããªæ§æããŒãã«ãæã£ãŠããŸã
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ãã£ãŒã«ãã«ãã£ã«ã¿ãŒãé©çšãããŸããã ãã®ããŒãã«ãå
æããããã«ç§ãå©ããŠãããŸãããïŒ
æãåèã«ãªãã³ã¡ã³ã
jsonfield__a_random_key=value
ãªã©ã®ã¯ãšãªãæå¹ã«ããJSONFilterããããšæ¬åœã«çŽ æŽããããšæããŸããobjects.filter
ã¡ãœããã§ãããã§ããããšãç§ã¯ç¥ã£ãŠããŸãã ããããããã¬ãŒããªãã¯ãã£ã«ã¿ãŒã®æ€èšŒã§ããå¯èœæ§ããããŸããïŒ