Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita8cbfca

Browse files
committed
Allow usage of Django 2.x path in SimpleRouter
1 parent98e56e0 commita8cbfca

File tree

3 files changed

+123
-9
lines changed

3 files changed

+123
-9
lines changed

‎docs/api-guide/routers.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ The router will match lookup values containing any characters except slashes and
173173
lookup_field = 'my_model_id'
174174
lookup_value_regex = '[0-9a-f]{32}'
175175

176+
By default the URLs created by`SimpleRouter` uses_regexs_ to build urls. This behavior can be modified by setting the`use_regex_path` argument to`False` when instantiating the router, in this case[path converters][path-convertes-topic-reference] are used. For example:
177+
178+
router = SimpleRouter(use_regex_path=False)
179+
180+
**Note**:`use_regex_path=False` only works with Django 2.x or above, since this feature was introduced in 2.0.0. See[release note][simplified-routing-release-note]
181+
176182
##DefaultRouter
177183

178184
This router is similar to`SimpleRouter` as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional`.json` style format suffixes.
@@ -340,3 +346,5 @@ The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions
340346
[drf-extensions-customizable-endpoint-names]:https://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name
341347
[url-namespace-docs]:https://docs.djangoproject.com/en/1.11/topics/http/urls/#url-namespaces
342348
[include-api-reference]:https://docs.djangoproject.com/en/2.0/ref/urls/#include
349+
[simplified-routing-release-note]:https://docs.djangoproject.com/en/2.0/releases/2.0/#simplified-url-routing-syntax
350+
[path-convertes-topic-reference]:https://docs.djangoproject.com/en/2.0/topics/http/urls/#path-converters

‎rest_framework/routers.py‎

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
fromcollectionsimportOrderedDict,namedtuple
1818

1919
fromdjango.core.exceptionsimportImproperlyConfigured
20-
fromdjango.urlsimportNoReverseMatch,re_path
20+
fromdjango.urlsimportNoReverseMatch,path,re_path
2121

2222
fromrest_frameworkimportviews
2323
fromrest_framework.responseimportResponse
@@ -123,8 +123,28 @@ class SimpleRouter(BaseRouter):
123123
),
124124
]
125125

126-
def__init__(self,trailing_slash=True):
126+
def__init__(self,trailing_slash=True,use_regex_path=True):
127127
self.trailing_slash='/'iftrailing_slashelse''
128+
ifuse_regex_path:
129+
self._base_regex='(?P<{lookup_prefix}{lookup_url_kwarg}>{lookup_value})'
130+
self._default_regex='[^/.]+'
131+
self._url_conf=re_path
132+
else:
133+
self._base_regex='<{lookup_value}:{lookup_prefix}{lookup_url_kwarg}>'
134+
self._default_regex='path'
135+
self._url_conf=path
136+
# remove regex characters from routes
137+
_routes= []
138+
forrouteinself.routes:
139+
url_param=route.url
140+
ifurl_param[0]=='^':
141+
url_param=url_param[1:]
142+
ifurl_param[-1]=='$':
143+
url_param=url_param[:-1]
144+
145+
_routes.append(route._replace(url=url_param))
146+
self.routes=_routes
147+
128148
super().__init__()
129149

130150
defget_default_basename(self,viewset):
@@ -213,13 +233,12 @@ def get_lookup_regex(self, viewset, lookup_prefix=''):
213233
214234
https://github.com/alanjds/drf-nested-routers
215235
"""
216-
base_regex='(?P<{lookup_prefix}{lookup_url_kwarg}>{lookup_value})'
217236
# Use `pk` as default field, unset set. Default regex should not
218237
# consume `.json` style suffixes and should break at '/' boundaries.
219238
lookup_field=getattr(viewset,'lookup_field','pk')
220239
lookup_url_kwarg=getattr(viewset,'lookup_url_kwarg',None)orlookup_field
221-
lookup_value=getattr(viewset,'lookup_value_regex','[^/.]+')
222-
returnbase_regex.format(
240+
lookup_value=getattr(viewset,'lookup_value_regex',self._default_regex)
241+
returnself._base_regex.format(
223242
lookup_prefix=lookup_prefix,
224243
lookup_url_kwarg=lookup_url_kwarg,
225244
lookup_value=lookup_value
@@ -253,8 +272,12 @@ def get_urls(self):
253272
# controlled by project's urls.py and the router is in an app,
254273
# so a slash in the beginning will (A) cause Django to give
255274
# warnings and (B) generate URLS that will require using '//'.
256-
ifnotprefixandregex[:2]=='^/':
257-
regex='^'+regex[2:]
275+
ifnotprefix:
276+
ifself._url_confispath:
277+
ifregex[0]=='/':
278+
regex=regex[1:]
279+
elifregex[:2]=='^/':
280+
regex='^'+regex[2:]
258281

259282
initkwargs=route.initkwargs.copy()
260283
initkwargs.update({
@@ -264,7 +287,7 @@ def get_urls(self):
264287

265288
view=viewset.as_view(mapping,**initkwargs)
266289
name=route.name.format(basename=basename)
267-
ret.append(re_path(regex,view,name=name))
290+
ret.append(self._url_conf(regex,view,name=name))
268291

269292
returnret
270293

‎tests/test_routers.py‎

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
fromrest_framework.decoratorsimportaction
1111
fromrest_framework.responseimportResponse
1212
fromrest_framework.routersimportDefaultRouter,SimpleRouter
13-
fromrest_framework.testimportAPIRequestFactory,URLPatternsTestCase
13+
fromrest_framework.testimport (
14+
APIClient,APIRequestFactory,URLPatternsTestCase
15+
)
1416
fromrest_framework.utilsimportjson
1517

1618
factory=APIRequestFactory()
@@ -75,9 +77,25 @@ def regex_url_path_detail(self, request, *args, **kwargs):
7577
returnResponse({'pk':pk,'kwarg':kwarg})
7678

7779

80+
classUrlPathViewSet(viewsets.ViewSet):
81+
@action(detail=False,url_path='list/<int:kwarg>')
82+
defurl_path_list(self,request,*args,**kwargs):
83+
kwarg=self.kwargs.get('kwarg','')
84+
returnResponse({'kwarg':kwarg})
85+
86+
@action(detail=True,url_path='detail/<int:kwarg>')
87+
defurl_path_detail(self,request,*args,**kwargs):
88+
pk=self.kwargs.get('pk','')
89+
kwarg=self.kwargs.get('kwarg','')
90+
returnResponse({'pk':pk,'kwarg':kwarg})
91+
92+
7893
notes_router=SimpleRouter()
7994
notes_router.register(r'notes',NoteViewSet)
8095

96+
notes_path_router=SimpleRouter(use_regex_path=False)
97+
notes_path_router.register('notes',NoteViewSet)
98+
8199
kwarged_notes_router=SimpleRouter()
82100
kwarged_notes_router.register(r'notes',KWargedNoteViewSet)
83101

@@ -90,6 +108,9 @@ def regex_url_path_detail(self, request, *args, **kwargs):
90108
regex_url_path_router=SimpleRouter()
91109
regex_url_path_router.register(r'',RegexUrlPathViewSet,basename='regex')
92110

111+
url_path_router=SimpleRouter(use_regex_path=False)
112+
url_path_router.register('',UrlPathViewSet,basename='path')
113+
93114

94115
classBasicViewSet(viewsets.ViewSet):
95116
deflist(self,request,*args,**kwargs):
@@ -459,6 +480,68 @@ def test_regex_url_path_detail(self):
459480
assertjson.loads(response.content.decode())== {'pk':pk,'kwarg':kwarg}
460481

461482

483+
classTestUrlPath(URLPatternsTestCase,TestCase):
484+
client_class=APIClient
485+
urlpatterns= [
486+
path('path/',include(url_path_router.urls)),
487+
path('example/',include(notes_path_router.urls))
488+
]
489+
490+
defsetUp(self):
491+
RouterTestModel.objects.create(uuid='123',text='foo bar')
492+
RouterTestModel.objects.create(uuid='a b',text='baz qux')
493+
494+
deftest_create(self):
495+
new_note= {
496+
'uuid':'foo',
497+
'text':'example'
498+
}
499+
response=self.client.post('/example/notes/',data=new_note)
500+
assertresponse.status_code==201
501+
assertresponse['location']=='http://testserver/example/notes/foo/'
502+
assertresponse.data== {"url":"http://testserver/example/notes/foo/","uuid":"foo","text":"example"}
503+
assertRouterTestModel.objects.filter(uuid='foo').exists()
504+
505+
deftest_retrieve(self):
506+
response=self.client.get('/example/notes/123/')
507+
assertresponse.status_code==200
508+
assertresponse.data== {"url":"http://testserver/example/notes/123/","uuid":"123","text":"foo bar"}
509+
510+
deftest_list(self):
511+
response=self.client.get('/example/notes/')
512+
assertresponse.status_code==200
513+
assertresponse.data== [
514+
{"url":"http://testserver/example/notes/123/","uuid":"123","text":"foo bar"},
515+
{"url":"http://testserver/example/notes/a%20b/","uuid":"a b","text":"baz qux"},
516+
]
517+
518+
deftest_update(self):
519+
updated_note= {
520+
'text':'foo bar example'
521+
}
522+
response=self.client.patch('/example/notes/123/',data=updated_note)
523+
assertresponse.status_code==200
524+
assertresponse.data== {"url":"http://testserver/example/notes/123/","uuid":"123","text":"foo bar example"}
525+
526+
deftest_delete(self):
527+
response=self.client.delete('/example/notes/123/')
528+
assertresponse.status_code==204
529+
assertnotRouterTestModel.objects.filter(uuid='123').exists()
530+
531+
deftest_list_extra_action(self):
532+
kwarg=1234
533+
response=self.client.get('/path/list/{}/'.format(kwarg))
534+
assertresponse.status_code==200
535+
assertjson.loads(response.content.decode())== {'kwarg':kwarg}
536+
537+
deftest_detail_extra_action(self):
538+
pk='1'
539+
kwarg=1234
540+
response=self.client.get('/path/{}/detail/{}/'.format(pk,kwarg))
541+
assertresponse.status_code==200
542+
assertjson.loads(response.content.decode())== {'pk':pk,'kwarg':kwarg}
543+
544+
462545
classTestViewInitkwargs(URLPatternsTestCase,TestCase):
463546
urlpatterns= [
464547
path('example/',include(notes_router.urls)),

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp