Skip to content

Commit

Permalink
Updated to latest mercurial version
Browse files Browse the repository at this point in the history
  • Loading branch information
Alberto Paro committed Jan 1, 2011
1 parent 992c10e commit cae17e9
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 89 deletions.
4 changes: 0 additions & 4 deletions djangotoolbox/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User, Group


class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name', 'is_active',
'is_staff', 'is_superuser')


class CustomUserAdmin(UserAdmin):
fieldsets = None
form = UserForm


admin.site.unregister(User)
admin.site.unregister(Group)
admin.site.register(User, CustomUserAdmin)

22 changes: 15 additions & 7 deletions djangotoolbox/db/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@
from .creation import NonrelDatabaseCreation

class NonrelDatabaseFeatures(BaseDatabaseFeatures):
def __init__(self, connection):
self.connection = connection
super(NonrelDatabaseFeatures, self).__init__()
can_return_id_from_insert = True
supports_unspecified_pk = False
supports_regex_backreferencing = True
supports_date_lookup_using_string = False
supports_timezones = False

supports_joins = False
distinguishes_insert_from_update = False
supports_select_related = False
supports_deleting_related_objects = False
string_based_auto_field = False
supports_dicts = False

def _supports_transactions(self):
return False

class NonrelDatabaseOperations(BaseDatabaseOperations):
def __init__(self, connection):
self.connection = connection
Expand Down Expand Up @@ -42,11 +49,12 @@ def prep_for_iexact_query(self, value):
return value

def check_aggregate_support(self, aggregate):
# TODO: Only COUNT(*) should be supported, by default.
# Raise NotImplementedError in all other cases.
pass
from django.db.models.sql.aggregates import Count
if not isinstance(aggregate, Count):
raise NotImplementedError("This database does not support %r "
"aggregates" % type(aggregate))

def year_lookup_bounds(self, value):
def year_lookup_bounds(self, value):
return [datetime.datetime(value, 1, 1, 0, 0, 0, 0),
datetime.datetime(value+1, 1, 1, 0, 0, 0, 0)]

Expand Down
4 changes: 2 additions & 2 deletions djangotoolbox/db/basecompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,8 @@ def execute_sql(self, return_id=False):
if not field.null and value is None:
raise DatabaseError("You can't set %s (a non-nullable "
"field) to None!" % field.name)
value = self.convert_value_for_db(field.db_type(connection=self.connection),
value)
db_type = field.db_type(connection=self.connection)
value = self.convert_value_for_db(db_type, value)
data[column] = value
return self.insert(data, return_id=return_id)

Expand Down
109 changes: 108 additions & 1 deletion djangotoolbox/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,25 @@

from django.db import models
from django.core.exceptions import ValidationError
from django.utils.importlib import import_module

__all__ = ('GenericField', 'ListField', 'DictField', 'SetField', 'BlobField')
__all__ = ('RawField', 'ListField', 'DictField', 'SetField',
'BlobField', 'EmbeddedModelField')

class _HandleAssignment(object):
"""
A placeholder class that provides a way to set the attribute on the model.
"""
def __init__(self, field):
self.field = field

def __get__(self, obj, type=None):
if obj is None:
raise AttributeError('Can only be accessed via an instance.')
return obj.__dict__[self.field.name]

def __set__(self, obj, value):
obj.__dict__[self.field.name] = self.field.to_python(value)

class RawField(models.Field):
""" Generic field to store anything your database backend allows you to. """
Expand Down Expand Up @@ -35,6 +52,10 @@ def contribute_to_class(self, cls, name):
self.item_field.name = name
super(AbstractIterableField, self).contribute_to_class(cls, name)

metaclass = getattr(self.item_field, '__metaclass__', None)
if issubclass(metaclass, models.SubfieldBase):
setattr(cls, self.name, _HandleAssignment(self))

def db_type(self, connection):
item_db_type = self.item_field.db_type(connection=connection)
return '%s:%s' % (self.__class__.__name__, item_db_type)
Expand All @@ -47,6 +68,21 @@ def _convert(self, func, values, *args, **kwargs):
def to_python(self, value):
return self._convert(self.item_field.to_python, value)

def pre_save(self, model_instance, add):
class fake_instance(object):
pass
fake_instance = fake_instance()
def wrapper(value):
assert not hasattr(self.item_field, 'attname')
fake_instance.value = value
self.item_field.attname = 'value'
try:
return self.item_field.pre_save(fake_instance, add)
finally:
del self.item_field.attname

return self._convert(wrapper, getattr(model_instance, self.attname))

def get_db_prep_value(self, value, connection, prepared=False):
return self._convert(self.item_field.get_db_prep_value, value,
connection=connection, prepared=prepared)
Expand Down Expand Up @@ -148,3 +184,74 @@ def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):

def value_to_string(self, obj):
return str(self._get_val_from_obj(obj))

class EmbeddedModelField(models.Field):
"""
Field that allows you to embed a model instance.
:param model: (optional) The model class that shall be embedded
(may also be passed as string similar to relation fields)
"""
__metaclass__ = models.SubfieldBase

def __init__(self, embedded_model=None, *args, **kwargs):
self.embedded_model = embedded_model
kwargs.setdefault('default', None)
super(EmbeddedModelField, self).__init__(*args, **kwargs)

def db_type(self, connection):
return 'DictField:RawField'

def _set_model(self, model):
# EmbeddedModelFields are not contribute[d]_to_class if using within
# ListFields (and friends), so we can only know the model field is
# used in when the IterableField sets our 'model' attribute in its
# contribute_to_class method.
# We need to know the model to generate a valid key for the lookup.

if model is not None and isinstance(self.embedded_model, basestring):
# The model argument passed to __init__ was a string, so we need
# to make sure to resolve that string to the corresponding model
# class, similar to relation fields. We abuse some of the
# relation fields' code to do the lookup here:
def _resolve_lookup(self_, resolved_model, model):
self.embedded_model = resolved_model
from django.db.models.fields.related import add_lazy_relation
add_lazy_relation(model, self, self.embedded_model, _resolve_lookup)

self._model = model

model = property(lambda self:self._model, _set_model)

def pre_save(self, model_instance, add):
embedded_instance = super(EmbeddedModelField, self).pre_save(model_instance, add)
if embedded_instance is None:
return None, None
if self.embedded_model is not None and \
not isinstance(embedded_instance, self.embedded_model):
raise TypeError("Expected instance of type %r, not %r"
% (type(self.embedded_model), type(embedded_instance)))

data = dict((field.name, field.pre_save(embedded_instance, add))
for field in embedded_instance._meta.fields)
return embedded_instance, data

def get_db_prep_value(self, (embedded_instance, embedded_dict), **kwargs):
if embedded_dict is None:
return None
values = dict()
for name, value in embedded_dict.iteritems():
field = embedded_instance._meta.get_field(name)
values[name] = field.get_db_prep_value(value, **kwargs)
if self.embedded_model is None:
values.update({'_module' : embedded_instance.__class__.__module__,
'_model' : embedded_instance.__class__.__name__})
return values

def to_python(self, values):
if not isinstance(values, dict):
return values
module, model = values.pop('_module', None), values.pop('_model', None)
if module is not None:
return getattr(import_module(module), model)(**values)
return self.embedded_model(**values)
14 changes: 11 additions & 3 deletions djangotoolbox/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
LOGIN_REQUIRED_PREFIXES = getattr(settings, 'LOGIN_REQUIRED_PREFIXES', ())
NO_LOGIN_REQUIRED_PREFIXES = getattr(settings, 'NO_LOGIN_REQUIRED_PREFIXES', ())

ALLOWED_DOMAINS = getattr(settings, 'ALLOWED_DOMAINS', None)
NON_REDIRECTED_PATHS = getattr(settings, 'NON_REDIRECTED_PATHS', ())
NON_REDIRECTED_BASE_PATHS = tuple(path.rstrip('/') + '/'
for path in NON_REDIRECTED_PATHS)

class LoginRequiredMiddleware(object):
"""
Redirects to login page if request path begins with a
Expand Down Expand Up @@ -32,9 +37,12 @@ def process_request(self, request):
host = request.get_host().split(':')[0]
# Turn off redirects when in debug mode, running unit tests, or
# when handling an App Engine cron job.
if settings.DEBUG or host == 'testserver' or \
not getattr(settings, 'ALLOWED_DOMAINS', None) or \
request.META.get('HTTP_X_APPENGINE_CRON') == 'true':
if (settings.DEBUG or host == 'testserver' or
not ALLOWED_DOMAINS or
request.META.get('HTTP_X_APPENGINE_CRON') == 'true' or
request.path.startswith('/_ah/') or
request.path in NON_REDIRECTED_PATHS or
request.path.startswith(NON_REDIRECTED_BASE_PATHS)):
return
if host not in settings.ALLOWED_DOMAINS:
return HttpResponseRedirect('http://' + settings.ALLOWED_DOMAINS[0])
Expand Down
68 changes: 4 additions & 64 deletions djangotoolbox/test.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
from .utils import object_list_to_table, equal_lists
from django.test import TestCase
from django.test.simple import DjangoTestSuiteRunner, DjangoTestRunner
from django.test.simple import DjangoTestSuiteRunner
from django.utils.unittest import TextTestRunner
import sys

try:
from StringIO import StringIO
except ImportError:
from cStringIO import StringIO

def skip_if(test):
"""Skips a unit test if ``test`` is ``True``"""
def _inner(func):
if test:
return None
return func
return _inner

class ModelTestCase(TestCase):
"""
A test case for models that provides an easy way to validate the DB
Expand Down Expand Up @@ -53,60 +46,7 @@ def validate_state(self, columns, *state_table):
print state
self.fail('DB state not valid')

class CapturingTestRunner(DjangoTestRunner):
def _makeResult(self):
result = super(CapturingTestRunner, self)._makeResult()
stdout = sys.stdout
stderr = sys.stderr

def extend_error(errors):
try:
captured_stdout = sys.stdout.getvalue()
captured_stderr = sys.stderr.getvalue()
except AttributeError:
captured_stdout = captured_stderr = ''
sys.stdout = stdout
sys.stderr = stderr
t, e = errors[-1]
if captured_stdout:
e += '\n--------------- Captured stdout: ---------------\n'
e += captured_stdout
if captured_stderr:
e += '\n--------------- Captured stderr: ---------------\n'
e += captured_stderr
if captured_stdout or captured_stderr:
e += '\n--------------- End captured output ---------------\n\n'
errors[-1] = (t, e)

def override(func):
func.orig = getattr(result, func.__name__)
setattr(result, func.__name__, func)
return func

@override
def startTest(test):
startTest.orig(test)
sys.stdout = StringIO()
sys.stderr = StringIO()

@override
def addSuccess(test):
addSuccess.orig(test)
sys.stdout = stdout
sys.stderr = stderr

@override
def addError(test, err):
addError.orig(test, err)
extend_error(result.errors)

@override
def addFailure(test, err):
addFailure.orig(test, err)
extend_error(result.failures)

return result

class CapturingTestSuiteRunner(DjangoTestSuiteRunner):
"""Captures stdout/stderr during test and shows them next to tracebacks"""
def run_suite(self, suite, **kwargs):
return CapturingTestRunner(verbosity=self.verbosity, failfast=self.failfast).run(suite)
return TextTestRunner(verbosity=self.verbosity, failfast=self.failfast, buffer=True).run(suite)
Loading

0 comments on commit cae17e9

Please sign in to comment.