Django Rest Framework using dot in url

2019-04-07 01:26发布

问题:

Using DRF (Django Rest Framework) one of my models has an IP Address as primary key.

class VTE(models.Model):
  ipaddr = models.CharField(primary_key=True, max_length=16)
  ...

The problem is that the PK contains dots, and when the REST URL is assembled the dots are interpreted as formatting options by DRF instead as part of the primary key. See the error below, and the matching patterns.

Request URL:    http://192.168.10.121/api/vtes/172.25.128.29/

Using the URLconf defined in vtfx.urls, Django tried these URL patterns, in this order:

...
^api/ ^$ [name='api-root']
^api/ ^\.(?P<format>[a-z0-9]+)$ [name='api-root']
^api/ ^vtes/$ [name='vte-list']
^api/ ^vtes/\.(?P<format>[a-z0-9]+)$ [name='vte-list']   <---- HERE
^api/ ^vtes/(?P<pk>[^/.]+)/$ [name='vte-detail']
^api/ ^vtes/(?P<pk>[^/.]+)/\.(?P<format>[a-z0-9]+)$ [name='vte-detail']
...

it's wrong to use dots as part of the primary key? If not it's kind of incompatible with how the DRF router assembly the URLs.

Is there a DRF way to solve that?

UPDATE: context

api.py:
 class VTESerializer(serializers.ModelSerializer):
     class Meta:
         model = models.VTE

 class VTEViewSet(viewsets.ModelViewSet):
     queryset = models.VTE.objects.all()
     serializer_class = VTESerializer
     permission_classes = (permissions.AllowAny,)

urls.py:
 router = routers.DefaultRouter()
 router.register(r'vtes', api.VTEViewSet) 

 urlpatterns = patterns('',
     url(r'^api/', include(router.urls)),
     ...
 )

回答1:

First, it's ok to use the . (dot) in urls. See Can . (period) be part of the path part of an URL?

Second, the problem wasn't in the format option but in the Regex expression used to catch the Primary Key, which excludes the . (dot) and / (slash) per default.

(?P<pk>[^/.]+)  <--- This excludes the dots in the IP Address

The regex can be overrided in the ViewSet with lookup_value_regex. This is the new ViewSet that solves the problem:

class VTEViewSet(viewsets.ModelViewSet):
    lookup_value_regex = '[0-9.]+'      #Just add this line & change your Regex if needed
    queryset = models.VTE.objects.all()
    serializer_class = VTESerializer
    permission_classes = (permissions.AllowAny,)