Difference between reverse() and reverse_lazy() in Django
Question:
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?
Answers:
#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.
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,
- If we are using
success_url
we have to use reverse_lazy()
.
- If we are reversing inside a function we can use
reverse()
.
Now it is crystal clear.
Thanks CoffeeBasedLifeform 🙂
Because: Python class attributes are evaluated on the declaration.
Please read this link: Python class attributes are…
Just understand the difference:
reverse() returns a string
& reverse_lazy() returns an <object>
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.
difference:
- reverse() use in funcation & reverse_lazy() use in class.
- reverse() use in string & reverse_lazy() use in object
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.
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?
#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.
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,
- If we are using
success_url
we have to usereverse_lazy()
. - If we are reversing inside a function we can use
reverse()
.
Now it is crystal clear.
Thanks CoffeeBasedLifeform 🙂
Because: Python class attributes are evaluated on the declaration.
Please read this link: Python class attributes are…
Just understand the difference:
reverse() returns a string
& reverse_lazy() returns an <object>
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.
difference:
- reverse() use in funcation & reverse_lazy() use in class.
- reverse() use in string & reverse_lazy() use in object
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 ofreverse
.
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.