preface

Django REST Framework (DRF) is a powerful and flexible toolkit for building Web APIs. DRF has its own way of defining routes, through the Router Register method, which contains a parameter called Basename. Let’s take a look at the DRF routing system by looking at this parameter.

What questions to explore

The reason for this article is that when using the Django REST Framework, you often see basename used when configuring routing. Like the namespace parameter in the include function in Django and the name parameter in the path, what’s the truth? Let’s look at this article with the following three questions. 👇

  • Django REST frameworkIn thebasename å’Œ DjangoIn thenamespaceParameters andnameWhat do parameters matter?
  • When does it need to be addedbasename, when is it not neededbasename?
  • If the defaultbasenameParameters,Django REST FrameworkHow do you deal with it?

Preliminary knowledge required

According to the Django framework for REST of official documentation is introduced: Routers – English document | Routers – Chinese documents

To answer the first question, you need to know the router.register, which you can find in this article
When using DRF, choose router.register or urlpatterns path.

Where does it work?

Break points:

View variable information

You can see that the basename parameter affects the name attribute of the URLPattern, which is concatenated

Practical application:

Basename is mostly used for reverse parsing and the general process is URL routing -> view but sometimes you need to get the URL from the view in the view such as a redirect, and then you can do soft coding with the basename

When basename is default

If the basename parameter is default, then the queryset property of the viewset parameter is used. If neither the basename parameter nor the queryset property of the viewset parameter is specified, then an error is reported!

 File "C:\Users\17293\Desktop\Coder\Python\Django\twitter\twitter\urls.py", line 17, in <module>
    router.register('/api/accounts/', AccountViewSet)
  File "C:\Users\17293\AppData\Local\Programs\Python\Python39\lib\site-packages\rest_framework\routers.py", line 54, in register
    basename = self.get_default_basename(viewset)
  File "C:\Users\17293\AppData\Local\Programs\Python\Python39\lib\site-packages\rest_framework\routers.py", line 137, in get_default_basename
    assert queryset is not None, '`basename` argument not specified, and could ' \
AssertionError: `basename` argument not specified, and could not automatically determine the name from the viewset, as it does not have a `.queryset` attribute.

Routing registration in the Django REST Framework works like this. Routing registration in the REST Framework works like this

router = routers.SimpleRouter()
router.register('/api/accounts/', AccountViewSet, basename='accounts')

The basename parameter can be written or not written. This does not mean that there is no basename. As mentioned earlier, basename is required for reverse parsing, so the default basename is given a default value.

You can see that the register method’s basename parameter has a default value of None, but this is not the final default. As you can see from the source code, when we do not customize this parameter, it defaults to None. When it is None, we get the value of the basename from the viewset parameter

class BaseRouter:
    def __init__(self):
        self.registry = []

    def register(self, prefix, viewset, basename=None):
        if basename is None:
            basename = self.get_default_basename(viewset)
        self.registry.append((prefix, viewset, basename))

        # invalidate the urls cache
        if hasattr(self, '_urls'):
            del self._urls

If you’re wondering why we use the SimpleRouter class Register for our custom route in urls.py, and why is the source code shown here BaseRouter class? The answer is because the SimpleRouter class inherits the register method from the BaseRouter class, which does not override the register method

Basename = self.get_default_basename(viewset) = self.get_default_basename(viewset); Take a look at the self.get_default_basename method 👇

Let’s start with the BaseRouter class’s get_default_basename method, emmmm, which is an abstract method

def get_default_basename(self, viewset):
    """
    If `basename` is not specified, attempt to automatically determine
    it from the viewset.
    """
    raise NotImplementedError('get_default_basename must be overridden')

One concept was mentioned above: abstract methods

When it comes to abstract methods, the superclass defines a method, and when you call the method, you throw an error. If I adjust it, it will report an error. Okay? With what? Why is that? What to do? The correct way to open an abstract method is to inherit from the parent class in a subclass and override the abstract method in a subclass, that is, to define a custom method in a subclass that does not throw an error. So why would a superclass make such an abstract method? Because this abstract method is important (not abstract method is important, this method, this method called abstract method, which throws an error when it is called is important, not the word “abstract method” or whatever the word “abstract method” refers to is important!!) Since it is to rewrite to use, it is better not to define directly in the parent class, directly write in the child class is not good? Can also! Absolutely! So why even define this method that throws an error? Answer is to occupy pit space, remind write subclass person, don’t forget this important method!! One more thing is that IDEs such as VSCode and PyChew will give you syntax errors if you don’t inherit. After all, you called get_default_basename in the register method of the BaseRouter class. And then you don’t define what get_default_basename is?? Then these IDEs will give you an error message!! So we need to make a pit and define a false get_default_basename summary:

  • Tell anyone who wants to subclass to remember to override this abstract method (important)
  • Pit space, code integrity (not that important)

Take a look at the get_default_basename method of the SimpleRouter class

def get_default_basename(self, viewset):
    """
    If `basename` is not specified, attempt to automatically determine
    it from the viewset.
    """
    queryset = getattr(viewset, 'queryset', None)

    assert queryset is not None, '`basename` argument not specified, and could ' \
        'not automatically determine the name from the viewset, as ' \
        'it does not have a `.queryset` attribute.'

    return queryset.model._meta.object_name.lower()

Getattr (viewset, ‘queryset’, None); getattr(‘queryset’, None); getattr(‘queryset’, None); Flip back!

  • router.register('/api/accounts/', AccountViewSet, basename='accounts')
  • def register(self, prefix, viewset, basename=None):
  • basename = self.get_default_basename(viewset)

So, the ViewSet object is the AccountViewSet class, and this class doesn’t come with a QuerySet property, it has to be defined by a human, it has to be defined by a programmer, it has to be defined by you sitting in front of your computer

Simple, right? But I’m sure there are a lot of newbies reading this article, so I want to geta little bit more basic. If you don’t know what getattr does, right? Then you really should search the engine more! What? You don’t even know how to use a search engine! Well, let me tell you that searching for keywords like “python getattr usage” will give you the answer you’re looking for

If the queryset is not None, then pass. If the queryset is None, then throw an error!

What! You don’t even know assert!! You are simply a 🤡

At this point it becomes clear that if you don’t customize either the basename property or the queryset property of the AccountViewSet class, you will get an error and say goodbye to you

What if there is no custom basename property, but the queryset property of the AccountViewSet class is defined? In this case, the QuerySet class is used as the Basename, but there is a small problem that the Basename is a string, and the QuerySet is usually a QuerySet class, or a subclass of Models.Model. Obviously the class can not be treated as a string ah, this time also need further processing! Read on!

queryset.model._meta.object_name.lower()

Look at the.lower() method, which is a built-in string method that converts strings to lowercase characters. Object_name is a string object that holds the name of the AccountViewSet class, which becomes AccountViewSet when converted to lowercase

Don’t understand,
lowerMethod, please refer to
Python3 lower () method