问题
I have a Django (version 1.5) application that is getting this Oracle error:
ORA-01461: can bind a LONG value only for insert into a LONG column
After doing some database debugging, it appears that the problem is caused by doing an INSERT on the DJANGO_SESSION table with a long (roughly 2.5k characters) Unicode string value for SESSION_DATA (data type NCLOB).
I have been told by a coworker that the problem does not occur on UPDATEs but only on INSERTs, because the Django database code for UPDATEs contains logic to (I'm paraphrasing here) split the database write into manageable chunks, but for some reason that logic is missing for INSERTs.
Armed with this information, I figured I could work around it by writing a small piece of custom middleware that does an immediate request.session.save() with a (presumably) empty value for SESSION_DATA and then later on when the session data is set, that would cause an UPDATE instead of an INSERT.
However, it appears not to be that simple. If I insert my custom middleware in settings.MIDDLEWARE_CLASSES above the entry for 'django.contrib.sessions.middleware.SessionMiddleware', the request object doesn't have the session attribute yet, and I get this error:
AttributeError: 'WSGIRequest' object has no attribute 'session'
And if I insert my middleware after the SessionMiddleware entry, it's too late in the process and I get the original ORA-01461 error.
So, is there a way to work around the ORA-01461 error using middleware? Or at all?
回答1:
Check this Django bug: https://code.djangoproject.com/ticket/11487
I've also hit this issue (on Django 1.6) and verified it by changing the 4000
character check to say 1000
here https://github.com/django/django/blob/stable/1.6.x/django/db/backends/oracle/base.py#L699. Unfortunately as it is Django core code this is a difficult thing to fix locally.
I have not tested, but AFAICT the issue at least has been attempted to be fixed in Django 1.7 - https://github.com/django/django/blob/stable/1.7.x/django/db/backends/oracle/base.py#L781
I guess if you cannot upgrade to 1.7 (if it indeed fixes the issue), you could find the places where the session data grows too large and work around by storing it elsewhere - if that is an option.
回答2:
The workaround that worked for me is:
- subclass unicode and set custom property
input_size
to cx_Oracle.CLOB - use new custom unicode to set django ORM model object CLOB property
input_size
property will be used by oracle adapter when binding oracle vars (see https://github.com/django/django/blob/stable/1.6.x/django/db/backends/oracle/base.py#L796 - i.e. research usage of input_size property in the module)
This should work:
class clob_unicode(unicode):
def __new__(cls, *args, **kwargs):
ret = unicode.__new__(cls, *args, **kwargs)
import cx_Oracle
ret.input_size = cx_Oracle.CLOB # to set proper bind var type
return ret
Usage:
django_object.clob_variable = clob_unicode(u"some very long string")
django_object.save() # will use .input_size to set oracle bind var type
See also this answer to see discussion on how to properly subclass and init string/unicode subclass object (initialized in __new__
method and not in __init__
).
回答3:
what worked for me was creating and saving a session object before the session data gets very big:
from django.contrib.sessions.backends.db import SessionStore
def view(request):
s = SessionStore()
s.create()
request.session = s
s.save()
# rest of the code here
subsequent queries are executed as update commands and not as inserts by oracle.
来源:https://stackoverflow.com/questions/21197523/django-error-ora-01461-workaround