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

Commitb1d231f

Browse files
committed
Allow usage of Django 2.x path in SimpleRouter
1 parentde497a9 commitb1d231f

File tree

3 files changed

+128
-10
lines changed

3 files changed

+128
-10
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: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
fromdjango.urlsimportNoReverseMatch
2222

2323
fromrest_frameworkimportviews
24+
fromrest_framework.compatimportpath
2425
fromrest_framework.responseimportResponse
2526
fromrest_framework.reverseimportreverse
2627
fromrest_framework.schemasimportSchemaGenerator
@@ -80,7 +81,7 @@ def urls(self):
8081

8182

8283
classSimpleRouter(BaseRouter):
83-
84+
8485
routes= [
8586
# List route.
8687
Route(
@@ -124,8 +125,28 @@ class SimpleRouter(BaseRouter):
124125
),
125126
]
126127

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

131152
defget_default_basename(self,viewset):
@@ -214,13 +235,12 @@ def get_lookup_regex(self, viewset, lookup_prefix=''):
214235
215236
https://github.com/alanjds/drf-nested-routers
216237
"""
217-
base_regex='(?P<{lookup_prefix}{lookup_url_kwarg}>{lookup_value})'
218238
# Use `pk` as default field, unset set. Default regex should not
219239
# consume `.json` style suffixes and should break at '/' boundaries.
220240
lookup_field=getattr(viewset,'lookup_field','pk')
221241
lookup_url_kwarg=getattr(viewset,'lookup_url_kwarg',None)orlookup_field
222-
lookup_value=getattr(viewset,'lookup_value_regex','[^/.]+')
223-
returnbase_regex.format(
242+
lookup_value=getattr(viewset,'lookup_value_regex',self._default_regex)
243+
returnself._base_regex.format(
224244
lookup_prefix=lookup_prefix,
225245
lookup_url_kwarg=lookup_url_kwarg,
226246
lookup_value=lookup_value
@@ -230,6 +250,7 @@ def get_urls(self):
230250
"""
231251
Use the registered viewsets to generate a list of URL patterns.
232252
"""
253+
assertself._url_confisnotNone,'SimpleRouter requires Django 2.x when using path'
233254
ret= []
234255

235256
forprefix,viewset,basenameinself.registry:
@@ -254,8 +275,12 @@ def get_urls(self):
254275
# controlled by project's urls.py and the router is in an app,
255276
# so a slash in the beginning will (A) cause Django to give
256277
# warnings and (B) generate URLS that will require using '//'.
257-
ifnotprefixandregex[:2]=='^/':
258-
regex='^'+regex[2:]
278+
ifnotprefix:
279+
ifself._url_confispath:
280+
ifregex[0]=='/':
281+
regex=regex[1:]
282+
elifregex[:2]=='^/':
283+
regex='^'+regex[2:]
259284

260285
initkwargs=route.initkwargs.copy()
261286
initkwargs.update({
@@ -265,7 +290,7 @@ def get_urls(self):
265290

266291
view=viewset.as_view(mapping,**initkwargs)
267292
name=route.name.format(basename=basename)
268-
ret.append(url(regex,view,name=name))
293+
ret.append(self._url_conf(regex,view,name=name))
269294

270295
returnret
271296

‎tests/test_routers.py‎

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
fromcollectionsimportnamedtuple
22

3+
importdjango
34
importpytest
45
fromdjango.conf.urlsimportinclude,url
56
fromdjango.core.exceptionsimportImproperlyConfigured
@@ -8,11 +9,13 @@
89
fromdjango.urlsimportresolve,reverse
910

1011
fromrest_frameworkimportpermissions,serializers,viewsets
11-
fromrest_framework.compatimportget_regex_pattern
12+
fromrest_framework.compatimportget_regex_pattern,path
1213
fromrest_framework.decoratorsimportaction
1314
fromrest_framework.responseimportResponse
1415
fromrest_framework.routersimportDefaultRouter,SimpleRouter
15-
fromrest_framework.testimportAPIRequestFactory,URLPatternsTestCase
16+
fromrest_framework.testimport (
17+
APIClient,APIRequestFactory,URLPatternsTestCase
18+
)
1619
fromrest_framework.utilsimportjson
1720

1821
factory=APIRequestFactory()
@@ -77,9 +80,25 @@ def regex_url_path_detail(self, request, *args, **kwargs):
7780
returnResponse({'pk':pk,'kwarg':kwarg})
7881

7982

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

99+
notes_path_router=SimpleRouter(use_regex_path=False)
100+
notes_path_router.register('notes',NoteViewSet)
101+
83102
kwarged_notes_router=SimpleRouter()
84103
kwarged_notes_router.register(r'notes',KWargedNoteViewSet)
85104

@@ -92,6 +111,9 @@ def regex_url_path_detail(self, request, *args, **kwargs):
92111
regex_url_path_router=SimpleRouter()
93112
regex_url_path_router.register(r'',RegexUrlPathViewSet,basename='regex')
94113

114+
url_path_router=SimpleRouter(use_regex_path=False)
115+
url_path_router.register('',UrlPathViewSet,basename='path')
116+
95117

96118
classBasicViewSet(viewsets.ViewSet):
97119
deflist(self,request,*args,**kwargs):
@@ -463,6 +485,69 @@ def test_regex_url_path_detail(self):
463485
assertjson.loads(response.content.decode())== {'pk':pk,'kwarg':kwarg}
464486

465487

488+
@pytest.mark.skipif(django.VERSION< (2,0),reason='Django version < 2.0')
489+
classTestUrlPath(URLPatternsTestCase,TestCase):
490+
client_class=APIClient
491+
urlpatterns= [
492+
path('path/',include(url_path_router.urls)),
493+
path('example/',include(notes_path_router.urls))
494+
]ifpathelse []
495+
496+
defsetUp(self):
497+
RouterTestModel.objects.create(uuid='123',text='foo bar')
498+
RouterTestModel.objects.create(uuid='a b',text='baz qux')
499+
500+
deftest_create(self):
501+
new_note= {
502+
'uuid':'foo',
503+
'text':'example'
504+
}
505+
response=self.client.post('/example/notes/',data=new_note)
506+
assertresponse.status_code==201
507+
assertresponse['location']=='http://testserver/example/notes/foo/'
508+
assertresponse.data== {"url":"http://testserver/example/notes/foo/","uuid":"foo","text":"example"}
509+
assertRouterTestModel.objects.filter(uuid='foo').first()isnotNone
510+
511+
deftest_retrieve(self):
512+
response=self.client.get('/example/notes/123/')
513+
assertresponse.status_code==200
514+
assertresponse.data== {"url":"http://testserver/example/notes/123/","uuid":"123","text":"foo bar"}
515+
516+
deftest_list(self):
517+
response=self.client.get('/example/notes/')
518+
assertresponse.status_code==200
519+
assertresponse.data== [
520+
{"url":"http://testserver/example/notes/123/","uuid":"123","text":"foo bar"},
521+
{"url":"http://testserver/example/notes/a%20b/","uuid":"a b","text":"baz qux"},
522+
]
523+
524+
deftest_update(self):
525+
updated_note= {
526+
'text':'foo bar example'
527+
}
528+
response=self.client.patch('/example/notes/123/',data=updated_note)
529+
assertresponse.status_code==200
530+
assertresponse.data== {"url":"http://testserver/example/notes/123/","uuid":"123","text":"foo bar example"}
531+
532+
deftest_delete(self):
533+
response=self.client.delete('/example/notes/123/')
534+
assertresponse.status_code==204
535+
assertRouterTestModel.objects.filter(uuid='123').first()isNone
536+
537+
deftest_list_extra_action(self):
538+
kwarg=1234
539+
response=self.client.get('/path/list/{}/'.format(kwarg))
540+
assertresponse.status_code==200
541+
assertjson.loads(response.content.decode())== {'kwarg':kwarg}
542+
543+
deftest_detail_extra_action(self):
544+
pk='1'
545+
kwarg=1234
546+
response=self.client.get('/path/{}/detail/{}/'.format(pk,kwarg))
547+
assertresponse.status_code==200
548+
assertjson.loads(response.content.decode())== {'pk':pk,'kwarg':kwarg}
549+
550+
466551
classTestViewInitkwargs(URLPatternsTestCase,TestCase):
467552
urlpatterns= [
468553
url(r'^example/',include(notes_router.urls)),

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp