Question

Does Django flattening response after process_response?

I have this model (simplified version):

class Ticket(models.Model):
    uuid = UUIDField(primary_key = True)
    is_active = models.BooleanField(default = False)
    remains = models.IntegerField(default = 2)

    def __unicode__(self):
        return '%s' % (self.is_active)

and middleware (simplified):

class TicketMiddleware(object):
    def process_request(self, request):
        # ...
        try:
            request.ticket = Ticket.objects.get(uuid_exact=request.GET['ticket'], is_active=True)
            print '%s %d' % (request.ticket.is_active, request.ticket.remains)
            # first case:
            return HttpResponse(request.ticket)
            # second case:
            #return HttpResponse(request.ticket.is_active)
        except Ticket.DoesNotExists:
            return HttpResponseForbidden()

    def process_response(self, request, response):
        if request.ticket is not None and request.ticket.remains > 0:
            request.ticket.remains -= 1
            if request.ticket.remains == 0:
                request.ticket.is_active = False
            request.ticket.save()
        return response

In first case I got forbidden page on fourth request:

   RESPONSE     PRINT
1: True         True 2
2: False        True 1
3: False        True 1
4: Forbidden

In second case I got forbidden page on third request:

   RESPONSE     PRINT
1: True         True 2
2: True         True 1
3: Forbidden

And this second case is the way I want. Why I must stringify my model first for properly flow? What I misunderstood?

django.VERSION = (1, 2, 3, 'final', 0)

EDIT

I found the problem. In real code I use foreign key for django.contrib.auth.models.User and unicoding my Ticket model with username of associated User:

class Ticket(models.Model):
    # ...
    user = models.ForeignKey(User)

    def __unicode__(self):
        # this works as expected
        return u'%s' % (self.is_active)
        # with this I have unexpected third step in first case
        # but I dont understand why
        #return u'%s %s' % (self.user.username, self.is_active)
Was it helpful?

Solution

I really doubt that the first case displays two times False. I've made a simple test with your code, and it displays for the first case:

1: True
2: False
3: Forbidden

which is the expected behavior. As long as the process_response modifies the is_active data and saves the ticket on the second call, I don't see how a third call can return False... Unless the ticket.remains is modified somehow. With the code shown here, I can't see how it is possible that process_response to save the ticket with is_active=False, and on subsequent request process_request not to redirect to Forbidden... Which Django version do you use? Print also ticket.remains in the __unicode__() to see how it changes ...

The reason that you get False in the second call with 1st case is that you pass a model instance

return HttpResponse(request.ticket)

The ticket.__unicode__() is being called after the process_response is being called, during the final writing of the contents of HttpResponse() to the client. And since in the first scenario ticket.is_active is being modified in process_response, when ticket.__unicode__() is finally called, it gets the modified value of ticket.is_active which is False.

In the second scenario the __unicode__ is called explicitly in process_request and evaluated to 'True'.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top