Middleware: usar el modelo en process_request y modificarlo en process_response

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)

EDITAR

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)

preguntado el 31 de julio de 12 a las 10:07

¿Estás seguro de que quisiste decir? request.ticket -= 1 y no request.ticket.remains -= 1. Igual por if request.ticket == 0? -

1 Respuestas

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 modifica el 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 en la __unicode__() to see how it changes ...

La razón por la que obtienes False in the second call with 1st case is that you pass a model instance

return HttpResponse(request.ticket)

Los programas 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, Cuando ticket.__unicode__() is finally called, it gets the modified value of ticket.is_active cual es False.

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

Respondido 31 Jul 12, 12:07

I change some details in code samples publicied in my question regards to reality, but there still fourth-request-forbidden behaviour in first case. Thanks to you I know now that ticket is stringified as last after response processing, but I dont understand what is doing on third request in first case. Help. - kbec

Where do you get the response? Do you log somewhere? Or load with a browser and get the response there? My only guess is that the middleware's method process_response is called twice for some reason(absolutely possible). But not sure why... I mean that it doesn't make sense to return twice False 1 to browser - it simply means that process_response is not called at all. But why. .. - tisho

I have only one view never called, because my middleware always return some HttpResponse (for test purposes only). print are logged with apache. There is nothing special. I'm avoid using user.username in ticket's model __unicode__ method and it works, but I dont know why this have meaning. - kbec

Ahaa... here it is: https://github.com/django/django/blob/master/django/core/handlers/base.py https://github.com/django/django/blob/stable/1.2.x/django/core/handlers/base.py. It seems that there is a big difference in the way 1.2 and 1.4 process the middleware's process_response. In 1.4 everything works fine. But it seems for some reason the process_response is not called for the second page load in your case, and it doesn't decrement the remains valor... - tisho

Great research, thanks. I must think about upgrade my server. - kbec

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.