forked from 0x2620/pandora
include copy of django_extension, dont install django from git
This commit is contained in:
parent
055018f12e
commit
3f7215035a
200 changed files with 14119 additions and 4 deletions
145
contrib/django_extensions/django_extensions/admin/__init__.py
Normal file
145
contrib/django_extensions/django_extensions/admin/__init__.py
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
#
|
||||
# Autocomplete feature for admin panel
|
||||
#
|
||||
# Most of the code has been written by Jannis Leidel and was updated a bit
|
||||
# for django_extensions.
|
||||
# http://jannisleidel.com/2008/11/autocomplete-form-widget-foreignkey-model-fields/
|
||||
#
|
||||
# to_string_function, Satchmo adaptation and some comments added by emes
|
||||
# (Michal Salaban)
|
||||
#
|
||||
|
||||
import six
|
||||
import operator
|
||||
from six.moves import reduce
|
||||
from django.http import HttpResponse, HttpResponseNotFound
|
||||
from django.db import models
|
||||
from django.db.models.query import QuerySet
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.text import get_text_list
|
||||
try:
|
||||
from functools import update_wrapper
|
||||
assert update_wrapper
|
||||
except ImportError:
|
||||
from django.utils.functional import update_wrapper
|
||||
|
||||
from django_extensions.admin.widgets import ForeignKeySearchInput
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
if 'reversion' in settings.INSTALLED_APPS:
|
||||
from reversion.admin import VersionAdmin as ModelAdmin
|
||||
assert ModelAdmin
|
||||
else:
|
||||
from django.contrib.admin import ModelAdmin
|
||||
|
||||
|
||||
class ForeignKeyAutocompleteAdmin(ModelAdmin):
|
||||
"""Admin class for models using the autocomplete feature.
|
||||
|
||||
There are two additional fields:
|
||||
- related_search_fields: defines fields of managed model that
|
||||
have to be represented by autocomplete input, together with
|
||||
a list of target model fields that are searched for
|
||||
input string, e.g.:
|
||||
|
||||
related_search_fields = {
|
||||
'author': ('first_name', 'email'),
|
||||
}
|
||||
|
||||
- related_string_functions: contains optional functions which
|
||||
take target model instance as only argument and return string
|
||||
representation. By default __unicode__() method of target
|
||||
object is used.
|
||||
"""
|
||||
|
||||
related_search_fields = {}
|
||||
related_string_functions = {}
|
||||
|
||||
def get_urls(self):
|
||||
try:
|
||||
from django.conf.urls import patterns, url
|
||||
except ImportError: # django < 1.4
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
def wrap(view):
|
||||
def wrapper(*args, **kwargs):
|
||||
return self.admin_site.admin_view(view)(*args, **kwargs)
|
||||
return update_wrapper(wrapper, view)
|
||||
|
||||
info = self.model._meta.app_label, self.model._meta.module_name
|
||||
|
||||
urlpatterns = patterns('', url(r'foreignkey_autocomplete/$', wrap(self.foreignkey_autocomplete), name='%s_%s_autocomplete' % info))
|
||||
urlpatterns += super(ForeignKeyAutocompleteAdmin, self).get_urls()
|
||||
return urlpatterns
|
||||
|
||||
def foreignkey_autocomplete(self, request):
|
||||
"""
|
||||
Searches in the fields of the given related model and returns the
|
||||
result as a simple string to be used by the jQuery Autocomplete plugin
|
||||
"""
|
||||
query = request.GET.get('q', None)
|
||||
app_label = request.GET.get('app_label', None)
|
||||
model_name = request.GET.get('model_name', None)
|
||||
search_fields = request.GET.get('search_fields', None)
|
||||
object_pk = request.GET.get('object_pk', None)
|
||||
try:
|
||||
to_string_function = self.related_string_functions[model_name]
|
||||
except KeyError:
|
||||
to_string_function = lambda x: x.__unicode__()
|
||||
if search_fields and app_label and model_name and (query or object_pk):
|
||||
def construct_search(field_name):
|
||||
# use different lookup methods depending on the notation
|
||||
if field_name.startswith('^'):
|
||||
return "%s__istartswith" % field_name[1:]
|
||||
elif field_name.startswith('='):
|
||||
return "%s__iexact" % field_name[1:]
|
||||
elif field_name.startswith('@'):
|
||||
return "%s__search" % field_name[1:]
|
||||
else:
|
||||
return "%s__icontains" % field_name
|
||||
model = models.get_model(app_label, model_name)
|
||||
queryset = model._default_manager.all()
|
||||
data = ''
|
||||
if query:
|
||||
for bit in query.split():
|
||||
or_queries = [models.Q(**{construct_search(smart_str(field_name)): smart_str(bit)}) for field_name in search_fields.split(',')]
|
||||
other_qs = QuerySet(model)
|
||||
other_qs.dup_select_related(queryset)
|
||||
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
|
||||
queryset = queryset & other_qs
|
||||
data = ''.join([six.u('%s|%s\n' % (to_string_function(f), f.pk)) for f in queryset])
|
||||
elif object_pk:
|
||||
try:
|
||||
obj = queryset.get(pk=object_pk)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
data = to_string_function(obj)
|
||||
return HttpResponse(data)
|
||||
return HttpResponseNotFound()
|
||||
|
||||
def get_help_text(self, field_name, model_name):
|
||||
searchable_fields = self.related_search_fields.get(field_name, None)
|
||||
if searchable_fields:
|
||||
help_kwargs = {
|
||||
'model_name': model_name,
|
||||
'field_list': get_text_list(searchable_fields, _('and')),
|
||||
}
|
||||
return _('Use the left field to do %(model_name)s lookups in the fields %(field_list)s.') % help_kwargs
|
||||
return ''
|
||||
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
"""
|
||||
Overrides the default widget for Foreignkey fields if they are
|
||||
specified in the related_search_fields class attribute.
|
||||
"""
|
||||
if (isinstance(db_field, models.ForeignKey) and db_field.name in self.related_search_fields):
|
||||
model_name = db_field.rel.to._meta.object_name
|
||||
help_text = self.get_help_text(db_field.name, model_name)
|
||||
if kwargs.get('help_text'):
|
||||
help_text = six.u('%s %s' % (kwargs['help_text'], help_text))
|
||||
kwargs['widget'] = ForeignKeySearchInput(db_field.rel, self.related_search_fields[db_field.name])
|
||||
kwargs['help_text'] = help_text
|
||||
return super(ForeignKeyAutocompleteAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
94
contrib/django_extensions/django_extensions/admin/widgets.py
Normal file
94
contrib/django_extensions/django_extensions/admin/widgets.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import six
|
||||
import django
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.admin.sites import site
|
||||
from django.utils.safestring import mark_safe
|
||||
if django.get_version() >= "1.4":
|
||||
from django.utils.text import Truncator
|
||||
else:
|
||||
from django.utils.text import truncate_words
|
||||
from django.template.loader import render_to_string
|
||||
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
|
||||
|
||||
|
||||
class ForeignKeySearchInput(ForeignKeyRawIdWidget):
|
||||
"""
|
||||
A Widget for displaying ForeignKeys in an autocomplete search input
|
||||
instead in a <select> box.
|
||||
"""
|
||||
# Set in subclass to render the widget with a different template
|
||||
widget_template = None
|
||||
# Set this to the patch of the search view
|
||||
search_path = '../foreignkey_autocomplete/'
|
||||
|
||||
def _media(self):
|
||||
js_files = ['django_extensions/js/jquery.bgiframe.min.js',
|
||||
'django_extensions/js/jquery.ajaxQueue.js',
|
||||
'django_extensions/js/jquery.autocomplete.js']
|
||||
return forms.Media(css={'all': ('django_extensions/css/jquery.autocomplete.css',)},
|
||||
js=js_files)
|
||||
|
||||
media = property(_media)
|
||||
|
||||
def label_for_value(self, value):
|
||||
key = self.rel.get_related_field().name
|
||||
obj = self.rel.to._default_manager.get(**{key: value})
|
||||
if django.get_version() >= "1.4":
|
||||
return Truncator(obj).words(14, truncate='...')
|
||||
else:
|
||||
return truncate_words(obj, 14)
|
||||
|
||||
def __init__(self, rel, search_fields, attrs=None):
|
||||
self.search_fields = search_fields
|
||||
if django.get_version() >= "1.4":
|
||||
super(ForeignKeySearchInput, self).__init__(rel, site, attrs)
|
||||
else:
|
||||
super(ForeignKeySearchInput, self).__init__(rel, attrs)
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs is None:
|
||||
attrs = {}
|
||||
#output = [super(ForeignKeySearchInput, self).render(name, value, attrs)]
|
||||
opts = self.rel.to._meta
|
||||
app_label = opts.app_label
|
||||
model_name = opts.object_name.lower()
|
||||
related_url = '../../../%s/%s/' % (app_label, model_name)
|
||||
params = self.url_parameters()
|
||||
if params:
|
||||
url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()])
|
||||
else:
|
||||
url = ''
|
||||
if not 'class' in attrs:
|
||||
attrs['class'] = 'vForeignKeyRawIdAdminField'
|
||||
# Call the TextInput render method directly to have more control
|
||||
output = [forms.TextInput.render(self, name, value, attrs)]
|
||||
if value:
|
||||
label = self.label_for_value(value)
|
||||
else:
|
||||
label = six.u('')
|
||||
|
||||
try:
|
||||
admin_media_prefix = settings.ADMIN_MEDIA_PREFIX
|
||||
except AttributeError:
|
||||
admin_media_prefix = settings.STATIC_URL + "admin/"
|
||||
|
||||
context = {
|
||||
'url': url,
|
||||
'related_url': related_url,
|
||||
'admin_media_prefix': admin_media_prefix,
|
||||
'search_path': self.search_path,
|
||||
'search_fields': ','.join(self.search_fields),
|
||||
'model_name': model_name,
|
||||
'app_label': app_label,
|
||||
'label': label,
|
||||
'name': name,
|
||||
'pre_django_14': (django.VERSION[:2] < (1, 4)),
|
||||
}
|
||||
output.append(render_to_string(self.widget_template or (
|
||||
'django_extensions/widgets/%s/%s/foreignkey_searchinput.html' % (app_label, model_name),
|
||||
'django_extensions/widgets/%s/foreignkey_searchinput.html' % app_label,
|
||||
'django_extensions/widgets/foreignkey_searchinput.html',
|
||||
), context))
|
||||
output.reverse()
|
||||
return mark_safe(six.u('').join(output))
|
||||
Loading…
Add table
Add a link
Reference in a new issue