Djangoã«ã¯çŽ æŽãããetag / condition / last_modifiedãã³ã¬ãŒã¿ããããŸãã drfã¯ã©ã¹ããŒã¹ã®ãã¥ãŒã§ã¯ããgetããè£ é£Ÿã§ããªããããæ©èœããŸããã getã¯httpå¿çã§ã¯ãªããªããžã§ã¯ããè¿ããããå¿çã«etagããããŒãè¿œå ããæ¹æ³ã¯ãããŸããã
drfå ãããããè¡ãæ¹æ³ãç¥ãããã§ãã ãªãœãŒã¹ã®ãªãŒããŒã©ã€ãå¯èœãªã¡ãœããããŸãã¯etagãçæããããã«äœ¿çšã§ãããã¥ãŒïŒãŸãã¯ããã¯ã¹ã€ã³ïŒã«æ²¿ã£ãäœããèããŠããŸãã
djangoã§ãããè¡ããã1ã€ã®æ¹æ³ã¯ãããã«ãŠã§ã¢ã䜿çšããããšã§ããããã³ã¬ãŒã¿ã®ããã«ãã¥ãŒã®æ¬äœãã·ã§ãŒãã«ããå®è¡ããããšã¯ã§ããŸããã
ããŠãç§ã¯äºåããããæã£ãŠããŸãïŒ //github.com/schinckel/django-rest-framework/commit/cc3a88edc6be21347a9b35929d158b8831ba9bd3
ãã ããããã«é¢ãããã£ãŒãããã¯ã«ã¯éåžžã«æºè¶³ããŠããŸãã
ããŠãæåã«ç§ãæžããã®ã¯ããã§ããâŠã
ãã£ãããããããç§ã¯æ¬åœã«ãããã§èŠããã§ãã
Couplaã®èã-çŸåšäœ¿çšããŠããView._ETAGã§ã¯ãªããView.add_headerã䜿çšã§ããã¯ãã§ãã
ïŒãããŠã.add_headerã¯ããããResponseMixinã¯ã©ã¹ã«ç§»åããå¿
èŠãããããã§ããïŒ
次ã«ã @ condition ã @ etag ã @ last_modifiedã®ãã³ã¬ãŒããã httpsïŒ//github.com/django/django/blob/master/django/views/decoratorsã®ã»ãŒçã£çŽããªã¯ããŒã³ã«ãªãããšã確èªããããšæã
ããããããå°ãç©äºã調ã¹ãŠããŸãã...
ãããŠãããããããã¯çµå±ã®ãšããå®å šã«æ£ããæ¹æ³ã§ã¯ãããŸãã...
å®éã«ã¯ãRESTãã¬ãŒã ã¯ãŒã¯ãã¥ãŒããHttpResponseãè¿ãããšãã§ããŸãããéåžžã®ã³ã³ãã³ãããŽã·ãšãŒã·ã§ã³/ã·ãªã¢ã«åã®ãã¹ãŠãé©çšãããããã§ã¯ãããŸããã @last_modified
ã @etag
ãããã³@condition
ãã³ã¬ãŒã¿ã¯ã空ã®HttpResponseã®ã¿ãè¿ããããå®éã«ã¯åé¡ã«ã¯ãªããŸããã
ã€ãŸããç§ãèããŠããã®ã¯ãåã«__setitem__
__getitem__
ãšhas_header
ãResponseã¯ã©ã¹ã«è¿œå ããå ŽåãDjangoã®æ¢åã®@last_modified
ã ãšæããŸãã @etag
ãš@condition
ãã¥ãŒã䜿çšããŠããas_ãã³ã¬ãŒã¿ã_soé·ãRESTãã¬ãŒã ã¯ãŒã¯ãã¥ãŒã§ããŸãåäœããã¯ãã§ãreturn Response(status, data)
ã§ã¯ãªããã¹ã¿ã€ã«return data
ã¹ã¿ã€ã«ãã
ãããææžåãããšäŸ¿å©ã§ãããDjangoããã§ã«è¡ã£ãŠããããšãè€è£œãããããçã«ããªã£ãŠãããããããŸããã
ã©ãæããŸããïŒ
djangoãã³ã¬ãŒã¿ã®äœ¿çšã«ã¯ãåé¡ã1ã€ããå¯èœæ§ããããŸããã¡ãœããã§æ©èœãããã©ããã¯ããããŸããããé¢æ°ã¯ããåºãã§ãã ç§ãæžãããã³ã¬ãŒã¿ã¯ããã®å Žåã®ããã«StackOverflowã§èŠã€ãããã³ã¬ãŒã¿ã«å€§ããåºã¥ããŠããŸãã
ããã§ã¯ãªãå ŽåããããŸããããã®å Žåããã®ãœãªã¥ãŒã·ã§ã³ã®æ¹ãåªããŠããããã«æãããŸãã
ããã¯èšã£ãŠãããã€ã©ãŒãã¬ãŒããå°ãªããããæ»ãããŒã¿ã¹ã¿ã€ã«ãè¿ããŠããŸãããéåžžãjsonãšããŠã·ãªã¢ã«åãããªããžã§ã¯ãã®ã¿ãè¿ããŸãã äž¡æ¹ã®æ¹æ³ã§æ©èœãããããšãã§ãããããããŸããã
å¥ã®ãªãã·ã§ã³ã¯ãããããViewã¯ã©ã¹ã«è¿œå ããããã¯ã¹ã€ã³ã§ãã
ãŸããdjangoãã³ã¬ãŒã¿ã¯ãæ¡ä»¶ä»ãã®PUTãPOSTãããã³DELETEèŠæ±ã«é¢ããŠæ£ããããšãè¡ããªãå¯èœæ§ãããããšãç§ã«ã¯æãæµ®ãã³ãŸãã ãã®åé¡ãä¿®æ£ããããã«sinatraã«ããããéä¿¡ããŸããã
æåŸã®ããããç¡èŠããŠãã ããïŒæããã«ç§ã¯ã³ãŒããæ£ããèªãã§ããŸããã§ããã
å®éã«ã¯ãããã«ãã€ã³ãããããŸãããããã®ãã³ã¬ãŒã¿ã¯ãè¿œå ã®ãselfãåŒæ°ãæã£ãŠããããã_methods_ã§ATMãæ©èœãããªãå¯èœæ§ããããŸãã ç§ã¯ããã調ã¹ãŠãDjangoã«ãã±ãããæåºããå¯èœæ§ããããŸãã圌ããCBVãšé£æºããå¿ èŠãããããã§ã...
ãããããããŸãã- @ method_decoratorã衚瀺ãããŸã... httpsïŒ//docs.djangoproject.com/en/dev/topics/class-based-views/#decorating -class -based-views
ã ããç§ã¯ãããéããããã«ãããå¿
èŠã ãšæããŸãïŒ
ãã®ããã¥ã¡ã³ãã調ã¹ããšæããŸããããã®ãã³ã¬ãŒã¿ã¯èŠã€ãããŸããã§ããã
djangoãã³ã¬ãŒã¿ã䜿çšããããšããŸããããæåã®çµæã¯éåžžã«å¥åŠã§ãé¢é£ã®ãªãetagé¢æ°ã«è¡šç€ºããããã¥ãŒã«ã®ã¿å±ããã¯ãã®æ å ±ãå«ãŸããŠããŸããã
ãããå°ãè¯ãããããšæ°æéåŸãç§ã¯èªåã®åé¡ã解決ããååã«äžè¬çã§ããããã«èŠãã解決çãæãã€ããŸããã ããã«ã€ããŠã©ãæããŸããïŒ
https://bitbucket.org/vitormazzi/django-rest-framework/changeset/6f8de4500c6f
2.xãªãªãŒã¹ãéå§ããä»ããã®åé¡ã«æ°ããªæ¯å¹ãå¹ã蟌ãããšãæãã§ããŸãã
ããã§ã®ç§ã®èãã¯ããããžã§ã¯ãã«æ©èœãè¿œå ããããšããããšãšããã®æçš¿ãèªã
DRFãETagãèæ ®ããå¿ èŠããã2ã€ã®é åãã€ãŸããã¥ãŒã§ã®äœ¿çšæ³ãšãã€ã³ã¹ã¿ã³ã¹ã®ããŒãžã§ã³ã®äžæã®è¡šçŸãååŸããæ¹æ³ãããããŸãã
åŸã
GETãªã¯ãšã¹ãã¯ãé©åãªããããŒã§ãªããžã§ã¯ãETagãæäŸããå¿
èŠããããŸãã RetrieveModelMixin
ãžã®1è¡ã®å€æŽã§ããããç°¡åã«è¿œå ã§ããŸãã
def retrieve(self, request, *args, **kwargs):
self.object = self.get_object()
serializer = self.get_serializer(self.object)
headers = {'ETag': self.object.etag}
return Response(serializer.data, headers=headers)
PUTãPATCHãDELETE
HTTPåè©ãæŽæ°ããããã®äžè¬çãªãã§ãã¯ã¯ããã¥ãŒã®dispatch
ããããETagããªã³ã«ãªã£ãŠãããã©ããããã§ãã¯ããå¿
èŠããããããå¥ã®ã¡ãœããã«ãã«ã¢ãŠãããããšãã§ããŸãïŒä»¥äžã®ãªãã·ã§ã³ã»ã¯ã·ã§ã³ãåç
§ïŒã
header_etag = request.META.get('HTTP_IF_MATCH')
if header_etag is None:
return Response({'error': 'IF_MATCH header is required'}, status=400)
次ã«ããªããžã§ã¯ããååŸããåŸã®ãã詳现ãªãã§ãã¯ã§ããªã¯ãšã¹ããæ£ãããªããžã§ã¯ããåç §ããŠãããšèŠãªããã©ããã確èªããŸãã
if self.object.etag != header_etag:
return Response({'error': 'object has been updated since you last saw it'}, status=412)
ãªããžã§ã¯ãã®ETagã®å®éã®çæãDRFã®åé¡ã«ãªããšã¯æããªãã ãªããžã§ã¯ãã®updated
ãã£ãŒã«ãã®ãšããã¯æéã䜿çšããŠãã¹ãããŠããŸãããããã£ãšè€éã«ããå¿
èŠãããããšãç°¡åã«ããããŸããã
DRFã¯ããã©ã«ãã§obj.etag
æ€çŽ¢ããããšãææ¡ããŸãããéåžžã®CBVãããŒïŒ get_etag()
ãetag_var = 'get_my_objects_etag'
ã䜿çšããŠæ§æã§ããŸãã
ãŸããããããŒãšæ¯èŒããŠã¿ã€ãã解éããããšãããšãããããèŠçã«ãªããããETagããªããžã§ã¯ãããæååãšããŠååŸãããããã«ããå¿ èŠããããŸãã
use_etags
ïŒãŸãã¯åæ§ã®ãã®ïŒ-ããŒã«å€etag_var
-åé¡ã®ãªããžã§ã¯ãã«å¯ŸããŠgetattr
ã§ããé¢æ°åã®æåå@ ghickman -ETagãšLastModifiedã決å®ããããã®åäœããä»ã®ãã©ã°å¯èœãªã¯ã©ã¹ãšåãããã«èŠããããã«ããããšæããŸãã ã€ãŸãã 次ã®ãããªãã®ããããŸãïŒ
class MyView(views.APIView):
cache_lookup_classes = []
眲åã®ãã£ãã·ã¥ã¯ETagãšLastModifiedã®äž¡æ¹ãåŠçããå¿ èŠããããæäŸããã2ã€ã®ç°ãªããã®ããããŸãã
BaseCacheLookup
ã次ã®ãããª2ã€ã®ã¡ãœããã·ã°ããã£ããããŸãã
.object_etag_and_last_modified(self, view, obj)
.preemptive_etag_and_last_modified(self, view, request, *view_kwargs, **view_kwargs)
ãªããžã§ã¯ãã
(etag, last modified)
2ã€ã®ã¿ãã«ãè¿ããšãããšãã©ã¡ããåçŽã«ãªãã«ãªãå¯èœæ§ããããŸãã
çä¿¡èŠæ±ã«äžèŽããIf-Modified-SinceãŸãã¯If-None-MatchããããŒãå«ãŸããŠããå Žåã304 NotModifiedå¿çãè¿ãããŸãã çä¿¡å¿çã«äžèŽããIf-MatchãŸãã¯If-Unmodified-Sinceãå«ãŸããŠããå Žåã412 PreconditionFailedå¿çãè¿ãããŸãã
ããã«ããã説æããå®è£ ã«äžèŽããCacheLookupClassã ãã§ãªããä»ã®ããªã¢ã³ããèš±å¯ãããŸãã
ããšãã°ãæåŸã«å€æŽãããç²åºŠãç°ãªãè€æ°ã®ãã£ãã·ã¥ã«ãã¯ã¢ããã¯ã©ã¹ãé©çšããããšãã§ããŸãã
å«ããGlobalLastModifiedLookup
ã«å ããŠã ObjectETagLookup
ã ããã«ããããã£ãã·ã¥ãããã³ããŒä»¥éã«æžã蟌ã¿ãè¡ãããªãã£ãå Žåã«ãããŒã¿ããŒã¹åŒã³åºããè¡ãåã«ãã¥ãŒãããªãšã³ããã£ãã«æ»ãããšãã§ããŸãã ïŒVarnishã§ãµãŒããŒåŽã®ãã£ãã·ã¥ã䜿çšããŠããå Žåããã®ãããªæ¬åœã«åºæ¬çãªããªã·ãŒã§ããã倧ããªéããçãå¯èœæ§ããããŸãïŒ
ãã®ãã©ã°ã€ã³å¯èœãªã¯ã©ã¹åŽã¯ããªãã«ãšã£ãŠåççã§ããïŒ
çŸåšã®å®è£ ã§ã¯LastModifiedã䜿çšããŠããªããããLastModifiedã«ã€ããŠã¯èããŠããŸããã§ããããç®çãèããã°ãLastModifiedãå«ããããšã¯ééããªãçã«ããªã£ãŠããŸãã
ç¹ã«LastModifiedãšETagã®å®è£ ãåºæ¬çãªäŸãšããŠå«ããå Žåããã©ã°ã€ã³å¯èœãªã¯ã©ã¹ã¯çŽ æŽãããã¢ã€ãã¢ã®ããã«èãããŸãã ãããžã§ã¯ãã«æå°éã®å€æŽãå ããã ãã§ãGETãã£ãã·ã¥ãéåžžã«ç°¡åã«ãªã³ã«ã§ãããšããèããæ°ã«å ¥ã£ãŠããŸãã
ç§ã¯ãetagãšlast_modifiedã®çæãïŒãããã®ååã®ïŒ2ã€ã®ã¡ãœããã«åå²ããããšæããŸããããªããææ¡ããããã«ãå®è£
ãããŠããªãå Žåã¯None
è¿ããŸãã 次ã«ãCacheLookupããã¯ãšã³ãã¯ãäžæ¹ãŸãã¯ä»æ¹ã®å®è£
ãéžæã§ããŸãã 䟿å©ã ãšæãããå Žåã¯ã2ã€ãçµã¿åããã䟿å©ãªãŠãŒãã£ãªãã£ã¡ãœããïŒ cachable_obj_repr
ãŸãã¯unique_obj_repr
ãããããŸãããïŒïŒããã€ã§ãæäŸã§ããŸãã
tl; drã¯ãããã©ã¬ãã«ã¯ã©ã¹åŽã¯åççã«èãããã¯ããã«é«ãæè»æ§ãæäŸããã¯ãã§ãã ãã®ããã®ããããæžãå§ããŠããããã§ãã
ããã«ã¡ã¯ãã¿ããªã èå³ãããã°ãæ¡åŒµæ©èœã©ã€ãã©ãªhttp://chibisov.github.io/drf-extensions/docs/ã«etagãµããŒãã®ããŸããŸãªã¢ãããŒããå®è£ ããŸããã
@chibisovNeato ã ç§ãã¡ã¯æ¬åœã«ïŒ1019ãçµããã¹ããªã®ã§ããã®ãããªããã±ãŒãžã«ãªã³ã¯ããããã®ããã¥ã¡ã³ãã®ã©ããã«ãããŸãã
ïŒ1019ãéãããã @ chibisovã®ããã±ãŒãžããªã¹ããããŠãããããããéããŸãã
ããã¯ãããæç¹ã§æ£åŒãªæ瀺ãäžãããã®ã§ãæå³çã«3.3ãšããŠãã€ã«ã¹ããŒã³ãããŸããã ãããéãããŸãŸã«ããããšãéžæãããã©ããã«ã€ããŠã¯ããŸãå¿é ããŠããŸããããããã¯ç§ã®å éšããŒããããã«ãããŸãã
æ®å¿µãªãããdrf-extensionsã®Etagæ©èœã«é¢ããããã©ã«ãã®å®è£ ãšããã¥ã¡ã³ãã¯åã«ééã£ãŠãããå±éºãªã»ã©ãã°ããããŸãã _response_ãå€æŽãããå Žåã§ã¯ãªãã_request_ãå€æŽãããå Žåã«Etagãå€æŽãããŸãã ããã¯ãŸãã«ãµãŒããŒãµã€ããã£ãã·ã³ã°ã«å¿ èŠãªãã®ã§ãããEtagã«ã¯å¿ èŠãªããã®ã§ãã
@mboxã¯ãdrf-extensionsã§ããã«é¢ããåé¡ãéãããDRFã®ãã³ã¢ãåé¡ã§ãããšæãããå Žåã¯ããã§éãã®ãæåã§ãã 倱æãããã¹ãã¯ãåé¡ã調ã¹ãããã®è¯ãã¹ã¿ãŒãã«ãªãããšã«æ³šæããŠãã ããã
@mbox @xordoquy
DRF APIãä»ããŠãªãœãŒã¹ãæäœããããã®æ¥œèŠ³çåæå®è¡å¶åŸ¡ãå¯èœã«ããPRãdrf-extensionsïŒhttps://github.com/chibisov/drf-extensions/pull/171ïŒã«éä¿¡ããŸããã ãã¹ãŠã®ãªããžã§ã¯ããã£ãŒã«ãã®ã»ãã³ãã£ãã¯ããã·ã¥ã䜿çšããŠããããã¢ã³ã¹ãã¬ãŒã·ã§ã³çšã«ãã¹ãã¢ããªãå«ããŸããã Python 2.7ã3.4ã3.5ã䜿çšããŠãDRF> = 3.3.1ããã³django> = 1.8ã«å¯ŸããŠãã¹ããããŠããŸãã
ããããšã
å°æ¥ã®èªè
ãžã®ã¡ã¢-Djangoã®æ¡ä»¶ä»ããã³ã¬ãŒã¿ãDRFãšäžç·ã«äœ¿çšããããã®å°ããªããã±ãŒãžãäœæããŸããã ãããã£ãŠãèå³ãããå ŽåïŒ
https://github.com/jozo/django-rest-framework-condition
æãåèã«ãªãã³ã¡ã³ã
æ®å¿µãªãããdrf-extensionsã®Etagæ©èœã«é¢ããããã©ã«ãã®å®è£ ãšããã¥ã¡ã³ãã¯åã«ééã£ãŠãããå±éºãªã»ã©ãã°ããããŸãã _response_ãå€æŽãããå Žåã§ã¯ãªãã_request_ãå€æŽãããå Žåã«Etagãå€æŽãããŸãã ããã¯ãŸãã«ãµãŒããŒãµã€ããã£ãã·ã³ã°ã«å¿ èŠãªãã®ã§ãããEtagã«ã¯å¿ èŠãªããã®ã§ãã