My favorites | Sign in
Project Home Downloads Wiki Issues Source
Checkout   Browse   Changes    
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# coding=utf8
from django import forms
from django.conf import settings
from django.utils.safestring import mark_safe
from django.utils.text import truncate_words

from django.contrib import admin
from django.db import models

import operator,settings
from django.contrib.auth.models import Message
from django.http import HttpResponse, HttpResponseNotFound, HttpResponseRedirect
from django.db.models.query import QuerySet
from django.utils.encoding import smart_str

from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
from django.utils.translation import ugettext as _
from django.utils.encoding import force_unicode
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.datastructures import MultiValueDict, MergeDict

class ForeignKeySearchInput(forms.HiddenInput):
"""
A Widget for displaying ForeignKeys in an autocomplete search input
instead in a <select> box.
"""
class Media:
css = {
'all': ('%s/jquery.autocomplete.css' % settings.MEDIA_URL,)
}
js = (
'%s/js/jquery.js' % settings.MEDIA_URL,
'%s/js/jquery.autocomplete.js' % settings.MEDIA_URL,
'%s/autocomplete/AutocompleteObjectLookups.js ' % settings.MEDIA_URL
)

def label_for_value(self, value):
rel_name = self.search_fields[0].split('__')[0]

key = self.rel.get_related_field().name
obj = self.rel.to._default_manager.get(**{key: value})

return getattr(obj,rel_name)

def __init__(self, rel, search_fields, attrs=None):
self.rel = rel
self.search_fields = search_fields
super(ForeignKeySearchInput, self).__init__(attrs)

def render(self, name, value, attrs=None):
if attrs is None:
attrs = {}
rendered = super(ForeignKeySearchInput, self).render(name, value, attrs)
if value:
label = self.label_for_value(value)
else:
label = u''
return rendered + mark_safe(u'''
<input type="text" id="lookup_%(name)s" value="%(label)s" size="40"/>
<script type="text/javascript">

function addItem_id_%(name)s(id, name) {

$("#id_%(name)s").val( id );
$("#lookup_%(name)s").val( name );
}

$(document).ready(function(){

function liFormat_%(name)s (row, i, num) {
var result = row[0] ;
return result;
}
function selectItem_%(name)s(li) {
if( li == null ) var sValue = '';
if( !!li.extra ) var sValue = li.extra[0];
else var sValue = li.selectValue;
$("#id_%(name)s").val( sValue );
}

// --- Autocomplete ---
$("#lookup_%(name)s").autocomplete("../search/", {
extraParams: {
search_fields: '%(search_fields)s',
app_label: '%(app_label)s',
model_name: '%(model_name)s',
},
delay:10,
minChars:2,
matchSubset:1,
autoFill:true,
matchContains:1,
cacheLength:10,
selectFirst:true,
formatItem:liFormat_%(name)s,
maxItemsToShow:10,
onItemSelect:selectItem_%(name)s
});
// --- Autocomplete ---
});
</script>

''') % {
'search_fields': ','.join(self.search_fields),
'MEDIA_URL': settings.MEDIA_URL,
'model_name': self.rel.to._meta.module_name,
'app_label': self.rel.to._meta.app_label,
'label': label,
'name': name,
'value': value,
}


class ManyToManySearchInput(forms.MultipleHiddenInput):
"""
A Widget for displaying ForeignKeys in an autocomplete search input
instead in a <select> box.
"""
class Media:
css = {
'all': ('%s/jquery.autocomplete.css' % settings.MEDIA_URL,)
}
js = (
'%s/js/jquery.js' % settings.MEDIA_URL,
'%s/js/jquery.autocomplete.js' % settings.MEDIA_URL,
'%s/autocomplete/AutocompleteObjectLookups.js ' % settings.MEDIA_URL
)


def __init__(self, rel, search_fields, attrs=None):
self.rel = rel
self.search_fields = search_fields
super(ManyToManySearchInput, self).__init__(attrs)
# self.help_text = u"To search, enter at least two characters"
self.help_text = u"Для поиска укажите хотя бы два символа"

def value_from_datadict(self, data, files, name):
if isinstance(data, (MultiValueDict, MergeDict)):
res = data.getlist(name)
else:
res = data.get(name, None)
print name, res
for id in res:
print self.rel.to.objects.get(pk=id)
return res

def render(self, name, value, attrs=None):
if attrs is None:
attrs = {}

if value is None:
value = []

label = ''
selected = ''
rel_name = self.search_fields[0].split('__')[0]

for id in value:
obj = self.rel.to.objects.get(pk=id)

selected = selected + mark_safe(u"""
<div class="to_delete deletelink" ><input type="hidden" name="%(name)s" value="%(value)s"/>%(label)s</div>"""
)%{
'label': getattr(obj,rel_name),
'name': name,
'value': obj.id,
}


return mark_safe(u'''
<input type="text" id="lookup_%(name)s" value="" size="40"/>%(label)s
<div style="float:left; padding-left:105px; width:300px;">
<font style="color:#999999;font-size:10px !important;">%(help_text)s</font>
<div id="box_%(name)s" style="padding-left:20px;cursor:pointer;">

%(selected)s
</div></div>

<script type="text/javascript">

function addItem_id_%(name)s(id,name) {
// --- add new element from popup ---
$('<div class="to_delete deletelink"><input type="hidden" name="%(name)s" value="'+id+'"/>'+name+'</div>')
.click(function () {$(this).remove();})
.appendTo("#box_%(name)s");

$("#lookup_%(name)s").val( '' );
}

$(document).ready(function(){

function liFormat_%(name)s (row, i, num) {
var result = row[0] ;
return result;
}
function selectItem_%(name)s(li) {
if( li == null ) return

// --- new element ---
$('<div class="to_delete deletelink"><input type="hidden" name="%(name)s" value="'+li.extra[0]+'"/>'+li.selectValue+'</div>')
.click(function () {$(this).remove();})
.appendTo("#box_%(name)s");

$("#lookup_%(name)s").val( '' );
}

// --- Autocomplete ---
$("#lookup_%(name)s").autocomplete("../search/", {
extraParams: {
search_fields: '%(search_fields)s',
app_label: '%(app_label)s',
model_name: '%(model_name)s',
},
delay:10,
minChars:2,
matchSubset:1,
autoFill:false,
matchContains:1,
cacheLength:10,
selectFirst:true,
formatItem:liFormat_%(name)s,
maxItemsToShow:10,
onItemSelect:selectItem_%(name)s
});
// --- delete initial element ---
$(".to_delete").click(function () {$(this).remove();});
});
</script>

''') % {
'search_fields': ','.join(self.search_fields),
'model_name': self.rel.to._meta.module_name,
'app_label': self.rel.to._meta.app_label,
'label': label,
'name': name,
'value': value,
'selected':selected,
'help_text':self.help_text
}

class AutocompleteModelAdmin(admin.ModelAdmin):
def __call__(self, request, url):
if url is None:
pass
elif url == 'search':
return self.search(request)
return super(AutocompleteModelAdmin, self).__call__(request, url)

def search(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)

#print '-----------------------'
#print search_fields, app_label, model_name, query

if search_fields and app_label and model_name and query:
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)
q = None
for field_name in search_fields.split(','):
name = construct_search(field_name)
#print name,'=',query
if q:
q = q | models.Q( **{str(name):query} )
else:
q = models.Q( **{str(name):query} )
qs = model.objects.filter( q )

rel_name = field_name.split('__')[0]

data = ''.join([u'%s|%s\n' % (getattr(f,rel_name), f.pk) for f in qs])
# data = ''.join([u'%s|%s\n' % (f.__unicode__(), f.pk) for f in qs])
return HttpResponse(data)
return HttpResponseNotFound()

def formfield_for_dbfield(self, db_field, **kwargs):
# For ForeignKey use a special Autocomplete widget.
if isinstance(db_field, models.ForeignKey) and db_field.name in self.related_search_fields:
kwargs['widget'] = ForeignKeySearchInput(db_field.rel,
self.related_search_fields[db_field.name])

# extra HTML to the end of the rendered output.
if 'request' in kwargs.keys():
kwargs.pop('request')

formfield = db_field.formfield(**kwargs)
# Don't wrap raw_id fields. Their add function is in the popup window.
if not db_field.name in self.raw_id_fields:
# formfield can be None if it came from a OneToOneField with
# parent_link=True
if formfield is not None:
formfield.widget = AutocompleteWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)
return formfield

# For ManyToManyField use a special Autocomplete widget.
if isinstance(db_field, models.ManyToManyField)and db_field.name in self.related_search_fields:
kwargs['widget'] = ManyToManySearchInput(db_field.rel,
self.related_search_fields[db_field.name])
db_field.help_text = ''

# extra HTML to the end of the rendered output.
if 'request' in kwargs.keys():
kwargs.pop('request')

formfield = db_field.formfield(**kwargs)
# Don't wrap raw_id fields. Their add function is in the popup window.
if not db_field.name in self.raw_id_fields:
# formfield can be None if it came from a OneToOneField with
# parent_link=True
if formfield is not None:
formfield.widget = AutocompleteWidgetWrapper(formfield.widget, db_field.rel, self.admin_site)
return formfield


return super(AutocompleteModelAdmin, self).formfield_for_dbfield(db_field, **kwargs)

def response_add(self, request, obj, post_url_continue='../%s/'):
"""
Determines the HttpResponse for the add_view stage.
"""
opts = obj._meta
pk_value = obj._get_pk_val()

msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
if request.POST.has_key("_continue"):
self.message_user(request, msg + ' ' + _("You may edit it again below."))
if request.POST.has_key("_popup"):
post_url_continue += "?_popup=%s" % request.POST.get('_popup')
return HttpResponseRedirect(post_url_continue % pk_value)

if request.POST.has_key("_popup"):
#htturn response to Autocomplete PopUp
if request.POST.has_key("_popup"):
return HttpResponse('<script type="text/javascript">opener.dismissAutocompletePopup(window, "%s", "%s");</script>' % (escape(pk_value), escape(obj)))

elif request.POST.has_key("_addanother"):
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
return HttpResponseRedirect(request.path)
else:
self.message_user(request, msg)

# Figure out where to redirect. If the user has change permission,
# redirect to the change-list page for this object. Otherwise,
# redirect to the admin index.
if self.has_change_permission(request, None):
post_url = '../'
else:
post_url = '../../../'
return HttpResponseRedirect(post_url)

class AutocompleteWidgetWrapper(RelatedFieldWidgetWrapper):
def render(self, name, value, *args, **kwargs):
rel_to = self.rel.to
related_url = '../../../%s/%s/' % (rel_to._meta.app_label, rel_to._meta.object_name.lower())
self.widget.choices = self.choices
output = [self.widget.render(name, value, *args, **kwargs)]
if rel_to in self.admin_site._registry: # If the related object has an admin interface:
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
output.append(u'<a href="%sadd/" class="add-another" id="add_id_%s" onclick="return showAutocompletePopup(this);"> ' % \
(related_url, name))
output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another')))
return mark_safe(u''.join(output))

Change log

r6 by dimasbka on Jun 25, 2009   Diff
Fixed bug with display of results
Go to: 
Sign in to write a code review

Older revisions

r5 by dimasbka on Jun 1, 2009   Diff
[No log message]
r4 by dimasbka on May 3, 2009   Diff
[No log message]
r2 by dimasbka on Feb 22, 2009   Diff
init
All revisions of this file

File info

Size: 12860 bytes, 383 lines
Powered by Google Project Hosting