diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..1412a46
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,16 @@
+Django CMS
+==========
+* Patrick Lauber
+* Jason Zylks
+* Simon Meers
+* Peter Cicman
+
+Django Page CMS
+===============
+* Batiste Bieler
+* Jannis Leidel
+* Antoni Aloy López
+* Benjamin Wohlwend
+* poweredbypenguins
+* homebrew79
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2445846
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2009, Patrick Lauber
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of the author nor the names of other
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a68e211
--- /dev/null
+++ b/README.md
@@ -0,0 +1,26 @@
+Django Smart Selects
+====================
+
+If you the following model:
+
+ class Location(models.Model)
+ continent = models.ForeignKey(Continent)
+ country = models.ForeignKey(Country)
+ area = models.ForeignKey(Area)
+ city = models.CharField(max_length=50)
+ street = models.CharField(max_length=100)
+
+And you want that if you select a continent only the countries are available that are located on this continent and the same for areas
+you can do the following:
+
+class Location(models.Model)
+ continent = models.ForeignKey(Continent)
+ country = ChainedForeignKey(Country, chained_field="continent", chained_model_field="continent")
+ area = ChainedForeignKey(Area, chained_field="country", chained_model_field="country")
+ city = models.CharField(max_length=50)
+ street = models.CharField(max_length=100)
+
+This example asumes that the Country Model has a "continent" field and that the Area model has "country" field.
+
+The chained field is the field on the same model the field should be chained too.
+The chained model field is the field of the chained model that corresponds to the model linked too by the chained field.
\ No newline at end of file
diff --git a/smart_selects/__init__.py b/smart_selects/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/smart_selects/fields.py b/smart_selects/fields.py
new file mode 100644
index 0000000..89de20f
--- /dev/null
+++ b/smart_selects/fields.py
@@ -0,0 +1,118 @@
+from django.db.models.fields.related import ForeignKey
+from django import forms
+from django.forms.models import ModelChoiceField
+from django.forms.fields import ChoiceField
+from django.forms.widgets import Select
+from django.core.urlresolvers import reverse
+from django.utils.safestring import mark_safe
+from django.conf import settings
+from django.db.models.loading import get_model
+
+JQ_URL = getattr(settings, 'JQUERY_URL', settings.MEDIA_URL + 'js/jquery/jquery-latest.js')
+
+class ChainedForeignKey(ForeignKey):
+ """
+ chains the choices of a previous combo box with this one
+ """
+ def __init__(self, to, chained_field, chained_model_field, *args, **kwargs):
+ self.app_name = to._meta.app_label
+ self.model_name = to._meta.object_name
+ self.chain_field = chained_field
+ self.model_field = chained_model_field
+ ForeignKey.__init__(self, to, *args, **kwargs)
+
+
+ def formfield(self, **kwargs):
+ defaults = {
+ 'form_class': ChainedModelChoiceField,
+ 'queryset': self.rel.to._default_manager.complex_filter(self.rel.limit_choices_to),
+ 'to_field_name': self.rel.field_name,
+ 'app_name':self.app_name,
+ 'model_name':self.model_name,
+ 'chain_field':self.chain_field,
+ 'model_field':self.model_field,
+ }
+ defaults.update(kwargs)
+ return super(ChainedForeignKey, self).formfield(**defaults)
+
+class ChainedSelect(Select):
+ def __init__(self, app_name, model_name, chain_field, model_field, *args, **kwargs):
+ self.app_name = app_name
+ self.model_name = model_name
+ self.chain_field = chain_field
+ self.model_field = model_field
+ super(Select, self).__init__(*args, **kwargs)
+
+ class Media:
+ js = (
+ JQ_URL,
+ )
+
+ def render(self, name, value, attrs=None, choices=()):
+ url = "/".join(reverse("chained_filter", kwargs={'app':self.app_name,'model':self.model_name,'field':self.model_field,'value':"1"}).split("/")[:-2])
+ js = """
+
+
+ """ % {"chainfield":self.chain_field, "url":url, "id":attrs['id']}
+ final_choices=[]
+ if value:
+ item = self.queryset.filter(pk=value)[0]
+ pk = getattr(item, self.model_field+"_id")
+ filter={self.model_field:pk}
+ filtered = get_model( self.app_name, self.model_name).objects.filter(**filter)
+ for choice in filtered:
+ final_choices.append((choice.pk, unicode(choice)))
+ for choice in self.choices:
+ self.choices = [choice]
+ break
+ output = super(ChainedSelect, self).render(name, value, attrs, choices=final_choices)
+ output += js
+ return mark_safe(output)
+
+
+class ChainedModelChoiceField(ModelChoiceField):
+ def __init__(self, app_name, model_name, chain_field, model_field, initial=None, *args, **kwargs):
+ defaults = {'widget':ChainedSelect(app_name,model_name,chain_field,model_field)}
+ defaults.update(kwargs)
+ super(ChainedModelChoiceField, self).__init__(initial=initial, *args, **defaults)
+
+ #widget = ChainedSelect
+ def _get_choices(self):
+ self.widget.queryset = self.queryset
+ choices = super(ChainedModelChoiceField, self)._get_choices()
+ return choices
+ if hasattr(self, '_choices'):
+ return self._choices
+
+ final = [("","---------"),]
+ return final
+ choices = property(_get_choices, ChoiceField._set_choices)
+
+
+
diff --git a/smart_selects/models.py b/smart_selects/models.py
new file mode 100644
index 0000000..e69de29
diff --git a/smart_selects/urls.py b/smart_selects/urls.py
new file mode 100644
index 0000000..096d012
--- /dev/null
+++ b/smart_selects/urls.py
@@ -0,0 +1,5 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('chained_selects.views',
+ url(r'^(?P[\w\-]+)/(?P[\w\-]+)/(?P[\w\-]+)/(?P[\w\-]+)/$', 'filterchain', name='chained_filter'),
+)
\ No newline at end of file
diff --git a/smart_selects/views.py b/smart_selects/views.py
new file mode 100644
index 0000000..e30ca81
--- /dev/null
+++ b/smart_selects/views.py
@@ -0,0 +1,13 @@
+from django.db.models import get_model
+from django.http import HttpResponse
+from django.utils import simplejson
+
+def filterchain(request, app, model, field, value):
+ Model = get_model(app, model)
+ keywords = {str(field): str(value)}
+ results = Model.objects.filter(**keywords)
+ result = []
+ for item in results:
+ result.append({'value':item.pk, 'display':unicode(item)})
+ json = simplejson.dumps(result)
+ return HttpResponse(json, mimetype='application/json')