Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
326 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
*.egg-info | ||
*.egg | ||
build | ||
dist | ||
.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,11 @@ | ||
language: python | ||
python: | ||
- "2.5" | ||
- "2.6" | ||
- "2.7" | ||
- "3.2" | ||
- "pypy" | ||
# command to install dependencies | ||
install: | ||
- pip install . --use-mirrors | ||
- pip install -r requirements.txt --use-mirrors | ||
# command to run tests | ||
script: nosetests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
from django.db.models import base | ||
from python_utils import converters | ||
|
||
|
||
class ModelBase(base.ModelBase): | ||
''' | ||
Model base with more readable naming convention | ||
Example: | ||
Assuming the model is called `app.FooBarObject` | ||
Default Django table name: `app_foobarobject` | ||
Table name with this base: `app_foo_bar_object` | ||
''' | ||
|
||
def __new__(cls, name, bases, attrs): | ||
class_ = base.ModelBase.__new__(cls, name, bases, attrs) | ||
module_name = converters.camel_to_underscore(name) | ||
|
||
app_label = class_.__module__.split('.')[-2] | ||
db_table = '%s_%s' % (app_label, module_name) | ||
if not getattr(class_._meta, 'proxy', False): | ||
class_._meta.db_table = db_table | ||
|
||
return class_ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
from django.core.management.base import BaseCommand | ||
from django.db.models.loading import get_models | ||
from django.db import models | ||
|
||
LIST_FILTER = ( | ||
models.DateField, | ||
models.DateTimeField, | ||
models.ForeignKey, | ||
models.BooleanField, | ||
) | ||
|
||
SEARCH_FIELD = ( | ||
'name', | ||
'slug', | ||
) | ||
|
||
DATE_HIERARCHY = ( | ||
'created_at', | ||
'updated_at', | ||
'joined_at', | ||
) | ||
|
||
PREPOPULATED_FIELDS = { | ||
'slug': ('name',) | ||
} | ||
|
||
|
||
class Command(BaseCommand): | ||
|
||
def handle(self, *args, **kwargs): | ||
apps = [] | ||
self.models = [] | ||
|
||
installed_apps = dict((a.__name__.rsplit('.', 1)[0], a) | ||
for a in models.get_apps()) | ||
for arg in args: | ||
app = installed_apps.get(arg) | ||
if app: | ||
apps.append(app) | ||
else: | ||
self.models.append(arg.lower()) | ||
|
||
for app in apps: | ||
self.handle_app(app, **kwargs) | ||
|
||
def handle_app(self, app, **options): | ||
models_dict = {} | ||
|
||
for model in get_models(app): | ||
name = model.__name__ | ||
field_names = [] | ||
|
||
if self.models and name.lower() not in self.models: | ||
continue | ||
|
||
models_dict[name] = model_dict = { | ||
'list_display': [], | ||
'list_filter': [], | ||
'raw_id_fields': [], | ||
'search_fields': [], | ||
'prepopulated_fields': {}, | ||
'date_hierarchy': None, | ||
} | ||
|
||
parent_fields = model._meta.parents.values() | ||
|
||
for field in model._meta.fields: | ||
if field in parent_fields: | ||
continue | ||
|
||
field_names.append(field.name) | ||
model_dict['list_display'].append(field.name) | ||
|
||
if isinstance(field, LIST_FILTER): | ||
if isinstance(field, models.ForeignKey): | ||
related_objects = field.related.parent_model.objects | ||
if related_objects.all()[:100].count() <= 100: | ||
model_dict['list_filter'].append(field.name) | ||
else: | ||
model_dict['raw_id_fields'].append(field.name) | ||
else: | ||
model_dict['list_filter'].append(field.name) | ||
|
||
if field.name in SEARCH_FIELD: | ||
model_dict['search_fields'].append(field.name) | ||
|
||
for field_name in DATE_HIERARCHY: | ||
if field_name in field_names \ | ||
and not model_dict['date_hierarchy']: | ||
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: | ||
if v not in field_names: | ||
incomplete = True | ||
break | ||
|
||
if not incomplete: | ||
model_dict['prepopulated_fields'][k] = vs | ||
|
||
print 'import models' | ||
print 'from django.contrib import admin' | ||
|
||
for name, model in sorted(models_dict.iteritems()): | ||
print '\n\nclass %sAdmin(admin.ModelAdmin):' % name | ||
for k, v in sorted(model.iteritems()): | ||
if v: | ||
if isinstance(v, (list, set)): | ||
v = tuple(v) | ||
|
||
row = ' %s = %r' % (k, v) | ||
row_parts = [] | ||
while len(row) > 78: | ||
pos = row.rfind(' ', 0, 78) | ||
row_parts.append(row[:pos]) | ||
row = ' ' + row[pos:] | ||
|
||
row_parts.append(row) | ||
|
||
print '\n'.join(row_parts) | ||
|
||
print '\n' | ||
for name in sorted(models_dict): | ||
print 'admin.site.register(models.%s, %sAdmin)' % (name, name) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import anyjson | ||
from django import shortcuts as django_shortcuts | ||
from django import http | ||
from django.template import RequestContext | ||
from django.contrib.auth import decorators | ||
|
||
|
||
class ViewError(Exception): | ||
pass | ||
|
||
|
||
class UnknownViewResponseError(ViewError): | ||
pass | ||
|
||
REQUEST_PROPERTIES = { | ||
'redirect': http.HttpResponseRedirect, | ||
'permanent_redirect': http.HttpResponsePermanentRedirect, | ||
'not_found': http.HttpResponseNotFound, | ||
} | ||
|
||
|
||
def _prepare_request(request, app, view): | ||
'''Add context and extra methods to the request''' | ||
request.context = RequestContext(request) | ||
request.context['view'] = view | ||
request.context['app'] = app | ||
|
||
for k, v in REQUEST_PROPERTIES.iteritems(): | ||
setattr(request, k, v) | ||
|
||
return request | ||
|
||
|
||
def _process_response(request, response): | ||
'''Generic response processing function, always returns HttpResponse''' | ||
|
||
'''If we add something to the context stack, pop it after adding''' | ||
pop = False | ||
|
||
try: | ||
if isinstance(response, (dict, list)): | ||
if request.ajax: | ||
output = json = anyjson.serialize(response) | ||
callback = request.GET.get('callback', False) | ||
if callback: | ||
output = '%s(%s)' % (callback, json) | ||
if request.GET.get('debug'): | ||
title = 'Rendering %(view)r in module %(app)r' % ( | ||
request.context) | ||
|
||
output = ''' | ||
<html> | ||
<head> | ||
<title>%s</title> | ||
</head> | ||
<body> | ||
<textarea>%s</textarea> | ||
</body> | ||
</html> | ||
''' % (title, output) | ||
response = http.HttpResponse(output, | ||
mimetype='text/html') | ||
else: | ||
response = http.HttpResponse(output, | ||
mimetype='text/plain') | ||
|
||
return response | ||
else: | ||
'''Add the dictionary to the context and let | ||
render_to_response handle it''' | ||
request.context.update(response) | ||
response = None | ||
pop = True | ||
|
||
if isinstance(response, http.HttpResponse): | ||
return response | ||
|
||
elif isinstance(response, basestring): | ||
if request.ajax: | ||
return http.HttpResponse(response, mimetype='text/plain') | ||
else: | ||
return http.HttpResponse(response) | ||
|
||
elif response is None: | ||
render_to_response = django_shortcuts.render_to_response | ||
|
||
return render_to_response(request.template, | ||
context_instance=request.context) | ||
|
||
else: | ||
raise UnknownViewResponseError( | ||
'"%s" is an unsupported response type' % type(response)) | ||
finally: | ||
if pop: | ||
request.context.pop() | ||
|
||
|
||
def env(function=None, login_required=False): | ||
''' | ||
View decorator that automatically adds context and renders response | ||
Keyword arguments: | ||
login_required -- is everyone allowed or only authenticated users | ||
Adds a RequestContext (request.context) with the following context items: | ||
name -- current function name | ||
Stores the template in request.template and assumes it to be in | ||
<app>/<view>.html | ||
''' | ||
|
||
def _env(request, *args, **kwargs): | ||
request.ajax = request.is_ajax() or bool(int( | ||
request.REQUEST.get('ajax', 0))) | ||
request.context = None | ||
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 | ||
|
||
response = function(request, *args, **kwargs) | ||
|
||
return _process_response(request, response) | ||
finally: | ||
'''Remove the context reference from request to prevent leaking''' | ||
try: | ||
del request.context, request.template | ||
for k, v in REQUEST_PROPERTIES.iteritems(): | ||
delattr(request, k) | ||
except AttributeError: | ||
pass | ||
|
||
if function: | ||
_env.__name__ = function.__name__ | ||
_env.__doc__ = function.__doc__ | ||
_env.__module__ = function.__module__ | ||
_env.__dict__ = function.__dict__ | ||
|
||
if login_required: | ||
return decorators.login_required(_env) | ||
else: | ||
return _env | ||
else: | ||
return lambda f: env(f, login_required) | ||
|