django-pyodbc-azure
django-pyodbc-azure copied to clipboard
Cursors in 2.0.1.0: directly addressing cursor doesn't return column as property
This used to work before 2.0.1.0:
from django.db import connections
cursor = connections['sql_server_connection'].cursor()
cursor.execute("SELECT 'ASDF' AS fake_column")
row = cursor.fetchone()
print(row.fake_column)
It now returns AttributeError: 'tuple' object has no attribute 'fake_column'.
As a work around, assigning the cursor to an object does work as intended:
from django.db import connections
cursor = connections['mssqlotis'].cursor()
res = cursor.execute("SELECT 'ASDF' AS fake_column")
row = res.fetchone()
print(row.fake_column)
This returns ASDF as expected. I haven't had a chance to dig deeper, and it isn't the end of the world, but thought it would be worth raising.
It is not a Django feature but a pyodbc feature described in the wiki below. https://github.com/mkleehammer/pyodbc/wiki/Row
- Values can be accessed by column name.
You can't utilize the feature in Django 2 unfortunately. In Django 2, the auth module assumes that rows from database adapters are hashable and django-pyodbc-azure needs to return a row as a tuple instead of a raw pyodbc Row object. As a side effect of this, rows from django-pyodbc-azure do not have column name properties now. https://github.com/michiya/django-pyodbc-azure/blob/2.0.3.0/sql_server/pyodbc/base.py#L578
We will see errors like the following if the backend returns a raw pyodbc Row object to the auth module.
======================================================================
ERROR: test_unrelated_model_lookups_forwards (migrations.test_executor.ExecutorTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\michiya\workspace\Django-2.0.3\django\test\testcases.py", line 202, in __call__
self._pre_setup()
File "C:\Users\michiya\workspace\Django-2.0.3\django\test\testcases.py", line 817, in _pre_setup
emit_post_migrate_signal(verbosity=0, interactive=False, db=db_name)
File "C:\Users\michiya\workspace\Django-2.0.3\django\core\management\sql.py", line 51, in emit_post_migrate_signal
**kwargs
File "C:\Users\michiya\workspace\Django-2.0.3\django\dispatch\dispatcher.py", line 178, in send
for receiver in self._live_receivers(sender)
File "C:\Users\michiya\workspace\Django-2.0.3\django\dispatch\dispatcher.py", line 178, in <listcomp>
for receiver in self._live_receivers(sender)
File "C:\Users\michiya\workspace\Django-2.0.3\django\contrib\auth\management\__init__.py", line 71, in create_permissions
"content_type", "codename"
TypeError: unhashable type: 'pyodbc.Row'
If you want to retireve data from a row using a column name, now you will need to implement it on your own following the Django document below. https://docs.djangoproject.com/en/2.0/topics/db/sql/#executing-custom-sql-directly
FWIW, one could use a collections.namedtuple to keep this cake while eating it. Since the row already carries the cursor_description, this wouldn't be very costly.