1

I wanted to create a model to store settings:

# name: string
# value: string
# desccription
# Example:
{
 "name": "currency.symbol",
 "value": "€",
 "description": "String to be attached at the end of some purchased values"
}

So I crated the model like this:

model.py

class Setting(models.Model):
    """Class to represent the global settings of the app that apply to all users."""

    name = models.CharField(primary_key=True, max_length=100)

    value = models.CharField(max_length=500, null=True)

    description = models.TextField(null=True)

    class Meta: # pylint: disable=too-few-public-methods
        """Class to represent metadata of the object."""
        ordering = ['pk']

    def __str__(self):
        """String for representing the Model object."""
        return str(self.name)

view.py

class SettingViewset(viewsets.ModelViewSet): # pylint: disable=too-many-ancestors
    """API Endpoint to return the list of settings"""
    queryset = Setting.objects.all()
    serializer_class = SettingSerializer
    pagination_class = None
    permission_classes = [IsAuthenticated, IsAdminUserOrReadOnly]

serializer.py

class SettingSerializer(serializers.ModelSerializer):
    """Serializer for Setting."""

    class Meta: # pylint: disable=too-few-public-methods
        """Class to represent metadata of the object."""
        model = Setting
        fields = '__all__'

This was working fine with settings like theme, language but now I though I also want to have a structure, like currency.name or currency.symbol and I am getting a 404 error because of the dot . using this paths:

<base_path>/api/v1/admin/setting/Theme/ # works
<base_path>/api/v1/admin/setting/currency.symbol/ # returns 404

I don't think is relevant, because I am getting the same from a curl, but I tried this in Angular where I have my FE and did not make any difference:

      const url = this.hostname + '/api/v1/admin/setting/' + encodeURI(element.name) + '/';

Is there an option to keep using the name as primary key and not having to switch to a new autoincremental pk?

Update: urls.py

"""
Django urls config for the  app.
"""
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from rest_framework.authtoken.views import obtain_auth_token
from myapp import views

# Create a router and register our viewsets with it.
router = DefaultRouter()
# ...
router.register(r'admin/setting', views.SettingViewset)
# ...

urlpatterns = [
    path('auth/', views.AuthToken.as_view(), name='auth'),
    #...
    path('', include(router.urls)),
]

nck
  • 1,047
  • 8
  • 19
  • I don't understand why there is a 404 with currency.symbol, cause [dots are authorized in path](https://stackoverflow.com/questions/6777274/can-urls-contain-dots-in-the-path-part/6777322) :/ Can you add the view declaration in `urls.py` ? In the frontend, have you tried Not to encodeURI the dot ? – Romain May 17 '21 at 05:39
  • @Romain thank you. I just added the relevant part of the `urls.py`. I did try without the encoded version, and also just the curl using `insomnia` to test the REST API. – nck May 17 '21 at 09:17
  • > /api/v1/admin/setting/currency.symbol/ # returns 404 I don't understand what it is you are showing there. Show your DB entity and endpoint you want to use that returns 404. – Tom Wojcik May 17 '21 at 14:47
  • @TomWojcik I mean for example `curl -v http://localhost:8000/api/v1/admin/setting/currency.symbol/` and I want to return the json which is represented at the beginning of the question. – nck May 17 '21 at 19:10

1 Answers1

2

I've found it out ! In fact, by default, « The router will match lookup values containing any characters except slashes and period characters. ». All you have to do is to extend this default with an appropriate regex value :

class SettingViewset(viewsets.ModelViewSet):
    ...
    lookup_value_regex = '[\w.]+'  # it should be enough :)

You can see that in this other stackoverflow question too.

Romain
  • 173
  • 2
  • 7