Skip to content

Commit

Permalink
Merge pull request #58 from greyside/master
Browse files Browse the repository at this point in the history
Django 1.8 Compatibility
  • Loading branch information
dcramer committed May 11, 2015
2 parents ff2e97f + d01e82a commit 08f20ab
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 15 deletions.
17 changes: 15 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ env:
- DJANGO=1.4.12
- DJANGO=1.5.7
- DJANGO=1.6.4
- DJANGO=1.7.7
- DJANGO=1.8

matrix:
exclude:
Expand All @@ -37,13 +39,24 @@ matrix:
- python: "3.4"
env: DJANGO=1.4.12

- python: "2.6"
env: DJANGO=1.7.7
- python: "3.2"
env: DJANGO=1.7.7

- python: "2.6"
env: DJANGO=1.8
- python: "3.2"
env: DJANGO=1.8

before_script:
- psql -c 'create database bitfield_test;' -U postgres

install:
- pip install Django==$DJANGO
- "if [[ $DJANGO == '1.2.7' ]]; then pip install psycopg2==2.4.1; fi"
- "if [[ $DJANGO == '1.3.7' ]]; then pip install psycopg2==2.4.1; fi"
- "if [[ $DJANGO == '1.2.7' ]]; then pip install psycopg2==2.4.1 django-nose==0.1.3; fi"
- "if [[ $DJANGO == '1.3.7' ]]; then pip install psycopg2==2.4.1 django-nose==0.1.3; fi"
- "if [[ $DJANGO == '1.8' ]]; then pip install https://github.com/django-nose/django-nose/archive/master.zip; fi"
- pip install -e .

script: python setup.py test
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ django-bitfield
---------------

.. image:: https://api.travis-ci.org/disqus/django-bitfield.png?branch=master
:target: https://travis-ci.org/disqus/django-bitfield

Provides a BitField like class (using a BigIntegerField) for your Django models.

Expand Down
15 changes: 9 additions & 6 deletions bitfield/compat.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
__all__ = ('bitand', 'bitor')

def bitand(a, b):
return a.bitand(b)

def bitor(a, b):
return a.bitor(b)

try:
from django.db.models.expressions import ExpressionNode
ExpressionNode.BITAND # noqa
del ExpressionNode
except ImportError:
# Django >= 1.8
pass
except AttributeError:
# Django < 1.5
def bitand(a, b):
return a & b

def bitor(a, b):
return a | b
else:
def bitand(a, b):
return a.bitand(b)

def bitor(a, b):
return a.bitor(b)
8 changes: 8 additions & 0 deletions bitfield/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ def _has_changed(self, initial, data):

class BitFormField(IntegerField):
def __init__(self, choices=(), widget=BitFieldCheckboxSelectMultiple, *args, **kwargs):

if isinstance(kwargs['initial'], int):
iv = kwargs['initial']
l = []
for i in range(0, 63):
if (1 << i) & iv > 0:
l += [choices[i][0]]
kwargs['initial'] = l
self.widget = widget
super(BitFormField, self).__init__(widget=widget, *args, **kwargs)
self.choices = self.widget.choices = choices
Expand Down
23 changes: 18 additions & 5 deletions bitfield/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from django.db.models import signals
from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.fields import Field, BigIntegerField
from django.db.models.fields.subclassing import Creator
try:
Expand Down Expand Up @@ -156,15 +155,19 @@ def get_prep_value(self, value):
# return super(BitField, self).get_db_prep_save(value, connection=connection)

def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
if isinstance(value, SQLEvaluator) and isinstance(value.expression, Bit):
if isinstance(getattr(value, 'expression', None), Bit):
value = value.expression
if isinstance(value, (BitHandler, Bit)):
return BitQueryLookupWrapper(self.model._meta.db_table, self.db_column or self.name, value)
if hasattr(self, 'class_lookups'):
# Django 1.7+
return [value.mask]
else:
return BitQueryLookupWrapper(self.model._meta.db_table, self.db_column or self.name, value)
return BigIntegerField.get_db_prep_lookup(self, lookup_type=lookup_type, value=value,
connection=connection, prepared=prepared)

def get_prep_lookup(self, lookup_type, value):
if isinstance(value, SQLEvaluator) and isinstance(value.expression, Bit):
if isinstance(getattr(value, 'expression', None), Bit):
value = value.expression
if isinstance(value, Bit):
if lookup_type in ('exact',):
Expand Down Expand Up @@ -197,6 +200,12 @@ def deconstruct(self):
return name, path, args, kwargs


try:
BitField.register_lookup(BitQueryLookupWrapper)
except AttributeError:
pass


class CompositeBitFieldWrapper(object):
def __init__(self, fields):
self.fields = fields
Expand Down Expand Up @@ -232,13 +241,17 @@ def __setattr__(self, attr, value):


class CompositeBitField(object):
is_relation = False
many_to_many = False
concrete = False

def __init__(self, fields):
self.fields = fields

def contribute_to_class(self, cls, name):
self.name = name
self.model = cls
cls._meta.add_virtual_field(self)
cls._meta.virtual_fields.append(self)

signals.class_prepared.connect(self.validate_fields, sender=cls)

Expand Down
19 changes: 19 additions & 0 deletions bitfield/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,25 @@ def as_sql(self, qn, connection=None):
[])


try:
# Django 1.7+
from django.db.models.lookups import Exact
class BitQueryLookupWrapper(Exact):

def process_lhs(self, qn, connection, lhs=None):
lhs_sql, params = super(BitQueryLookupWrapper, self).process_lhs(
qn, connection, lhs)
if self.rhs:
lhs_sql = lhs_sql + ' & %s'
else:
lhs_sql = lhs_sql + ' | %s'
params.extend(self.get_db_prep_lookup(self.rhs, connection)[1])
return lhs_sql, params

except ImportError:
pass


class BitQuerySaveWrapper(BitQueryLookupWrapper):
def as_sql(self, qn, connection):
"""
Expand Down
1 change: 0 additions & 1 deletion bitfield/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
from .forms import *
from .models import *
from .tests import *
2 changes: 2 additions & 0 deletions bitfield/tests/forms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from django import forms
from bitfield.tests.models import BitFieldTestModel, CompositeBitFieldTestModel


class BitFieldTestModelForm(forms.ModelForm):

class Meta:
model = BitFieldTestModel
exclude = tuple()
22 changes: 22 additions & 0 deletions bitfield/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,25 @@ def get_label(self, flag):
if isinstance(flag, Bit):
flag = flag.number
return self._labels[flag]


import django

if django.VERSION[:2] >= (1, 8):
from django.core.exceptions import ImproperlyConfigured

# We need to register adapters in Django 1.8 in order to prevent
# "ProgrammingError: can't adapt type"
try:
from django.db.backends.sqlite3.base import Database
Database.register_adapter(Bit, lambda x: int(x))
Database.register_adapter(BitHandler, lambda x: int(x))
except ImproperlyConfigured:
pass

try:
from django.db.backends.postgresql_psycopg2.base import Database
Database.extensions.register_adapter(Bit, lambda x: Database.extensions.AsIs(int(x)))
Database.extensions.register_adapter(BitHandler, lambda x: Database.extensions.AsIs(int(x)))
except ImproperlyConfigured:
pass
12 changes: 11 additions & 1 deletion runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@ def runtests(*test_args, **kwargs):
if not test_args:
test_args = ['bitfield']

test_runner = NoseTestSuiteRunner(**kwargs)
try:
from django.test.runner import DiscoverRunner
test_runner = DiscoverRunner(**kwargs)
except ImportError:
test_runner = NoseTestSuiteRunner(**kwargs)

import django
try:
django.setup()
except AttributeError:
pass

failures = test_runner.run_tests(test_args)
sys.exit(failures)
Expand Down

0 comments on commit 08f20ab

Please sign in to comment.