Skip to content

Instantly share code, notes, and snippets.

@ruiwen
Created August 28, 2011 13:59
Show Gist options
  • Save ruiwen/1176697 to your computer and use it in GitHub Desktop.
Save ruiwen/1176697 to your computer and use it in GitHub Desktop.
Utility methods for Django
# Various utility functions
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
from django.template import RequestContext, Context, loader
from django.conf import settings
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import authenticate, login as django_login
from django.db import models
import json
def render(request, template, dictionary={}, context_instance=None, mimetype='text/html', status=200):
t = loader.get_template(template)
c = RequestContext(request, dictionary)
return HttpResponse(t.render(c), mimetype=mimetype, status=status)
def login(request, template_name='registration/login.html', redirect_field_name=settings.LOGIN_REDIRECT_URL, authentication_form=AuthenticationForm):
'''Custom login function, with ability to handle both Ajax and non-Ajax logins'''
if request.method == 'POST':
form = authentication_form(data=request.POST)
if form.is_valid():
u = form.get_user()
django_login(request, u) # It's valid, so log the User in
if request.is_ajax():
return HttpResponse(json.dumps({
'id': u.id,
'username': u.username,
'first_name': u.first_name,
'last_name': u.last_name,
'email': u.email
}))
else:
return HttpResponseRedirect(redirect_field_name)
else:
if request.is_ajax():
return HttpResponseForbidden()
else:
form = authentication_form()
return render(request, template_name, {'next': redirect_field_name, 'form': form})
# Utility classes
class UtilMixIn():
def __json_render_field(self, f, field, only_fields=[], mapped_fields={}):
if isinstance(field, models.fields.DateTimeField):
return time.mktime(getattr(self, f).timetuple())
elif isinstance(field, models.fields.related.ForeignKey):
return getattr(self, "%s_id" % f)
elif isinstance(field, (models.related.RelatedObject,models.related.RelatedField)):
# Retrieve only() the fields specified in secondary
items = getattr(self, f).only(*only_fields).all()
return [i.json(as_dict=True, fields=mapped_fields) for i in items]
elif isinstance(field, models.fields.files.ImageFieldFile):
return getattr(self, f).name
else:
try:
return getattr(self, f)
except AttributeError:
return getattr(self, field)
def json(self, **kwargs):
'''
Returns a JSON representation of a Model
@param fields list / dict
List of fields to return
Defaults to fields defined on model.
Use this to limit the set of fields returned
Using the dict form of the 'fields' parameter implies that you'd like to perform
custom remapping of field names.
For example, if you wanted a 'name' attribute on a User Model, that was obtained
from the get_full_name() method, you would do this:
>>> # u is a User instance
>>> u = User.objects.get(username='testuser')
>>> u.json(fields={'name': 'get_full_name'})
'{"name": "Test User"}'
As it stands, you can only use attribute names that do not already exist on the
model. Using an existing name will simply cause the remapping to be ignored
The value in the dictionary corresponding to the custom field name (as the key)
can also be a callable, like so (in combination with the above):
>>> def say_hello():
... return "Hello"
>>>
>>> u.json(fields={'name': 'get_full_name', 'greeting': say_hello})
'{"name": "Test User", "greeting": "Hello"}'
To use field name remapping on related fields, eg. ForeignKeys or their reverse
relation, use the expanded tuple form:
>>> u.json(fields=[
... ('answers' , ('response', 'updated'),
... {'resp': 'response', 'up': 'updated'}
... )
... ])
'{"answers": [{"resp": "Answer, User 2, Wks 1, Qn 1", "up": 1302859482.0}, {"resp": "Answer, User 2, Wks 1, Qn 2", "up": 1302859482.0}, {"resp": "Answer, User 2, Wks 1, Qn 3", "up": 1302859482.0}, {"resp": "Answer, User 2, Wks 2, Qn 1", "up": 1302859482.0}, {"resp": "Answer, User 2, Wks 2, Qn 2", "up": 1302859482.0}, {"resp": "Answer, User 2, Wks 2, Qn 3", "up": 1302859482.0}, {"resp": "Answer, User 2, Wks 3, Qn 1", "up": 1302859482.0}, {"resp": "Answer, User 2, Wks 3, Qn 2", "up": 1302859482.0}, {"resp": "Answer, User 2, Wks 3, Qn 3", "up": 1302859482.0}]}'
In this form, the reverse relation 'answers' is followed on the User model,
retrieving related Answers.
The second tuple tells the JSON renderer which fields are to be retrieved from
the database. This defaults to all model fields.
The last parameter in the tuple, the dictionary, defines the remapping for field
names in the format {'new_name': 'orig_name'}
In plain English, the above call means:
- On this User instance
- Retrieve only the 'response' and the 'updated' fields from the database
- Remap the field 'response' to 'resp', and the field 'updated' to 'up'
The result of the above call to .json() returns something similar to the following:
'{"answers": [
{"resp": "Answer, User 2, Wks 1, Qn 1", "up": 1304446496.0},
{"resp": "Answer, User 2, Wks 1, Qn 2", "up": 1302859482.0},
...
]}'
@param exclude list
List of fields NOT to return.
Defaults to an empty list
You can only exclude fields that would have been in 'fields', default or otherwise
@param as_dict bool
If True, returns a dict, a JSON-formatted string otherwise
Defaults to False
'''
if kwargs.has_key('fields') and len(kwargs['fields']) > 0:
fields = kwargs['fields']
else:
fields = [f.name for f in self._meta.fields]
if kwargs.has_key('exclude'):
exclude = kwargs['exclude']
else:
exclude = []
out = {}
for f in fields:
# Pre-processing
only_fields = []
mapped_fields = {}
if isinstance(f, (tuple, list)):
if len(f) > 3:
raise SyntaxError("Field tuple should have at most 3 values, not %s" % (len(f))) # Take max of 3 values
else:
try:
only_fields = tuple(f[1])
try:
mapped_fields = f[2]
except IndexError:
pass
except IndexError:
only_fields = []
f = f[0] # Original 'f' tuple now destroyed
# Moving swiftly along
if f in exclude:
continue
try:
field = self._meta.get_field_by_name(f)[0]
out[f] = self.__json_render_field(f, field, only_fields, mapped_fields)
except models.fields.FieldDoesNotExist: # Sneaky exception hiding in django.db.fields
if isinstance(fields, (tuple,list)):
field = fields[fields.index(f)]
elif isinstance(fields, dict):
field = fields[f]
# If the secondary parameter is a straight-up callable..
if hasattr(field, '__call__'):
out[f] = field() # .. call it.
# Or is it a callable on self?
elif hasattr(getattr(self, field), '__call__'):
out[f] = getattr(self, field)()
# Or is it just a field by another name?
elif hasattr(self, field):
# Try getting the field from the model again
try:
real_field = self._meta.get_field_by_name(field)[0]
out[f] = self.__json_render_field(field, real_field)
except models.fields.FieldDoesNotExist:
# If we get this exception, chances are, this 'attribute' is a @property method
# So let's just assign it straight
out[f] = getattr(self, field)
else:
raise SyntaxError("Second parameter in field tuple, %s, must be a callable if first parameter, %s, doesn't exist on object: %s" % (field, f, self) )
if kwargs.has_key('as_dict') and kwargs['as_dict'] is True:
return out # Return a dict
else:
return json.dumps(out) # Return a string
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment