Permalink
Browse files

100% test coverage

  • Loading branch information...
1 parent 6d88e59 commit 22af0acfb07c5aafea0d043ffaa7ae851952f384 @WoLpH committed Sep 20, 2013
@@ -1,5 +1,6 @@
from django.db.models import base
from django.db import models
+from django.template import defaultfilters
from python_utils import formatters
@@ -43,7 +44,6 @@ class Meta:
class NameMixin(object):
-
'''Mixin to automatically get a unicode and repr string base on the name
>>> x = NameMixin()
@@ -67,6 +67,33 @@ def __str__(self):
def __repr__(self):
return (u'<%s[%d]: %s>' % (
self.__class__.__name__,
- self.pk,
+ self.pk or -1,
self.name,
)).encode('utf-8')
+
+
+class SlugMixin(NameMixin):
+ '''Mixin to automatically slugify the name and add both a name and slug to
+ the model
+
+ >>> x = NameMixin()
+ >>> x.pk = 123
+ >>> x.name = 'test'
+ >>> repr(x)
+ '<NameMixin[123]: test>'
+ >>> str(x)
+ 'test'
+ >>> unicode(x)
+ u'test'
+
+ '''
+
+ def save(self, *args, **kwargs):
+ if not self.slug and self.name:
+ self.slug = defaultfilters.slugify(self.name)
+
+ super(NameMixin, self).save(*args, **kwargs)
+
+ class Meta(object):
+ unique_together = ('slug',)
+
View
@@ -70,7 +70,7 @@ class Enum(choices.Choices):
Eggs = choices.Choice()
enum = models.IntegerField(
- choices=Enum.choices, default=SomeModel.Foo)
+ choices=Enum.choices, default=Enum.Foo)
To reference these properties:
@@ -125,7 +125,21 @@ def __str__(self):
class Choice(object):
'''The choice object has an optional label and value. If the value is not
- given an autoincrementing id (starting from 1) will be used'''
+ given an autoincrementing id (starting from 1) will be used
+
+ >>> choice = Choice('value', 'label')
+ >>> choice
+ <Choice[1]:label>
+ >>> str(choice)
+ 'label'
+
+ >>> choice = Choice()
+ >>> choice
+ <Choice[2]:None>
+ >>> str(choice)
+ 'None'
+
+ '''
order = 0
def __init__(self, value=None, label=None):
@@ -145,11 +159,11 @@ def __str__(self):
return unicode(self).encode('utf-8', 'replace')
def __unicode__(self):
- value = self.value
- if isinstance(value, str):
- return unicode(value, 'utf-8', 'replace')
+ label = self.label
+ if isinstance(label, str):
+ return unicode(label, 'utf-8', 'replace')
else:
- return unicode(value)
+ return unicode(label)
class ChoicesMeta(type):
@@ -189,5 +203,30 @@ def __new__(cls, name, bases, attrs):
class Choices(object):
- '''The choices class is what you should inherit in your Django models'''
+ '''The choices class is what you should inherit in your Django models
+
+ >>> choices = Choices()
+ >>> choices.choices[0]
+ Traceback (most recent call last):
+ ...
+ KeyError: 'Key 0 does not exist'
+ >>> choices.choices
+ OrderedDict()
+ >>> str(choices.choices)
+ 'OrderedDict()'
+ >>> choices.choices.items()
+ []
+
+ >>> class ChoiceTest(Choices):
+ ... a = Choice()
+ >>> choices = ChoiceTest()
+ >>> choices.choices.items()
+ [(0, <Choice[3]:a>)]
+ >>> choices.a
+ 0
+ >>> choices.choices['a']
+ <Choice[3]:a>
+ >>> choices.choices[0]
+ <Choice[3]:a>
+ '''
__metaclass__ = ChoicesMeta
@@ -1,6 +1,6 @@
import re
import sys
-from django.core.management.base import BaseCommand
+from . import base_command
from django.db.models.loading import get_models
from django.db import models
@@ -30,9 +30,10 @@
RAW_ID_THRESHOLD = 100
-class Command(BaseCommand):
+class Command(base_command.CustomBaseCommand):
def handle(self, *args, **kwargs):
+ super(Command, self).handle(*args, **kwargs)
self.model_res = []
installed_apps = dict((a.__name__.rsplit('.', 1)[0], a)
@@ -115,10 +116,6 @@ def handle_app(self, app, **options):
model_dict['date_hierarchy'] = field_name
for k, vs in sorted(PREPOPULATED_FIELDS.iteritems()):
- if field_name in field_names \
- and not model_dict['date_hierarchy']:
- model_dict['date_hierarchy'] = field_name
-
if k in field_names:
incomplete = False
for v in vs:
@@ -11,6 +11,7 @@
class CustomBaseCommand(BaseCommand):
+ loggers = ()
def __init__(self):
self.verbosity = DEFAULT_VERBOSITY
@@ -27,7 +28,9 @@ def handle(self, *args, **kwargs):
def create_logger(self):
name = self.__class__.__module__.split('.')[-1]
- logger_name = 'management.commands.%s' % name
- logger = logging.getLogger(logger_name)
- logger.setLevel(VERBOSITY_LOG_MAP[self.verbosity])
+ loggers = self.loggers + ('management.commands.%s' % name,)
+ for logger_name in loggers:
+ logger = logging.getLogger(logger_name)
+ logger.setLevel(VERBOSITY_LOG_MAP[self.verbosity])
+
return logger
@@ -1,8 +1,17 @@
-import anyjson
+import json
from django import shortcuts as django_shortcuts
from django import http
from django.template import RequestContext
from django.contrib.auth import decorators
+from django.conf import settings
+from django.core import serializers
+from django.db import models
+from django.core import urlresolvers
+
+try:
+ from coffin import shortcuts as coffin_shortcuts
+except ImportError:
+ coffin_shortcuts = None
class ViewError(Exception):
@@ -12,10 +21,32 @@ class ViewError(Exception):
class UnknownViewResponseError(ViewError):
pass
+
+def json_default_handler(obj):
+ if hasattr(obj, 'isoformat'):
+ return obj.isoformat()
+ else:
+ raise TypeError('Object of type %s with value of %s is not JSON '
+ 'serializable' % (type(obj), repr(obj)))
+
+
+def redirect(url, *args, **kwargs):
+ if '/' not in url or args or kwargs:
+ url = urlresolvers.reverse(url, args=args, kwargs=kwargs)
+ return http.HttpResponseRedirect(url)
+
+
+def permanent_redirect(url, *args, **kwargs):
+ if '/' not in url or args or kwargs:
+ url = urlresolvers.reverse(url, args=args, kwargs=kwargs)
+ return http.HttpResponsePermanentRedirect(url)
+
+
REQUEST_PROPERTIES = {
- 'redirect': http.HttpResponseRedirect,
- 'permanent_redirect': http.HttpResponsePermanentRedirect,
+ 'redirect': redirect,
+ 'permanent_redirect': permanent_redirect,
'not_found': http.HttpResponseNotFound,
+ 'reverse': urlresolvers.reverse,
}
@@ -24,6 +55,7 @@ def _prepare_request(request, app, view):
request.context = RequestContext(request)
request.context['view'] = view
request.context['app'] = app
+ request.context['request'] = request
for k, v in REQUEST_PROPERTIES.iteritems():
setattr(request, k, v)
@@ -38,12 +70,16 @@ def _process_response(request, response):
pop = False
try:
- if isinstance(response, (dict, list)):
+ if isinstance(response, (dict, list, models.query.QuerySet)):
if request.ajax:
- output = json = anyjson.serialize(response)
+ if isinstance(response, models.query.QuerySet):
+ output = serializers.serialize('json', response)
+ else:
+ output = json.dumps(response, default=json_default_handler)
+
callback = request.GET.get('callback', False)
if callback:
- output = '%s(%s)' % (callback, json)
+ output = '%s(%s)' % (callback, output)
if request.GET.get('debug'):
title = 'Rendering %(view)r in module %(app)r' % (
request.context)
@@ -82,7 +118,12 @@ def _process_response(request, response):
return http.HttpResponse(response)
elif response is None:
- render_to_response = django_shortcuts.render_to_response
+ if request.jinja:
+ assert coffin_shortcuts, ('To use Jinja the `coffin` module '
+ 'must be installed')
+ render_to_response = coffin_shortcuts.render_to_response
+ else:
+ render_to_response = django_shortcuts.render_to_response
return render_to_response(
request.template, context_instance=request.context)
@@ -113,17 +154,12 @@ def _env(request, *args, **kwargs):
request.ajax = request.is_ajax() or bool(int(
request.REQUEST.get('ajax', 0)))
request.context = None
+ request.jinja = getattr(settings, 'DJANGO_UTILS_USE_JINJA', False)
try:
name = function.__name__
app = function.__module__.split('.')[0]
-
request = _prepare_request(request, app, name)
-
- if app:
- request.template = '%s/%s.html' % (app, name)
- else:
- request.template = '%s.html' % name
-
+ request.template = '%s/%s.html' % (app, name)
response = function(request, *args, **kwargs)
return _process_response(request, response)
View
@@ -31,8 +31,13 @@
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest',
- 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.intersphinx',
+ 'sphinx.ext.coverage',
+ 'sphinx.ext.viewcode',
+]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
View
@@ -8,8 +8,8 @@ with-doctest=1
with-coverage=1
cover-package=django_utils
-pdb=1
-pdb-failures=1
+#pdb=1
+#pdb-failures=1
with-django=1
django-settings=tests.settings
View
@@ -1,5 +1,5 @@
import os
-from setuptools import setup, find_packages
+import setuptools
# Little hack to make sure tests work
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'
@@ -11,26 +11,20 @@
else:
long_description = 'See http://pypi.python.org/pypi/django-utils/'
-setup(
+setuptools.setup(
name=django_utils.__package_name__,
version=django_utils.__version__,
author=django_utils.__author__,
author_email=django_utils.__author_email__,
description=django_utils.__description__,
url=django_utils.__url__,
license='BSD',
- packages=find_packages(),
+ packages=setuptools.find_packages(),
install_requires=[
'python-utils>=1.1.1',
'anyjson>=0.3.0'
],
long_description=long_description,
test_suite='nose.collector',
- tests_requires=[
- 'nose',
- 'gitt+git://github.com/akheron/nosedjango@nose-and-django-versions#egg=nosedjango',
- 'coverage',
- 'django',
- ],
classifiers=['License :: OSI Approved :: BSD License'],
)
View
@@ -1,2 +1,8 @@
-e git://github.com/akheron/nosedjango@e60b994fcf669af9d74f801947773103d17ba701#egg=nosedjango-dev
tissue==0.8
+nose
+coverage
+django
+coffin
+jinja2
+
View
@@ -108,6 +108,7 @@
WSGI_APPLICATION = 'tests.wsgi.application'
TEMPLATE_DIRS = (
+ 'tests/templates'
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
@@ -124,6 +125,8 @@
'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
+ 'django_utils',
+ 'tests.test_app',
)
# A sample logging configuration. The only tangible logging
No changes.
No changes.
View
@@ -0,0 +1,10 @@
+from django.db import models
+
+
+class Spam(models.Model):
+ a = models.CharField(max_length=50)
+
+
+class Eggs(Spam):
+ b = models.CharField(max_length=100)
+
Oops, something went wrong.

0 comments on commit 22af0ac

Please sign in to comment.