Permalink
Browse files

initial commit

  • Loading branch information...
jiaaro committed Aug 25, 2011
0 parents commit 25685f1a95d4946a2692f7d7196f56bbf99da361
@@ -0,0 +1,8 @@
+
+.DS_Store
+.pydevproject
+.project
+.settings
+*.pyc
+/jsonate.egg-info/
+/dist/
21 LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2011 James Robert
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
@@ -0,0 +1 @@
+include README.markdown
@@ -0,0 +1,94 @@
+## Installation ##
+
+ 1. Install lib with pip:
+
+ `pip install jsonate`
+
+ **- OR -**
+
+ Put the "jsonate" directory somewhere in your python path
+
+ 2. Add "jsonate" to your installed apps (in the settings.py file)
+
+
+## Usage ##
+
+### In templates
+
+ {% load jsonate %}
+
+ {{ anything|jsonate }}
+
+This is especially useful for embedding data in in data attributes for
+use with javascript libraries like jQuery (note jsonate-attr is identical to jsonate|escape):
+
+ <div id="user-widget" data-user="{{ user|jsonate-attr }}"></div>
+
+ <script>
+ ...
+ user_data = $("#user-widget").data('user');
+ ...
+ </script>
+
+Or just use it directly in javascript...
+
+ <script>
+ var user_data = {{ user|jsonate }};
+ </script>
+
+### In Python
+
+ from jsonate import jsonate
+
+ # querysets
+ json = jsonate(User.objects.all())
+
+ # values
+ json = jsonate(User.objects.values())
+
+ # model instances
+ json = jsonate(User.objects.get(email="my_email@gmail.com"))
+
+Jsonate turns datetimes into iso format for easy parsing in javascript
+
+ >>> print jsonate(User.objects.all()[0])
+ '''{
+ "username": "asdfasdf",
+ "first_name": "",
+ "last_name": "",
+ "is_active": false,
+ "email": "asdf@example.com",
+ "is_superuser": false,
+ "is_staff": false,
+ "last_login": "2011-08-22T19:14:50.603531",
+ "id": 5,
+ "date_joined": "2011-08-22T19:14:50.220049"
+ }'''
+
+## Fields / Exclude options
+
+You may specify which fields should be serialized in the meta options of
+your models. This affects the serialization of model instances, and querysets,
+just like the Admin!
+
+Example
+
+ class MyModel(models.Model):
+ normal_info = models.CharField(max_length=10)
+ sensitive_info = models.CharField(max_length=10)
+
+ class Meta:
+ jsonate_exclude = ('sensitive_info',)
+ # this would also work:
+ # jsonate_fields = ('normal_info',)
+
+By default the User model in `django.contrib.auth.models` is monkey-patched
+to exclude the password field when serializing querysets or instances
+
+If you want to specify which fields will be serialized on a per-case basis,
+use `values()` instead. like so
+
+ >>> jsonate(User.objects.values("username", "password"))
+ ... '[{"username": "someuser", "password": "sha1$f26b2$d03a6123487fce20aabcdef0987654321abcdef0"}]'
+
+note: this is obviously not a real password or salt :)
@@ -0,0 +1 @@
+from jsonate.utils import jsonate
@@ -0,0 +1,80 @@
+from datetime import datetime, date
+from decimal import Decimal
+from django.utils import simplejson as json
+from django.db.models.query import QuerySet, ValuesQuerySet
+from django.db.models import Model
+from django.db.models.fields.related import ForeignKey
+from django.db.models.fields.files import FieldFile
+
+# Custom encoder using a list of mapping functions
+type_map = []
+class JsonateEncoder(json.JSONEncoder):
+ def default(self, obj):
+ for obj_type, mapper in type_map:
+ if isinstance(obj, obj_type):
+ # recurse until we get to an object the encoder
+ # can handle natively
+ return self.default(mapper(obj))
+ try:
+ # this will handle the non-string things that
+ # the default encoder can handle (like numbers, booleans etc)
+ return super(JsonateEncoder, self).default(obj)
+ except TypeError:
+ return obj
+
+
+# Decorator for registering mapping functions
+def register_typemap(obj_type):
+ def wrapper(fn):
+ type_map.append((obj_type, fn))
+ return fn
+ return wrapper
+
+
+# helper function for getting the correct fields to serialize
+def jsonate_fields(model):
+ all_fields = tuple(f.name for f in model._meta.fields)
+
+ excluded = getattr(model._meta, "jsonate_exclude", ())
+ fields = getattr(model._meta, 'jsonate_fields', all_fields)
+
+ serialize = set(fields).difference(set(excluded))
+
+ return tuple(field for field in model._meta.fields
+ if field.name in serialize)
+
+#########################
+## Mapping functions ##
+#########################
+
+@register_typemap(QuerySet)
+def map_queryset(obj):
+ fields = jsonate_fields(obj.model)
+ return obj.values(*[field.name for field in fields])
+
+@register_typemap(ValuesQuerySet)
+def map_values_queryset(obj):
+ return list(obj)
+
+@register_typemap(Model)
+def map_model_instance(obj):
+ d = {}
+ for field in jsonate_fields(obj):
+ value = getattr(obj, field.name)
+ if isinstance(field, ForeignKey) and value is not None:
+ value = value._get_pk_val()
+ d[field.name] = value
+ return d
+
+@register_typemap(FieldFile)
+def map_filefield_file(obj):
+ return obj.url
+
+@register_typemap(date)
+@register_typemap(datetime)
+def map_datetime(obj):
+ return obj.isoformat()
+
+@register_typemap(Decimal)
+def map_decimal(obj):
+ return float(obj)
@@ -0,0 +1 @@
+import jsonate.monkey_patches #@UnusedImport
@@ -0,0 +1,8 @@
+import django.db.models.options as options
+
+try:
+ from django.contrib.auth.models import User
+ User._meta.jsonate_exclude = ('password',)
+except ImportError: pass
+
+options.DEFAULT_NAMES += ('jsonate_fields', 'jsonate_exclude')
No changes.
@@ -0,0 +1,11 @@
+from django.template import Library
+from django.template.defaultfilters import escape
+from jsonate import jsonate
+
+register = Library()
+
+register.filter("jsonate", jsonate)
+
+@register.filter
+def jsonate_attr(obj):
+ return escape(jsonate(obj))
@@ -0,0 +1,5 @@
+import json
+from jsonate.json_encoder import JsonateEncoder
+
+def jsonate(obj):
+ return json.dumps(obj, cls=JsonateEncoder)
@@ -0,0 +1,40 @@
+from setuptools import setup
+
+setup(
+ name='jsonate',
+ version='0.1.0',
+
+ author='James Robert',
+ author_email='jiaaro@gmail.com',
+
+ description=('Django library that can make ANYTHING into json'),
+ long_description=open('README.markdown').read(),
+
+ license='MIT',
+ keywords='django json templatetags',
+
+ url='https://jsonate.com',
+
+ install_requires=[
+ "django",
+ ],
+
+ packages=[
+ 'jsonate',
+ 'jsonate.templatetags',
+ ],
+
+ include_package_data=True,
+
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'License :: OSI Approved :: MIT License',
+ 'Programming Language :: Python',
+ 'Framework :: Django',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Topic :: Utilities'
+ ]
+)
No changes.
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ execute_manager(settings)
No changes.
@@ -0,0 +1,46 @@
+import os.path
+import sys
+
+PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
+sys.path.insert(0, os.path.join(PROJECT_ROOT, '..'))
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.mysql',
+ 'NAME': 'test_jsonate',
+ 'USER': 'root'
+ }
+}
+
+MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media')
+MEDIA_URL = '/media/'
+
+TEMPLATE_DIRS = (
+ os.path.join(PROJECT_ROOT, 'templates'),
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+)
+
+ROOT_URLCONF = 'test_project.urls'
+
+SITE_ID = 1
+
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.admin',
+
+ 'jsonate',
+ 'test_app',
+)
No changes.
@@ -0,0 +1,25 @@
+from datetime import datetime, date
+from django.db import models
+from django.contrib.auth.models import User
+
+class MyModel(models.Model):
+ foreign_key = models.ForeignKey(User)
+ normal_field1 = models.CharField(max_length=25, default="field1")
+ normal_field2 = models.CharField(max_length=25, default='field2')
+
+ sensitive_field1 = models.CharField(max_length=25, default='sensitive')
+
+ datetime_field = models.DateTimeField(default=datetime(2011, 1, 11, 11, 11, 11))
+ date_field = models.DateField(default=date(2011, 1, 11))
+
+ decimal_field = models.DecimalField(max_digits=10, decimal_places=2, default='32.25')
+ float_field = models.FloatField(default=32.25)
+
+ file_field = models.FileField(upload_to="files")
+ image_field = models.ImageField(upload_to="images")
+
+ boolean_field = models.BooleanField(default=True)
+ null_field = models.NullBooleanField(default=None)
+
+ class Meta:
+ jsonate_exclude = ('sensitive_field1',)
Oops, something went wrong.

0 comments on commit 25685f1

Please sign in to comment.