Django JQuery Autocomplete for Model Selection

  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
# fields.py


# -*- coding: utf-8 -*-
from django import forms
from django.utils.safestring import mark_safe
from django.utils.encoding import force_unicode 
from django.core.urlresolvers import reverse
from django.forms.util import ErrorList, ValidationError
CLIENT_CODE = """
<input type="text" name="%s_text" id="%s_text"/>
<input type="hidden" name="%s" id="%s" value="" />
<script type="text/javascript">
     $(function(){
	function formatItem(row) {
		return row[1] ;
	}
	function formatResult(row) {
                return row[1];
	}
	$("#%s_text").autocomplete('%s', {
                mustMatch: true,
		formatItem: formatItem,
		formatResult: formatResult
	});
	$("#%s_text").result(function(event, data, formatted) {
              $("#%s").val(data[0]);                         

	});

     });
</script>
"""

class ModelAutoCompleteWidget(forms.widgets.TextInput):
    """ widget autocomplete for text fields
    """
    html_id = ''
    def __init__(self, 
                 lookup_url=None, 
                 *args, **kw):
        super(forms.widgets.TextInput, self).__init__(*args, **kw)
        # url for Datasource
        self.lookup_url = lookup_url
       

    def render(self, name, value, attrs=None):
        if value == None:
            value = ''
        html_id = attrs.get('id', name)
        self.html_id = html_id

        lookup_url = self.lookup_url
        detail_url = reverse('ajax_platos_detail')
        return mark_safe(CLIENT_CODE % (name, html_id, name, html_id, html_id,
                                       lookup_url, html_id, html_id, detail_url))


    def value_from_datadict(self, data, files, name):
        """
        Given a dictionary of data and this widget's name, returns the value
        of this widget. Returns None if it's not provided.
        """

        return data.get(name, None)



        
class ModelAutoCompleteField(forms.fields.CharField):
    """
    Autocomplete form field for Model Model
    """
    model = None
    url = None


    def __init__(self, model,  lookup_url, *args, **kwargs):
        self.model, self.url = model, lookup_url
        super(ModelAutoCompleteField, self).__init__(
            widget = ModelAutoCompleteWidget(lookup_url=self.url),
            max_length=255,
            *args, **kwargs)

    def clean(self, value):

        try: 
            obj = self.model.objects.get(pk=value)
        except self.model.DoesNotExist:
            raise ValidationError(u'Invalid item selected')            
        return obj     





# urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns('mymodel.views',
    # ajax
   url(r'^ajax/list/$', 'ajax_mymodel_list',
        name='ajax_mymodel_list'),                           

)


# views.py
from django.http import HttpResponse
from django.template import RequestContext
from mymodel.models import MyModel

def ajax_mymodel_list(request):
    """ returns data displayed at autocomplete list - 
    this function is accessed by AJAX calls
    """
    limit = 10
    query = request.GET.get('q', None)
    # it is up to you how query looks
    if query:
        qargs = [django.db.models.Q(name__istartswith=query)]
        
    instances = MyModel.objects.filter(django.db.models.Q(*qargs))[:limit]

    results = ""
    for item in instances:
        results += "%s|%s \n" %(item.pk,item.name)

    return HttpResponse(results)


# forms.py
from django import forms
from django.core.urlresolvers import reverse
from mymodel.models import MyModel
from utils.fields import ModelAutoCompleteField

class TestForm(forms.Form):
    theField = ModelAutoCompleteField(lookup_url = reverse('ajax_mymodel_list'),
                                   model = MyModel, required=True)

Comments

mirobe (on October 3, 2008):

Can I see record name instead of ID (pk) after choosing the right one from the list?

Also, In the line: "super(ModelAutoCompleteField, self).init(*args, kwargs)"

I am getting: "global name 'args' is not defined" on save()

Thanks

#

elpenia (on October 4, 2008):

This field was designed to retrieve the object selected by the user but you can always access to the pk and the text field values using POST['field'] and POST['field_text'] or GET['field'] and GET['field_text'].

Sorry for the error the line init in clean is wrong, I'm correcting this now, please let me now if it worked and send me any feedback.

Thanks for using it.

#

denvist (on December 2, 2008):

Sorry for my english. I'm trying to use this snippet. But always get this error:

Tried people_add in module mysite.forum.views. Error was: 'module' object has no attribute 'people_add'

urls.py: from django.conf.urls.defaults import * urlpatterns = patterns('', url(r'^people_list/$', mysite.forum.views.people_list', name='people_list'), url(r'^people_add/$', 'mysite.forum.views.people_add', name='people_add'), )

forum/models.py: from django.db import models

class People(models.Model): name = models.CharField(max_length=100)

forum/forms.py: from django import forms from django.core.urlresolvers import reverse from forum.models import People from forum.fields import ModelAutoCompleteField

class TestForm(forms.Form): theField = ModelAutoCompleteField(lookup_url = reverse('mysite.forum.views.people_list'), model = People, required=True)

forum/views.py: from django.http import HttpResponse from django.template import RequestContext from django.shortcuts import render_to_response from django.db.models import Q from forum.models import People from forum.forms import TestForm

def people_list(request): """ returns data displayed at autocomplete list - this function is accessed by AJAX calls """ limit = 10 query = request.GET.get('q', None) # it is up to you how query looks qargs = '' if query: qargs = [Q(name__istartswith=query)]

instances = People.objects.filter(Q(*qargs))[:limit]

results = ""
for item in instances:
    results += "%s|%s \n" %(item.pk,item.name)

return HttpResponse(results)

def people_add(request): form = TestForm() return render_to_response('people_add.html', {'form': form})

fields.py was saved as mysite/forum/fields.py

P.S.:

If forum/forms.py looks like this: class TestForm(forms.Form): theField = forms.CharField(max_length=50)

All work perfectly.

#

(Forgotten your password?)

You may use Markdown syntax here, but raw HTML will be removed.