Хорошо, я нашел способ запустить этот фрагмент. Решение довольно хакерское, но оно работает. Было бы здорово, если бы кто-то мог
опубликуйте более удобный способ автозаполнения полей внешнего ключа.
Вариант использования этого решения: У нас есть контактная модель с около 2000 контактов. У нас есть проекты, и мы хотим
назначить ответственного за проект. Этот человек выбран из 2000 контактов. Поэтому простой
Выберите выпадающий не может быть использован. Таким образом, я попытался запустить автозаполнение.
Я пытаюсь объяснить это здесь.
Это две модели:
class Project(models.Model):
name = models.CharField(max_length=100)
project_manager_externally = models.ForeignKey(Contact, null=True, related_name='project_manager_externally')
class Contact(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
Это форма. ProjectForm использует виджет JQueryAutoComplete из http://www.djangosnippets.org/snippets/233/, адаптированный для удовлетворения моих потребностей.
Обратите внимание, что поле project_manager_externally является значением модели, которое мы хотим присвоить с помощью ForeignKey для модели Contact.
Мы скрываем это поле от пользователя с помощью widget = forms.HiddenInput (). Autocomplete.js обновит это поле для пользователя, когда пользователь выберет
запись из автоматически заполненного списка в project_manager_externally_auto_complete.
Обратите внимание, что я ограничиваю выбор всеми доступными контактами с помощью choices = contact_choices (). Поскольку JQueryAutoComplete отображается как поле ввода текста,
пользователь может вставить все, что он / она хочет. С добавлением опции выбора в поле я оставляю правила проверки вплоть до django.
Последний открытый вопрос был сейчас. Что происходит, когда пользователь вводит правильное имя, но не выбирает его поверх выпадающего списка autocomplete.js. поскольку
autocomplete.js обновляет project_manager_externally с правильным контактом pk, контакт pk не будет обновляться, и поэтому мы
не будет сохранять новый вход. Поэтому я перезаписываю чистый метод. Сначала я получаю объект Contact с cleaned_data.get ("project_manager_externally").
Если объект Contact был обновлен с помощью autocomplete.js, я могу быть уверен, что first_name и last_name совпадают со значением в
project_manager_externally_auto_complete. Как вы видите ниже в виджете JQueryAutoComplete, поле ввода заполняется first_name и last_name.
Если они не совпадают, я знаю, что пользователь вставил имя контакта напрямую, без использования выпадающего списка автозаполнения.
from crm.models import Contact
from project_management.models import Project
from ajax_filtered_fields.forms import ForeignKeyByLetter
from django.forms.util import ErrorList
def contact_choices():
for contact in Contact.objects.all():
the_value = contact.first_name + " " + contact.last_name
yield (the_value, the_value)
class ProjectForm(forms.ModelForm):
project_manager_externally_auto_complete = forms.ChoiceField(choices=contact_choices(), label='fake p manager extern', widget=JQueryAutoComplete('/pm/contact_autocomplete'))
project_manager_externally = forms.ModelChoiceField(queryset=Contact.objects.all(), widget=forms.HiddenInput())
def clean(self):
cleaned_data = self.cleaned_data
project_manager_externally_auto_complete = cleaned_data.get("project_manager_externally_auto_complete")
project_manager_externally = cleaned_data.get("project_manager_externally")
if project_manager_externally_auto_complete and project_manager_externally:
the_value = project_manager_externally.first_name + " " + project_manager_externally.last_name
if the_value != project_manager_externally_auto_complete:
msg = u"Please select a value with the drop-down!"
self._errors["project_manager_externally_auto_complete"] = ErrorList([msg])
return cleaned_data
class Meta:
model = Project
exclude = ('created_by',)
class Media:
js = (
settings.MEDIA_URL + "js/jquery.autocomplete.js",
)
css = {
'screen': (settings.MEDIA_URL + "css/jquery.autocomplete.css",),
}
Адаптированный виджет JQueryAutoComplete:
Я просто адаптировал материал возврата js и добавил эту часть.
После успешного вызова автозаполнения функция результата заменит значение атрибута на item [1], тогда как item [1] - это pk выбранного контакта. Увидеть
смотрите ниже для получения более подробной информации о пункте [1]
from django import forms
from django.forms.widgets import flatatt
from django.forms.util import smart_unicode
from django.utils.html import escape
from django.utils.simplejson import JSONEncoder
from django.utils.safestring import mark_safe
class JQueryAutoComplete(forms.TextInput):
def __init__(self, source, options={}, attrs={}):
"""source can be a list containing the autocomplete values or a
string containing the url used for the XHR request.
For available options see the autocomplete sample page::
http://jquery.bassistance.de/autocomplete/"""
self.options = None
self.attrs = {'autocomplete': 'off'}
self.source = source
if len(options) > 0:
self.options = JSONEncoder().encode(options)
self.attrs.update(attrs)
def render_js(self, field_id):
if isinstance(self.source, list):
source = JSONEncoder().encode(self.source)
elif isinstance(self.source, str):
source = "'%s'" % escape(self.source)
else:
raise ValueError('source type is not valid')
options = ''
if self.options:
options += ',%s' % self.options
#in order to reference to the original field we remove the _auto_complete value from the field_id
#note that this is a convention. in order to make this work, each form field using this widget has to be named
#after the field for which it handles the foreign keys + the string '_auto_complete'
field_id_origin = field_id.replace('_auto_complete', '')
return u'$(\'#%s\').autocomplete(%s%s).result(function(event, item) {$(\'#%s\').attr("value", item[1]);});' % (field_id, source, options, field_id_origin)
def render(self, name, value=None, attrs=None):
final_attrs = self.build_attrs(attrs, name=name)
if value:
final_attrs['value'] = escape(smart_unicode(value))
if not self.attrs.has_key('id'):
final_attrs['id'] = 'id_%s' % name
return mark_safe(u'''<input type="text" %(attrs)s/>
<script type="text/javascript"><!--//
%(js)s//--></script>
''' % {
'attrs' : flatatt(final_attrs),
'js' : self.render_js(final_attrs['id']),
})
Вид:
Это также адаптированная часть фрагмента. Важной частью здесь является:
yield '% s% s |% s \ n'% (r.first_name, r.last_name, r.id)
Единственное, что вы должны понимать здесь, это то, что эта строка является причиной того, что вы можете
получить доступ к идентификатору контакта с помощью пункта [1] в ветви результатов вызова автозаполнения выше.
from django.http import HttpResponse, HttpResponseBadRequest
from django.views.decorators.cache import cache_page
from crm.models import Contact
from django.db.models import Q
@cache_page(120)
def contact_autocomplete(request):
print "holy jquery shit"
def iter_results(results):
if results:
for r in results:
yield '%s %s|%s\n' % (r.first_name, r.last_name, r.id)
if not request.GET.get('q'):
return HttpResponse(mimetype='text/plain')
q = request.GET.get('q')
limit = request.GET.get('limit', 15)
try:
limit = int(limit)
except ValueError:
return HttpResponseBadRequest()
contacts = Contact.objects.filter(Q(first_name__istartswith=q) | Q(last_name__istartswith=q))
return HttpResponse(iter_results(contacts), mimetype='text/plain')
Надеюсь, это поможет кому-нибудь внедрить решение автозаполнения для полей внешнего ключа. Я знаю, что есть менее хакерские решения
там, но я не нашел тот, который удовлетворяет мои потребности сегодня. Таким образом, я придумал собственное решение: -)
На самом деле, если у кого-то есть альтернатива для обработки внешних ключей с большим количеством значений, было бы здорово получить сообщение для другого решения.