Add support for Django 1.2 (though 1.1 *might* work)

This commit is contained in:
Bradley Ayers 2012-09-18 14:54:23 +10:00
parent 5cf596510c
commit 0df5c4d50f
10 changed files with 128 additions and 106 deletions

View File

@ -6,7 +6,10 @@ from .columns import (BooleanColumn, Column, CheckBoxColumn, DateColumn,
URLColumn)
from .config import RequestConfig
from .utils import A, Attrs
from .views import SingleTableMixin, SingleTableView
try:
from .views import SingleTableMixin, SingleTableView
except ImportError:
pass
__version__ = "0.11.0"

View File

@ -7,8 +7,8 @@ from django.template import RequestContext
from django.template.loader import get_template
from django.utils.encoding import StrAndUnicode
import warnings
from .utils import (Accessor, AttributeDict, cached_property, OrderBy,
OrderByTuple, segment, Sequence)
from .utils import (Accessor, AttributeDict, cached_property, build_request,
OrderBy, OrderByTuple, segment, Sequence)
from .rows import BoundRows
from . import columns
@ -362,10 +362,8 @@ class Table(StrAndUnicode):
generated will clobber the querystring of the request. Use the
``{% render_table %}`` template tag instead.
"""
# minimizes Django 1.3 dependency
from django.test.client import RequestFactory
request = RequestFactory().get('/')
template = get_template(self.template)
request = build_request()
return template.render(RequestContext(request, {'table': self}))
@property

View File

@ -1,6 +1,6 @@
{% spaceless %}
{% load querystring nospaceless from django_tables2 %}
{% load trans blocktrans from i18n %}
{% load django_tables2 %}
{% load i18n %}
{% if table.page %}
<div class="table-container">
{% endif %}
@ -48,7 +48,7 @@
{% if table.page %}
{% with table.page.paginator.count as total %}
{% with table.page|length as count %}
{% with table.page.object_list|length as count %}
{% block pagination %}
<ul class="pagination">
{% if table.page.has_previous %}
@ -56,7 +56,7 @@
{% endif %}
{% if table.page.has_previous or table.page.has_next %}
{% nospaceless %}{% block pagination.current %}<li class="current">{% blocktrans with current=table.page.number total=table.paginator.num_pages %}Page {{ current }} of {{ total }}{% endblocktrans %}</li>{% endblock pagination.current %}{% endnospaceless %}
{% nospaceless %}{% block pagination.current %}<li class="current">{% blocktrans with table.page.number as current and table.paginator.num_pages as total %}Page {{ current }} of {{ total }}{% endblocktrans %}</li>{% endblock pagination.current %}{% endnospaceless %}
{% endif %}
{% if table.page.has_next %}

View File

@ -1,8 +1,11 @@
# coding: utf-8
from __future__ import absolute_import, unicode_literals
from django.core.handlers.wsgi import WSGIRequest
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.test.client import FakePayload
from itertools import chain
from StringIO import StringIO
class Sequence(list):
@ -416,3 +419,31 @@ class cached_property(object):
def __get__(self, instance, type):
res = instance.__dict__[self.func.__name__] = self.func(instance)
return res
def build_request(uri='/'):
"""
Return a fresh HTTP GET / request.
This is essentially a heavily cutdown version of Django 1.3's
``django.test.client.RequestFactory``.
"""
path, _, querystring = uri.partition('?')
return WSGIRequest({
'CONTENT_TYPE': 'text/html; charset=utf-8',
'PATH_INFO': path,
'QUERY_STRING': querystring,
'REMOTE_ADDR': '127.0.0.1',
'REQUEST_METHOD': 'GET',
'SCRIPT_NAME': '',
'SERVER_NAME': 'testserver',
'SERVER_PORT': '80',
'SERVER_PROTOCOL': 'HTTP/1.1',
'wsgi.version': (1, 0),
'wsgi.url_scheme': 'http',
'wsgi.input': FakePayload(b''),
'wsgi.errors': StringIO(),
'wsgi.multiprocess': True,
'wsgi.multithread': False,
'wsgi.run_once': False,
})

View File

@ -15,8 +15,8 @@ setup(
packages=find_packages(exclude=['tests.*', 'tests', 'example.*', 'example']),
include_package_data=True, # declarations in MANIFEST.in
install_requires=['Django >=1.3'],
tests_require=['Django >=1.3', 'django-attest >=0.5.0', 'fudge', 'pylint',
install_requires=['Django >=1.1'],
tests_require=['Django >=1.2', 'django-attest >=0.6.0', 'fudge', 'pylint',
'django-haystack', 'unittest-xml-reporting', 'lxml', 'pytz'],
test_loader='tests:loader',

View File

@ -3,14 +3,13 @@
from __future__ import unicode_literals
from attest import assert_hook, Tests, warns # pylint: disable=W0611
from datetime import date, datetime
from django_attest import TestContext
from django_attest import settings, TestContext
import django_tables2 as tables
from django_tables2.utils import build_request
from django_tables2 import A, Attrs
from django.db import models
from django.test.client import RequestFactory
from django.core.urlresolvers import reverse
from django.template import Context, Template
from django.test.utils import override_settings
from django.utils.translation import ugettext
from django.utils.safestring import mark_safe, SafeData
try:
@ -438,7 +437,7 @@ def unicode():
]
table = UnicodeTable(dataset)
request = RequestFactory().get('/some-url/')
request = build_request('/some-url/')
template = Template('{% load django_tables2 %}{% render_table table %}')
html = template.render(Context({'request': request, 'table': table}))
@ -626,33 +625,33 @@ def should_handle_explicit_format():
@datecolumn.test
@override_settings(DATE_FORMAT="D Y b")
def should_handle_long_format():
class TestTable(tables.Table):
date = tables.DateColumn(short=False)
with settings(DATE_FORMAT="D Y b"):
class TestTable(tables.Table):
date = tables.DateColumn(short=False)
class Meta:
default = ""
class Meta:
default = ""
table = TestTable([{"date": date(2012, 9, 11)},
{"date": None}])
assert table.rows[0]["date"] == "Tue 2012 sep"
assert table.rows[1]["date"] == ""
table = TestTable([{"date": date(2012, 9, 11)},
{"date": None}])
assert table.rows[0]["date"] == "Tue 2012 sep"
assert table.rows[1]["date"] == ""
@datecolumn.test
@override_settings(SHORT_DATE_FORMAT="b Y D")
def should_handle_short_format():
class TestTable(tables.Table):
date = tables.DateColumn(short=True)
with settings(SHORT_DATE_FORMAT="b Y D"):
class TestTable(tables.Table):
date = tables.DateColumn(short=True)
class Meta:
default = ""
class Meta:
default = ""
table = TestTable([{"date": date(2012, 9, 11)},
{"date": None}])
assert table.rows[0]["date"] == "sep 2012 Tue"
assert table.rows[1]["date"] == ""
table = TestTable([{"date": date(2012, 9, 11)},
{"date": None}])
assert table.rows[0]["date"] == "sep 2012 Tue"
assert table.rows[1]["date"] == ""
@datecolumn.test
@ -710,7 +709,7 @@ def should_handle_long_format(dt):
class Meta:
default = ""
with override_settings(DATETIME_FORMAT="D Y b A f"):
with settings(DATETIME_FORMAT="D Y b A f"):
table = TestTable([{"date": dt}, {"date": None}])
assert table.rows[0]["date"] == "Tue 2012 sep PM 12:30"
assert table.rows[1]["date"] == ""
@ -724,7 +723,7 @@ def should_handle_short_format(dt):
class Meta:
default = ""
with override_settings(SHORT_DATETIME_FORMAT="b Y D A f"):
with settings(SHORT_DATETIME_FORMAT="b Y D A f"):
table = TestTable([{"date": dt}, {"date": None}])
assert table.rows[0]["date"] == "sep 2012 Tue PM 12:30"
assert table.rows[1]["date"] == ""

View File

@ -1,13 +1,12 @@
# coding: utf-8
from attest import Tests
from django_tables2 import RequestConfig
from django_tables2.utils import build_request
from django.core.paginator import EmptyPage, PageNotAnInteger
from django.test.client import RequestFactory
from fudge import Fake
NOTSET = object() # unique value
factory = RequestFactory()
requestconfig = Tests()
@ -21,7 +20,7 @@ def faketable():
@requestconfig.test
def no_querystring(table):
request = factory.get("/")
request = build_request("/")
table = table.has_attr(order_by=NOTSET).expects("paginate")
RequestConfig(request).configure(table)
assert table.order_by is NOTSET
@ -29,7 +28,7 @@ def no_querystring(table):
@requestconfig.test
def full_querystring(table):
request = factory.get("/?page=1&per_page=5&sort=abc")
request = build_request("/?page=1&per_page=5&sort=abc")
table = (table
.expects("paginate").with_args(page=1, per_page=5)
.expects("order_by").with_args("abc"))
@ -38,7 +37,7 @@ def full_querystring(table):
@requestconfig.test
def partial_querystring(table):
request = factory.get("/?page=1&sort=abc")
request = build_request("/?page=1&sort=abc")
table = (table
.expects("paginate").with_args(page=1, per_page=5)
.expects("order_by").with_args("abc"))
@ -47,7 +46,7 @@ def partial_querystring(table):
@requestconfig.test
def silent_page_not_an_integer_error(table):
request = factory.get("/")
request = build_request("/")
paginator = (Fake("Paginator")
.expects("page").with_args(1))
table = (table
@ -61,7 +60,7 @@ def silent_page_not_an_integer_error(table):
@requestconfig.test
def silent_empty_page_error(table):
request = factory.get("/")
request = build_request("/")
paginator = (Fake("Paginator")
.has_attr(num_pages=987)
.expects("page").with_args(987))

View File

@ -4,9 +4,9 @@ from contextlib import contextmanager
from django_attest import queries, TestContext
import django_tables2 as tables
from django_tables2.config import RequestConfig
from django_tables2.utils import build_request
from django.conf import settings
from django.template import Template, RequestContext, Context
from django.test.client import RequestFactory
from django.utils.translation import ugettext_lazy
from django.utils.safestring import mark_safe
from urlparse import parse_qs
@ -26,7 +26,6 @@ def attrs(xml):
return lxml.html.fromstring(xml).attrib
factory = RequestFactory()
database = contextmanager(TestContext())
templates = Tests()
@ -108,7 +107,7 @@ def custom_rendering():
@templates.test
def render_table_templatetag():
# ensure it works with a multi-order-by
request = factory.get('/')
request = build_request('/')
table = CountryTable(MEMORY_DATA, order_by=('name', 'population'))
RequestConfig(request).configure(table)
template = Template('{% load django_tables2 %}{% render_table table %}')
@ -124,14 +123,14 @@ def render_table_templatetag():
# no data with no empty_text
table = CountryTable([])
template = Template('{% load django_tables2 %}{% render_table table %}')
html = template.render(Context({'request': factory.get('/'), 'table': table}))
html = template.render(Context({'request': build_request('/'), 'table': table}))
root = parse(html)
assert len(root.findall('.//thead/tr')) == 1
assert len(root.findall('.//thead/tr/th')) == 4
assert len(root.findall('.//tbody/tr')) == 0
# no data WITH empty_text
request = factory.get('/')
request = build_request('/')
table = CountryTable([], empty_text='this table is empty')
RequestConfig(request).configure(table)
template = Template('{% load django_tables2 %}{% render_table table %}')
@ -162,7 +161,7 @@ def render_table_should_support_template_argument():
table = CountryTable(MEMORY_DATA, order_by=('name', 'population'))
template = Template('{% load django_tables2 %}'
'{% render_table table "dummy.html" %}')
request = RequestFactory().get('/')
request = build_request('/')
context = RequestContext(request, {'table': table})
assert template.render(context) == 'dummy template contents\n'
@ -188,7 +187,7 @@ def querystring_templatetag():
# Should be something like: <root>?name=Brad&amp;a=b&amp;c=5&amp;age=21</root>
xml = template.render(Context({
"request": RequestFactory().get('/?a=b&name=dog&c=5'),
"request": build_request('/?a=b&name=dog&c=5'),
"foo": {"bar": "age"},
"value": 21,
}))
@ -206,7 +205,7 @@ def querystring_templatetag():
@templates.test
def querystring_templatetag_supports_without():
context = Context({
"request": RequestFactory().get('/?a=b&name=dog&c=5'),
"request": build_request('/?a=b&name=dog&c=5'),
"a_var": "a",
})
@ -235,13 +234,13 @@ def title_should_only_apply_to_words_without_uppercase_letters():
}
for raw, expected in expectations.items():
template = Template("{% load title from django_tables2 %}{{ x|title }}")
template = Template("{% load django_tables2 %}{{ x|title }}")
assert template.render(Context({"x": raw})) == expected
@templates.test
def nospaceless_works():
template = Template("{% load nospaceless from django_tables2 %}"
template = Template("{% load django_tables2 %}"
"{% spaceless %}<b>a</b> <i>b {% nospaceless %}<b>c</b> <b>d</b> {% endnospaceless %}lic</i>{% endspaceless %}")
assert template.render(Context()) == "<b>a</b><i>b <b>c</b>&#32;<b>d</b> lic</i>"
@ -292,7 +291,7 @@ def render_table_db_queries():
with queries(count=2):
# one query for pagination: .count()
# one query for page records: .all()[start:end]
request = factory.get('/')
request = build_request('/')
table = PersonTable(Person.objects.all())
RequestConfig(request).configure(table)
render(table=table, request=request)

View File

@ -2,12 +2,13 @@
from .app.models import Region
from attest import assert_hook, Tests
from django_attest import TestContext
from django.test.client import RequestFactory
import django_tables2 as tables
from django_tables2.utils import build_request
views = Tests()
views.context(TestContext())
USING_CBV = hasattr(tables, "SingleTableView")
class DispatchHookMixin(object):
@ -23,23 +24,22 @@ class SimpleTable(tables.Table):
model = Region
@views.test
@views.test_if(USING_CBV)
def view_should_support_pagination_options():
for name in ("Queensland", "New South Wales", "Victoria", "Tasmania"):
Region.objects.create(name=name)
class SimpleView(DispatchHookMixin, tables.SingleTableView):
table_class = SimpleTable
table_pagination = {"per_page": 1}
model = Region # needed for ListView
request = RequestFactory().get('/')
request = build_request('/')
response, view = SimpleView.as_view()(request)
assert view.get_table().paginator.num_pages == 4
@views.test
@views.test_if(USING_CBV)
def should_support_explicit_table_data():
class SimpleView(DispatchHookMixin, tables.SingleTableView):
table_class = SimpleTable
@ -51,6 +51,6 @@ def should_support_explicit_table_data():
table_pagination = {"per_page": 1}
model = Region # needed for ListView
request = RequestFactory().get('/')
request = build_request('/')
response, view = SimpleView.as_view()(request)
assert view.get_table().paginator.num_pages == 3

85
tox.ini
View File

@ -1,8 +1,8 @@
[tox]
envlist =
py27-dj, py27-dj1.4.x, py27-dj1.3.x, py27-dj1.2.x, py27-dj1.1.x,
py26-dj, py26-dj1.4.x, py26-dj1.3.x, py26-dj1.2.x, py26-dj1.1.x,
py264-dj, py264-dj1.4.x, py264-dj1.3.x, py264-dj1.2.x, py264-dj1.1.x
py27-dj, py27-dj1.4.x, py27-dj1.3.x, py27-dj1.2.x,
py26-dj, py26-dj1.4.x, py26-dj1.3.x, py26-dj1.2.x,
py264-dj, py264-dj1.4.x, py264-dj1.3.x, py264-dj1.2.x
[testenv]
commands =
@ -14,7 +14,7 @@ commands =
testing =
https://github.com/dag/attest/tarball/master
coverage
django-attest>=0.3.0
django-attest>=0.6.0
django-haystack
fudge
lxml
@ -22,7 +22,6 @@ testing =
unittest-xml-reporting
[django]
1.1.x = Django>=1.1,<1.2
1.2.x = Django>=1.2,<1.3
1.3.x = Django>=1.3,<1.4
1.4.x = Django>=1.4,<1.5
@ -50,48 +49,14 @@ deps =
[testenv:py27-dj1.2.x]
basepython = python2.7
commands =
python {envbindir}/coverage run setup.py test []
mkdir -p reports
coverage xml --include=django_tables2/* -o reports/coverage.xml
deps =
{[tools]testing}
{[django]1.2.x}
[testenv:py27-dj1.1.x]
basepython = python2.7
deps =
{[tools]testing}
{[django]1.1.x}
; -- python 2.6 ---------------------------------------------------------------
[testenv:py26-dj]
basepython = python2.6
deps =
{[tools]testing}
{[django]latest}
[testenv:py26-dj1.4.x]
basepython = python2.6
deps =
{[tools]testing}
{[django]1.4.x}
[testenv:py26-dj1.3.x]
basepython = python2.6
deps =
{[tools]testing}
{[django]1.3.x}
[testenv:py26-dj1.2.x]
basepython = python2.6
deps =
{[tools]testing}
{[django]1.2.x}
[testenv:py26-dj1.1.x]
basepython = python2.6
deps =
{[tools]testing}
{[django]1.1.x}
; -- python 2.6.4 -------------------------------------------------------------
[testenv:py264-dj]
@ -114,12 +79,40 @@ deps =
[testenv:py264-dj1.2.x]
basepython = python2.6.4
commands =
python {envbindir}/coverage run setup.py test []
mkdir -p reports
coverage xml --include=django_tables2/* -o reports/coverage.xml
deps =
{[tools]testing}
{[django]1.2.x}
[testenv:py264-dj1.1.x]
basepython = python2.6.4
; -- python 2.6 ---------------------------------------------------------------
[testenv:py26-dj]
basepython = python2.6
deps =
{[tools]testing}
{[django]1.1.x}
{[django]latest}
[testenv:py26-dj1.4.x]
basepython = python2.6
deps =
{[tools]testing}
{[django]1.4.x}
[testenv:py26-dj1.3.x]
basepython = python2.6
deps =
{[tools]testing}
{[django]1.3.x}
[testenv:py26-dj1.2.x]
basepython = python2.6
commands =
python {envbindir}/coverage run setup.py test []
mkdir -p reports
coverage xml --include=django_tables2/* -o reports/coverage.xml
deps =
{[tools]testing}
{[django]1.2.x}