Django DetailView with custom identifier (not pk): Field 'id' expected a number but got 'XQ1wfrpiLVAkjAUL'

Viewed 504

I am using Django 3.2

I have a model and GCBV defined as follows:

class Foo(models.Model):
    identifier = models.CharField(max_length=16, db_index=True)
    # ... 

class FooDetailView(DetailView):
    model = Foo
    template_name = 'foo_detail.html'
    pk_url_kwarg = 'identifier'

    # TODO, need to add logic of flagged items etc. to custom Manager and use that instead
    queryset = Foo.objects.filter(is_public=True)

    # # FIXME: This is a hack, just to demo
    # def get_object(self, queryset=None):
    #     objs = Foo.objects.filter(identifier=self.request.GET.get('identifier', 0))
    #     if objs:
    #         return objs[0]
    #     else:
    #         obj = Foo()
    #         return obj

In urls.py, I have the following statement:

path('foo/view/<str:identifier>/', FooDetailView.as_view(), name='foo-detail'),

Why is Django expecting a number (even though I have explicitly specified a string - AND also provided a pk_url_kwarg parameter)?

How do I fix this?

1 Answers

The pk_url_kwarg attribute is only used by Django to get the correct kwarg from the views kwargs. In the end Django will still make a filter of the form queryset.filter(pk=pk) (Where pk = self.kwargs.get(self.pk_url_kwarg)). Instead if you want to perform filtering on a custom field you should set slug_url_kwarg and slug_field:

class FooDetailView(DetailView):
    model = Foo
    template_name = 'foo_detail.html'
    slug_url_kwarg = 'identifier'
    slug_field = 'identifier'

    # TODO, need to add logic of flagged items etc. to custom Manager and use that instead
    queryset = Foo.objects.filter(is_public=True)
Related