Difference between reverse() and reverse_lazy() in Django
Asked Answered
S

7

58

I understand that we can use reverse() in FBV and reverse_lazy() in CBV. I understand that we have to use reverse_lazy() in CBV as the urls are not loaded when the file is imported (Ref: Reverse_lazy and URL Loading?)

What I don't understand is:

How are the urls loaded when we call reverse from the FBV? As when we import the views at the top of the urls.py in a Django app, urlpatterns list is yet to be evaluated. How does reverse() for FBV work but not for CBV?

Sonde answered 7/2, 2018 at 17:8 Comment(0)
I
52
#importme.py
def a():
    print("FUNCTION HELLO")

class B():
    print("CLASS HELLO") 
    

>>> import importme
>>> CLASS HELLO

Edit: The reason: The class creation process involves executing the body of the class.

The class body is executed (approximately) as exec(body, globals(), namespace). [...] Once the class namespace has been populated by executing the class body, the class object is created by calling metaclass(name, bases, namespace, **kwds).

https://docs.python.org/3/reference/datamodel.html?highlight=metaclass#executing-the-class-body



My original answer text. You can ignore it - I'm just leaving it in because mirek's comment was a direct response to it:

Class attributes are evaluated on import. The answer to when or exactly how that happens, resides within the depths of python's import system.

Izolaiztaccihuatl answered 7/2, 2018 at 18:59 Comment(1)
>> resides within the depths of python's import system -- I think it resides in the principle of class not in the "depth of..". The defined class must have its attributes.Urbas
S
66

Consider these two ways of defining the success_url. The first is commented out, the second is the function:

class NewJobCBV(LoginRequiredMixin, CreateView):
    template_name = 'company/job.html'
    form_class = newJobForm
    # success_url = reverse_lazy('newJob')

    def get_success_url(self, **kwargs):
        return reverse("newJob")

@CoffeeBasedLifeform : you are right, class attributes are evaluated on import, I checked after reading your answer. So,

  1. If we are using success_url we have to use reverse_lazy().
  2. If we are reversing inside a function we can use reverse().

Now it is crystal clear.

Thanks CoffeeBasedLifeform :)

Sonde answered 9/2, 2018 at 7:12 Comment(1)
Is there any reason to use reverse then? Just use reverse_lazy all the time and don't worry if the urls are loaded. Does reverse have any benefits?Slatternly
I
52
#importme.py
def a():
    print("FUNCTION HELLO")

class B():
    print("CLASS HELLO") 
    

>>> import importme
>>> CLASS HELLO

Edit: The reason: The class creation process involves executing the body of the class.

The class body is executed (approximately) as exec(body, globals(), namespace). [...] Once the class namespace has been populated by executing the class body, the class object is created by calling metaclass(name, bases, namespace, **kwds).

https://docs.python.org/3/reference/datamodel.html?highlight=metaclass#executing-the-class-body



My original answer text. You can ignore it - I'm just leaving it in because mirek's comment was a direct response to it:

Class attributes are evaluated on import. The answer to when or exactly how that happens, resides within the depths of python's import system.

Izolaiztaccihuatl answered 7/2, 2018 at 18:59 Comment(1)
>> resides within the depths of python's import system -- I think it resides in the principle of class not in the "depth of..". The defined class must have its attributes.Urbas
N
17

Reverse_lazy is, as the name implies, a lazy implementation of the reverse URL resolver. Unlike the traditional reverse function, reverse_lazy won't execute until the value is needed.

It is useful because it prevent Reverse Not Found exceptions when working with URLs that may not be immediately known.

Why do we need it? It's needed because, Class attributes are evaluated on import and at that time Reverse method will return 'Reverse Not Found'. Later upon need, at the time of its execution, all the necessary code snippets will be executed already, to give a valid URL.

Nelsen answered 23/6, 2021 at 14:32 Comment(0)
V
5

Just understand the difference:

reverse() returns a string & reverse_lazy() returns an <object>

Velodrome answered 17/12, 2020 at 6:47 Comment(2)
How does this explain the weird behavior? Genuinely asking.Ascender
explanation here: https://mcmap.net/q/329203/-difference-between-reverse-and-reverse_lazy-in-djangoSnappish
P
5

difference:

  1. reverse() use in function & reverse_lazy() use in class.
  2. reverse() returns a string & reverse_lazy() returns an object.
Polder answered 4/11, 2021 at 15:26 Comment(0)
S
2

Both reverse and reverse_lazy functions have the same objective: to generate a URL based on the inputs (like a named url).

The difference is in when they are evaluated:

  • reverse is used to generate a URL at the time the function or method is called.

Let's put an example:

A function:

#views.py

from django.urls import reverse

def my_view(request):
    url = reverse('url-name', args=[1])
    # do something with the URL

The reverse function is evaluated at the moment the my_view function is called: it generates a URL for the named url url-name, passing the integer 1 as an argument.

A method:

#models.py 

from django.db import models

class Post(models.Model):
    #rest of the class

    def get_absolute_url(self):
        return reverse("blog:post_detail", kwargs={"pk": self.pk})

Again, the reverse function is evaluated at the moment the get_absolute_url method is called.

What is important in both this cases is that at the moment the reverse function is called, it already has the information of the URLConf, so it can easily find the url pattern.

  • reverse_lazy is also used to generate a URL, but it defers the moment of evaluation until it is actually needed. That's way it is a lazily evaluated version of reverse.

You may be wondering why...

There are moments in your code when you need to use a URL reversal, but you don't exactly know if the URLConf has already been loaded. If you use reverse when the URLConf hasn't already been loaded, you would get an error.

This error occurs because reverse needs to know the URL patterns defined in the project's URLConf in order to generate the correct URL. If the URLConf has not been loaded, reverse will not be able to find the necessary information to generate the URL and will raise an exception.

Example:

A class based view:

from django.views.generic.edit import DeleteView
from django.urls import reverse_lazy

class PostDeleteView(DeleteView):
   model = Post
   template_name = "post_delete.html"
   success_url = reverse_lazy('home')

When you defined the success_url (a class attribute) you used reverse_lazy instead of reverse. This is because class attributes are evaluated at the time when the class definition is executed. This means that the values assigned to class attributes are determined when the class is defined, and not when instances of the class are created.

When you import the class, the Python interpreter executes the class definition from top to bottom and that includes the class atrtributes like success_url, but there are chances that the URLConf hadn't been loaded.

To not have an error, instead of using reverse we use reverse_lazy, which delays the actual call to the URLConf to the moment is needed and not when the class attribute is evaluated.

Snappish answered 6/3, 2023 at 16:12 Comment(0)
B
1

Because: Python class attributes are evaluated on the declaration. Please read this link: Python class attributes are...

Bother answered 4/9, 2020 at 15:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.