Code

Opened 6 years ago

Closed 6 years ago

Last modified 3 years ago

#8110 closed (fixed)

Admin interface: 'long' object has no attribute 'isdigit'

Reported by: anonymous Owned by: nobody
Component: contrib.admin Version: 1.0
Severity: Keywords:
Cc: piranha@… Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

Time and again there is a error in the admin interface.
We noiced that the error occures just when option Debug = false,
and by changing it to true everything function normaly.

Traceback (most recent call last):

File "/opt/elec/python/django/core/handlers/base.py", line 87, in get_response
response = callback(request, *callback_args, **callback_kwargs)

File "/opt/elec/python/django/contrib/admin/sites.py", line 142, in root
return self.index(request)

File "/opt/elec/python/django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)

File "/opt/elec/python/django/contrib/admin/sites.py", line 331, in index
context_instance=template.RequestContext(request)

File "/opt/elec/python/django/shortcuts/__init__.py", line 18, in render_to_response
return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)

File "/opt/elec/python/django/template/loader.py", line 107, in render_to_string
return t.render(context_instance)

File "/opt/elec/python/django/template/__init__.py", line 176, in render
return self.nodelist.render(context)

File "/opt/elec/python/django/template/__init__.py", line 751, in render
bits.append(self.render_node(node, context))

File "/opt/elec/python/django/template/__init__.py", line 764, in render_node
return node.render(context)

File "/opt/elec/python/django/template/loader_tags.py", line 97, in render
return compiled_parent.render(context)

File "/opt/elec/python/django/template/__init__.py", line 176, in render
return self.nodelist.render(context)

File "/opt/elec/python/django/template/__init__.py", line 751, in render
bits.append(self.render_node(node, context))

File "/opt/elec/python/django/template/__init__.py", line 764, in render_node
return node.render(context)

File "/opt/elec/python/django/template/loader_tags.py", line 97, in render
return compiled_parent.render(context)

File "/opt/elec/python/django/template/__init__.py", line 176, in render
return self.nodelist.render(context)

File "/opt/elec/python/django/template/__init__.py", line 751, in render
bits.append(self.render_node(node, context))

File "/opt/elec/python/django/template/__init__.py", line 764, in render_node
return node.render(context)

File "/opt/elec/python/django/template/loader_tags.py", line 24, in render
result = self.nodelist.render(context)

File "/opt/elec/python/django/template/__init__.py", line 751, in render
bits.append(self.render_node(node, context))

File "/opt/elec/python/django/template/__init__.py", line 764, in render_node
return node.render(context)

File "/opt/elec/python/django/contrib/admin/templatetags/log.py", line 17, in render
if not self.user.isdigit():

AttributeError: 'long' object has no attribute 'isdigit'

Attachments (2)

isdigitpatch.diff (671 bytes) - added by julianb 6 years ago.
Since isdigit only works with strings we make it a string
8110.diff (778 bytes) - added by kmtracey 6 years ago.
Allow for render being called more than once for an AdminLogNode

Download all attachments as: .zip

Change History (32)

comment:1 Changed 6 years ago by ElliottM

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

Can you post more information about your model and ModelAdmin?

comment:2 Changed 6 years ago by julianb

I can confirm that the error appears from time to time.

However, the site on which it appears for me is still using the old admin. Would be nice to know what the original poster is using.

comment:3 Changed 6 years ago by anonymous

I use newforms-admin. What additional Information do you need.

comment:4 Changed 6 years ago by julianb

  • milestone set to 1.0
  • Triage Stage changed from Unreviewed to Accepted

comment:5 Changed 6 years ago by julien

Hmmm, this is quite difficult to debug. Can you please provide a test case or any code that reproduces the error?
I've never seen that error before, and at first glance it is hard to know what's causing it...

comment:6 Changed 6 years ago by jacob

  • Resolution set to worksforme
  • Status changed from new to closed

I can't reproduce this, either. We'll need more information -- your model, ModelAdmin, etc. Marking worksforme; feel free to reopen if you can provide more information.

comment:7 Changed 6 years ago by julianb

I updated the site where I got the error to newforms admin. Since the update, the error never occurred anymore, but it occurred very rarely nonetheless. I have no idea how to reproduce or why it still occurred for anonymous in his newforms admin.

comment:8 Changed 6 years ago by julianb

Huh, got the error on newforms admin today... strange!

Changed 6 years ago by julianb

Since isdigit only works with strings we make it a string

comment:9 follow-up: Changed 6 years ago by julianb

  • Has patch set
  • Resolution worksforme deleted
  • Status changed from closed to reopened
  • Version changed from SVN to 1.0

I don't know what causes the error, somehow self.user can be long sometimes. If I continuously refresh my admin page the error appears frequently now. The patch fixes it.

comment:10 in reply to: ↑ 9 Changed 6 years ago by kmtracey

Replying to julianb:

I don't know what causes the error, somehow self.user can be long sometimes. If I continuously refresh my admin page the error appears frequently now. The patch fixes it.

It would be good to understand how it comes about that sometimes self.user is a long. Looking at the code, one way would be if render was called more than once for the same AdminLogNode, since render itself rebinds self.user from the user variable name to the identified user's ID. I'm going to attach a patch that avoids doing that. Could you try running with it and seeing if it also prevents the error? If so, then the next question will by why is render being called multiple times for the same AdminLogNode? I thought that would only happen if the template tag (get_admin_log in this case) was placed inside a loop in the template. It's not in a loop in the admin/index.html file. Have you customized this file or do you have another template that uses get_admin_log in a loop? (I am unable to recreate the error myself by refreshing the admin page to the limits of my patience.)

Changed 6 years ago by kmtracey

Allow for render being called more than once for an AdminLogNode

comment:11 Changed 6 years ago by julianb

Your patch works. No errors while frequently reloading. (Btw, it seems to always enter the if condition, otherwise user_id would not be set, or maybe that's what you wanted to check.)

I have not customized anything so that the tag is in a loop...

I'm also getting the error on two different sites on different servers. One running mod_python and the other mod_wsgi.

comment:12 Changed 6 years ago by kmtracey

Hmm, OK. So it looks like render is getting called multiple times for the same AdminLogNode. I don't understand why that's happening, and can't recreate it (previously I had tried under the development server, now I have tried under mod_wsgi as well). I don't suppose you recall anything you changed in your configuration around 9/3 that could be relevant? Are you, like the original reporter, also running with debug off?

(I expected it was always entering the if condition, because the only use of that templatetag that I can find within Django passes in a variable name, not a user number. It seems to have been written to allow more options on calling it than are currently exercised by Django code.)

comment:13 Changed 6 years ago by kmtracey

Another report, also noted to happen only with debug turned off: http://groups.google.com/group/django-users/browse_thread/thread/f335b27a221b6720

comment:14 Changed 6 years ago by oxyum

May be someone apply this patch to trunk and branch 1.0.x? This bug is really annoing and second patch really solve problem!

comment:15 Changed 6 years ago by kmtracey

The problem with just applying the existing patch is it essentially hides a problem we don't yet understand the root cause of. I haven't been able to recreate the problem -- if you are seeing this regularly could you provide any feedback on your configuration including web server, middleware, caching setup, etc. that might help us figure out what is at the root of the problem? In the meantime the patch in the ticket can also be used by anyone experiencing the problem as a stopgap to avoid the error. But we'd like to better understand what is going on here before just patching over the error in the code base.

comment:16 Changed 6 years ago by oxyum

So, if it really can help. I'm install byteflow blog engine (http://byteflow.su/) on linux via fastcgi. webserver nginx. fastcgi via sockets. Django from trunk (today).

Some time later, I'm try to debug it.

comment:17 Changed 6 years ago by Konstantinos Metaxas <kmetaxas@…>

I too have installed byteflow and am experiencing the same problem. I'm on Apache/mod_python sitting behind an nginx reverse proxy. Django 9084. I'm using SQLite.
I'm not getting this error on manage.py runserver.

comment:18 follow-up: Changed 6 years ago by ramiro

To the people reorting this: Is it possible these conditions are being met in the cases you see the reported error?:

  • Django is being executed in multiple threads (that would be Apache worker MPM + mod_python, or mod_wsgi in daemon mode)
  • The admin app is being used at the same time by another user

comment:19 in reply to: ↑ 18 ; follow-up: Changed 6 years ago by oxyum

Replying to ramiro:

To the people reorting this: Is it possible these conditions are being met in the cases you see the reported error?:

  • Django is being executed in multiple threads (that would be Apache worker MPM + mod_python, or mod_wsgi in daemon mode)

No, I'm using preforked fastcgi flup server over WSGI.

  • The admin app is being used at the same time by another user

No, im my installation Django has only one staff user.

But sometimes it happens when very fast refresh admin page.

comment:20 in reply to: ↑ 19 Changed 6 years ago by nekron

Replying to oxyum:

Replying to ramiro:

To the people reorting this: Is it possible these conditions are being met in the cases you see the reported error?:

I can confirm this bug with byteflow and lighttpd 1.4.20. Django runs as fastcgi process with the following startup:

python manage.py runfcgi host=127.0.0.1 port=8888 

Lighty has been configured like this:

fastcgi.server = (
              "/byteflow.fcgi" => (
              "main" => (
              "min-procs" => 1,
              "max-procs" => 1,
              "host" => "127.0.0.1",
              "port" => 8888,
              #"socket" => "/usr/src/byteflow/socket" + var.PID,
              "check-local" => "disable",
              #"bin-path" => "/usr/src/byteflow/byteflow.fcgi"
                        )
                                  )
                 )

The wsgi environment shows this(copy from traceback mail):

<WSGIRequest GET:<QueryDict: {}>, 
POST:<QueryDict: {}>, 
COOKIES:{'sessionid': '6a0863baf921667e81217b534fd5c02b'}, 
META:{'DOCUMENT_ROOT': '/var/www/novh/', 
'GATEWAY_INTERFACE': 'CGI/1.1', 
'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 
'HTTP_ACCEPT_ENCODING': 'gzip,deflate', 
'HTTP_ACCEPT_LANGUAGE': 'en-us,en;q=0.5', 
'HTTP_CONNECTION': 'keep-alive', 
'HTTP_COOKIE': 'sessionid=6a0863baf921667e81217b534fd5c02b', 
'HTTP_HOST': 'blog.noordsee.de', 
'HTTP_IF_MODIFIED_SINCE': 'Tue, 14 Oct 2008 18:15:38 GMT', 
'HTTP_IF_NONE_MATCH': '"de6ded5f6a2828356f158f21599e28d9"', 
'HTTP_KEEP_ALIVE': '300',
'HTTP_REFERER': 'http://xxxxxxxxxxxxxxxx/admin/blog/post/', 
'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16', 
'PATH_INFO': u'/admin/', 
'PATH_TRANSLATED': '/var/www/novh//admin/', 
'QUERY_STRING': '', 
'REDIRECT_STATUS': '200', 
'REDIRECT_URI': '/byteflow.fcgi/admin/',
'REMOTE_ADDR': '84.141.xxxxx.xxxx', 
'REMOTE_PORT': '44745', 
'REQUEST_METHOD': 'GET', 
'REQUEST_URI': '/admin/', 
'SCRIPT_FILENAME': '/var/www/novh/byteflow.fcgi', 
'SCRIPT_NAME': u'', 
'SERVER_ADDR': '85.214.xxxx.xxxxx', 
'SERVER_NAME': 'xxxxxxxxxxxxx.de', 
'SERVER_PORT': '80', 
'SERVER_PROTOCOL': 'HTTP/1.1', 
'SERVER_SOFTWARE': 'lighttpd/1.4.20', 
'wsgi.errors': <flup.server.fcgi_base.TeeOutputStream object at 0xb63266cc>, 
'wsgi.input': <flup.server.fcgi_base.InputStream object at 0xb64a2c2c>,
'wsgi.multiprocess': True,
'wsgi.multithread': False,
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0)}> 

Last traceback call shows:

Original Traceback (most recent call last): File "/usr/local/lib/python2.5/site-packages/django/template/debug.py", line 71, 
in render_node result = node.render(context) File "/usr/local/lib/python2.5/site-packages/django/contrib/admin/templatetags/log.py", 
line 17, in render if not self.user.isdigit(): 
AttributeError: 'int' object has no attribute 'isdigit' 

I am using the current 1.0 release (no trunk or 1.0.x branch) as Django installation. Lighty, python 2.5 and flup are compiled from current sourcecode releases.

If you set min and max processes on lighty to one you can get error 500 real fast switching from byteflows blog (edit some blog) to "home". Do this a few times and each time you call "/admin" you get error 500. This is only happening if the app runs in Debug=False mode. The blog runs on a VServer Virtuozzo system with (while for testing) SQLite3 as db backend.

Hope this helps!

comment:21 Changed 6 years ago by nekron

I did today evening some debugging and found a bugfix / workaround for this behaviour.

Original file: django/contrib/admin/templatetags/log.py

   def render(self, context):
        if self.user is None:
            context[self.varname] = LogEntry.objects.all().select_related()[:self.limit]
        else:
            if not self.user.isdigit():
                self.user = context[self.user].id
            context[self.varname] = LogEntry.objects.filter(user__id__exact=self.user).select_related()[:self.limit]
        return ''

Look at the line "if not self.user.isdigit()". We assume that self.user must be an unicode string containing a digit value. If this is not the case we get the current user id from the context dictionary and save it as self.user. And now here it happends that we get the id which is an integer value and no string. So we ovwerwrite the current string with an integer. The next time the render method is called we try to call isdigit() on an integer object which crashes the admin interface (error 500). What I did to fix this is that I put str(..) around the context[self.user].id. So I make sure that self.user is a string, however containing an integer values so isdigit() will become true.

Fixed render method looks like:

   def render(self, context):
        if self.user is None:
            context[self.varname] = LogEntry.objects.all().select_related()[:self.limit]
        else:
            if not self.user.isdigit():
                self.user = str(context[self.user].id)
            context[self.varname] = LogEntry.objects.filter(user__id__exact=self.user).select_related()[:self.limit]
        return ''

comment:22 follow-ups: Changed 6 years ago by kmtracey

Thanks for the info. We already know how to prevent the error from occurring by either the approach you mention or just avoiding updating self.user inside render (see the patches already on the ticket). The reason neither has been applied to the code base is we'd really like to understand WHY render is being called more than once for this template tag. The template tag isn't in a loop or anything, so it is not clear why render() is being called multiple times for the same node. That's the mystery we'd like to understand before checking in a fix.

I am confused by your traceback included above. The one that starts "Original traceback" and starts with "django/template/debug.py", line 71,"...yet this bug you and everyone else reports only occurs with DEBUG set to False. There is also no mention of django/template/debug.py in the original posted traceback, nor any indication that there are multiple exceptions raised...so all in all I find that traceback info confusing.

comment:23 in reply to: ↑ 22 Changed 6 years ago by nekron

Replying to kmtracey:

I am confused by your traceback included above. The one that starts "Original traceback" and starts with "django/template/debug.py", line 71,"...yet this bug you and everyone else reports only occurs with DEBUG set to False. There is also no mention of django/template/debug.py in the original posted traceback, nor any indication that there are multiple exceptions raised...so all in all I find that traceback info confusing.

To get my traceback I did uncomment the ADMIN settings variable in settings_local.py (from the byteflow installation). Thus I got the error 500 traceback mailed. The complete traceback reads (I have slighty formatted the output because c&p from my webclient did not copy carridge returns):

Traceback (most recent call last): File "/usr/local/lib/python2.5/site-packages/django/core/handlers/base.py", line 86, in get_response response = callback(request, *callback_args, **callback_kwargs) 

File "/usr/local/lib/python2.5/site-packages/django/contrib/admin/sites.py", line 145, in root return self.index(request) 

File "/usr/local/lib/python2.5/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func response = view_func(request, *args, **kwargs) 

File "/usr/local/lib/python2.5/site-packages/django/contrib/admin/sites.py", line 319, in index context_instance=template.RequestContext(request) 

File "/usr/local/lib/python2.5/site-packages/django/shortcuts/__init__.py", line 18, in render_to_response return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs) 

File "/usr/local/lib/python2.5/site-packages/django/template/loader.py", line 107, in render_to_string return t.render(context_instance) 

File "/usr/local/lib/python2.5/site-packages/django/template/__init__.py", line 176, in render return self.nodelist.render(context) 

File "/usr/local/lib/python2.5/site-packages/django/template/__init__.py", line 768, in render bits.append(self.render_node(node, context)) 

File "/usr/local/lib/python2.5/site-packages/django/template/debug.py", line 71, in render_node result = node.render(context) 

File "/usr/local/lib/python2.5/site-packages/django/template/loader_tags.py", line 97, in render return compiled_parent.render(context) 

File "/usr/local/lib/python2.5/site-packages/django/template/__init__.py", line 176, in render return self.nodelist.render(context) 

File "/usr/local/lib/python2.5/site-packages/django/template/__init__.py", line 768, in render bits.append(self.render_node(node, context)) 

File "/usr/local/lib/python2.5/site-packages/django/template/debug.py", line 71, in render_node result = node.render(context) 

File "/usr/local/lib/python2.5/site-packages/django/template/loader_tags.py", line 97, in render return compiled_parent.render(context) 

File "/usr/local/lib/python2.5/site-packages/django/template/__init__.py", line 176, in render return self.nodelist.render(context)

File "/usr/local/lib/python2.5/site-packages/django/template/__init__.py", line 768, in render bits.append(self.render_node(node, context)) 

File "/usr/local/lib/python2.5/site-packages/django/template/debug.py", line 71, in render_node result = node.render(context) 

File "/usr/local/lib/python2.5/site-packages/django/template/loader_tags.py", line 24, in render result = self.nodelist.render(context) 

File "/usr/local/lib/python2.5/site-packages/django/template/__init__.py", line 768, in render bits.append(self.render_node(node, context)) 

File "/usr/local/lib/python2.5/site-packages/django/template/debug.py", line 81, in render_node raise wrapped TemplateSyntaxError: Caught an exception while rendering: 'int' object has no attribute 'isdigit' 

Original Traceback (most recent call last): File "/usr/local/lib/python2.5/site-packages/django/template/debug.py", line 71, in render_node result = node.render(context) 

File "/usr/local/lib/python2.5/site-packages/django/contrib/admin/templatetags/log.py", line 17, in render if not self.user.isdigit(): AttributeError: 'int' object has no attribute 'isdigit' 

I know that casting from int to string is just a workaround and there must be a bug somewhere in the template stuff or otherwise.

comment:24 in reply to: ↑ 22 Changed 6 years ago by oxyum

Replying to kmtracey:

I am confused by your traceback included above. The one that starts "Original traceback" and starts with "django/template/debug.py", line 71,"...yet this bug you and everyone else reports only occurs with DEBUG set to False. There is also no mention of django/template/debug.py in the original posted traceback, nor any indication that there are multiple exceptions raised...so all in all I find that traceback info confusing.

That's small bug in byteflow default configuration. When we set DEBUG to False, DEBUG_TEMPLATES doesn't set to False. But I'm trying set DEBUG_TEMPLATES to False - it's doesn't matter.

comment:25 Changed 6 years ago by kmtracey

OK, has anyone hit this WITHOUT having byteflow installed? Because after looking into byteflow I understand why it happens there: byteflow replaces Django's django.template.loader.get_template with its own cached_get_template which re-uses compiled templates:

TEMPLATE_CACHE = {}
def cached_get_template(template_name):
    global TEMPLATE_CACHE
    t = TEMPLATE_CACHE.get(template_name, None)
    if not t or settings.DEBUG:
        source, origin = loader.find_template_source(template_name)
        t = loader.get_template_from_string(source, origin, template_name)
        TEMPLATE_CACHE[template_name] = t
    return t

That code also explains why this has only been observed with DEBUG set to False.

So, there is no mysterious re-use of compiled templates within Django itself, it's being done by the app. Assuming that's a legit thing for an app to do (?) the simple fix of avoiding changing self within render fixes it.

(Actually it strikes me as not a very legit thing to be doing because if multiple apps do what byteflow does:

from django import template
from lib.template_loaders import cached_get_template
template.loader.get_template = cached_get_template

the last one "wins" and the custom code of the others gets ignored. But there it is doing it, and the result is the admin interface generates a 500 error, which is ugly. Also if/when some form of "supported" template caching does get implemented, that would also run into trouble here. So I do think it has exposed a real bug in the render() function that we need to fix, despite not particularly liking what byteflow has done here. And I wonder how many other template tags may have similar issues with re-use of compiled versions.)

comment:26 Changed 6 years ago by anonymous

I run several Django sites and have *only* hit this bug on my Byteflow blog.

comment:27 Changed 6 years ago by SmileyChris

Yeah, this is a bug in the admin template tag which is notable when caching templates. The patch in #9154 fixes it (but really, it should be a separate ticket). There are also other issues around things like the cycle tag which store state on the node rather than the context.

comment:28 Changed 6 years ago by piranha

  • Cc piranha@… added

despite not particularly liking what byteflow has done here.

Yeah, it's not that nice, but redoing cached_get_template to use original loader.get_template doesn't help there. Is there any ideas what I can do except disabling caching (though I can go this way because anyway I'll add real caching in - probably near - future).

comment:29 Changed 6 years ago by kmtracey

  • Resolution set to fixed
  • Status changed from reopened to closed

(In [9233]) Fixed #8110 -- Allow for AdminLogNode's render to be called more than once.

comment:30 Changed 3 years ago by jacob

  • milestone 1.0 deleted

Milestone 1.0 deleted

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.