Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make reverse_lazy() lazy for arguments too?

Introduction

For a particular need (django-jqgrid), I define a class (representing a Grid) where I have to provide an url attribute. Since I use only reversed URLs (nothing statically defined), I use reverse_lazy() for this matter.

This works great as long as I do not have to pass an argument to reverse_lazy(). However, if I want to make that Grid specific to an object (here, a DocumentSet), this url arguments needs to be also specific to that object, and so I need to provide an argument to reverse_lazy().

On runtime, I can access the DocumentSet because I defined it as an attribute on the object, and I ensure that the first function called on this Grid takes this object as an argument, and sets the attribute correctly.

First attempt

Code

I tried using this code :

class DocumentGrid(JqGrid):
    documentset = None
    model = Document
    url = reverse_lazy('document-grid-handler', kwargs = {'pk' : documentset.id, })

    def get_queryset(self, request):
        return self.documentset.documents
    def get_json(self, request, documentset):
        self.documentset = documentset
        return super(DocumentGrid, self).get_json(request)

Error

But of course, this fails when the file is imported because NoneType object has no attribute 'id'.

Second attempt

Code

So I tried using django.utils.functional.lazy(), by adding this trivial and stupid function to my model :

def get_id(self):
    return self.id

And by using this code :

class DocumentGrid(JqGrid):
    documentset = DocumentSet
    model = Document
    url = reverse_lazy('document-grid-handler', kwargs = {'pk' : lazy(documentset.get_id, int), })

    def get_queryset(self, request):
        return self.documentset.documents
    def get_json(self, request, documentset):
        self.documentset = documentset
        return super(DocumentGrid, self).get_json(request)

Error

Now the file is imported correctly, without Django complaining. However, at runtime, I get the following error :

Reverse for 'document-grid-handler' with arguments '()' and keyword arguments '{'pk': <function get_id at 0x1a07410>}' not found.

Conclusion

Is this the correct way to go, but I'm making a small mistake ? Or did I misunderstood everything about lazy evaluation and should take a completely different approach and rewrite reverse_lazy() ?

like image 911
pistache Avatar asked Mar 29 '13 12:03

pistache


1 Answers

django.utils.functional.lazy() return you a lazy-evaluated callable, in other words it gives you a function that will return lazy value.

See example:

l = lazy(lambda : 42, str)
unicode(l)  # returns u'<function <lambda> at 0x3a5bcf8>'
lazy_value = l()
unicode(lazy_value)  # returns u'42'

However if you are replacing the self.documentset field in your get_json function you can get wrong results with your lazy call. As it will use an old DocumentSet.

For JqGrid there is another solution. JqGrid provides you the get_url method, by default it just returns self.url. But you can override this behaviour:

class DocumentGrid(JqGrid):
    documentset = None
    model = Document
    url = None

    def get_queryset(self, request):
        return self.documentset.documents

    def get_json(self, request, documentset):
        self.documentset = documentset
        return super(DocumentGrid, self).get_json(request)

    def get_url(self):
        return reverse('document-grid-handler', kwargs = {'pk' : self.documentset.id, })
like image 73
Igor Avatar answered Nov 15 '22 02:11

Igor