Skip to content
17 changes: 10 additions & 7 deletions datatableview/columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
import django
from django.db import models
from django.db.models import Model, Manager, Q
from django.db.models.fields import FieldDoesNotExist
from django.core.exceptions import ObjectDoesNotExist
from django.utils.encoding import smart_text
from django.core.exceptions import ObjectDoesNotExist, FieldDoesNotExist
from django.utils.encoding import smart_str
from django.utils.safestring import mark_safe
try:
from django.forms.utils import flatatt
Expand All @@ -24,6 +23,7 @@
from django.utils.encoding import python_2_unicode_compatible
except ImportError:
from .compat import python_2_unicode_compatible
from .compat import get_rel_to

import six
import dateutil.parser
Expand Down Expand Up @@ -56,8 +56,11 @@ def get_column_for_modelfield(model_field):
# that as the real field. It is possible that a ForeignKey points to a model with table
# inheritance, however, so we need to traverse the internal OneToOneField as well, so this will
# climb the 'pk' field chain until we have something real.
while model_field.rel:
model_field = model_field.rel.to._meta.pk
rel_to = get_rel_to(model_field)
while rel_to:
model_field = rel_to._meta.pk
rel_to = get_rel_to(model_field)

for ColumnClass, modelfield_classes in COLUMN_CLASSES:
if isinstance(model_field, tuple(modelfield_classes)):
return ColumnClass
Expand Down Expand Up @@ -112,13 +115,13 @@ def __init__(self, label=None, sources=None, processor=None, source=None,

self.name = None # Set outside, once the Datatable can put it there
if label is not None:
label = smart_text(label)
label = smart_str(label)
self.sources = sources or [] # TODO: Process for real/virtual
if not isinstance(self.sources, (tuple, list)):
self.sources = [self.sources]
self.separator = separator
self.label = label
self.empty_value = smart_text(empty_value)
self.empty_value = smart_str(empty_value)
self.localize = localize
self.sortable = sortable
self.visible = visible
Expand Down
10 changes: 10 additions & 0 deletions datatableview/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,13 @@ def escape_uri_path(path):
if django_escape_uri_path:
return django_escape_uri_path(path)
return escape(path)

def get_rel_to(model_field):
# as per https://gist.github.com/ofalk/404f9f637b7b4520e26dcdd520dbe248
rel_to = None
if hasattr(model_field, 'rel'):
rel_to = model_field.rel.to if model_field.rel else None
elif hasattr(model_field, 'remote_field'):
rel_to = model_field.remote_field.model if model_field.remote_field else None
return rel_to

4 changes: 2 additions & 2 deletions datatableview/datatables.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
except ImportError:
pass

from django.db.models.fields import FieldDoesNotExist
from django.core.exceptions import FieldDoesNotExist
from django.template.loader import render_to_string
from django.db.models import QuerySet
try:
from django.utils.encoding import force_text
except ImportError:
from django.utils.encoding import force_unicode as force_text
from django.utils.encoding import force_str as force_text
try:
from django.utils.encoding import python_2_unicode_compatible
except ImportError:
Expand Down
13 changes: 7 additions & 6 deletions datatableview/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
pass

from django.db import models
from django.db.models.fields import FieldDoesNotExist
from django.core.exceptions import FieldDoesNotExist
from django.utils.text import smart_split
try:
from django.db.models.related import RelatedObject
Expand All @@ -15,7 +15,7 @@
from django.db.models.fields.related import RelatedField
USE_RELATED_OBJECT = False

from .compat import get_field
from .compat import get_field, get_rel_to

MINIMUM_PAGE_LENGTH = 1
DEFAULT_EMPTY_VALUE = ""
Expand Down Expand Up @@ -63,7 +63,7 @@
def resolve_orm_path(model, orm_path):
"""
Follows the queryset-style query path of ``orm_path`` starting from ``model`` class. If the
path ends up referring to a bad field name, ``django.db.models.fields.FieldDoesNotExist`` will
path ends up referring to a bad field name, ``django.core.exceptions.FieldDoesNotExist`` will
be raised.

"""
Expand Down Expand Up @@ -96,11 +96,12 @@ def get_model_at_related_field(model, attr):
# -- Django <1.8 mode
return field.model

if hasattr(field, 'rel') and field.rel: # Forward/m2m relationship
return field.rel.to
rel_to = get_rel_to(field)
if rel_to: # Forward/m2m relationship
return rel_to

if hasattr(field, 'field'): # Forward GenericForeignKey in Django 1.6+
return field.field.rel.to
return get_rel_to(field.field)

raise ValueError("{0}.{1} ({2}) is not a relationship field.".format(model.__name__, attr,
field.__class__.__name__))
Expand Down
6 changes: 5 additions & 1 deletion datatableview/views/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@

log = logging.getLogger(__name__)

# https://stackoverflow.com/a/70419609
def is_ajax(request):
return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'


class DatatableJSONResponseMixin(object):
def dispatch(self, request, *args, **kwargs):
if request.is_ajax() or getattr(request, request.method).get('ajax') == 'true':
if is_ajax(request) or getattr(request, request.method).get('ajax') == 'true':
datatable = self.get_datatable()
datatable.configure()
if request.method == datatable.config['request_method']:
Expand Down