Hi Kamil and all,

I have finally found what I was looking for by using the branch
newforms-admin.
It could be tidied up a bit, but at least it works!
Hope that will help others.

(Note: all this led me to some other questions, which I have posted
there: 
http://groups.google.com/group/django-users/browse_thread/thread/cab9828c8da63975/bcc49fa7309464c8#bcc49fa7309464c8)

Here is the actual code:

class Country(models.Model):
    name = models.CharField(max_length=50)
    def __unicode__(self):
        return self.name

class City(models.Model):
    name = models.CharField(max_length=50)
    country = models.ForeignKey(Country)
    def __unicode__(self):
        return self.name

class Person(models.Model):
    firstName = models.CharField(max_length=30)
    lastName = models.CharField(max_length=30)
    citiesLived = models.ManyToManyField(City, null=True, blank=True)
    def __unicode__(self):
        return self.firstName + " " + self.lastName



from django.newforms.widgets import *
from django.newforms.fields import MultipleChoiceField
from django.template import Context, Template
from django.newforms.util import flatatt
from django.utils.encoding import force_unicode
from itertools import chain
from django.utils.html import escape
class CitiesWidget(CheckboxSelectMultiple):
    template_string="""<p>{% regroup city_list|dictsort:"country" by
country as cities_by_country %}
                       {% for country in cities_by_country %}
                           <b>{{ country.grouper }}</b><br/>
                           {% for city in country.list|dictsort:"name"
%}
                               {{ city.html }}<br/>
                           {% endfor %}
                           <br/>
                       {% endfor %}</p>
    """
    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = []
        has_id = attrs and 'id' in attrs
        final_attrs = self.build_attrs(attrs, name=name)
        str_values = set([force_unicode(v) for v in value]) #
Normalize to strings.
        city_list = []
        for i, (option_value, option_label) in
enumerate(chain(self.choices, choices)):
            city = City.objects.get(id=option_value)
            # If an ID attribute was given, add a numeric index as a
suffix,
            # so that the checkboxes don't all have the same ID
attribute.
            if has_id:
                final_attrs = dict(final_attrs, id='%s_%s' %
(attrs['id'], i))
            cb = CheckboxInput(final_attrs, check_test=lambda value:
value in str_values)
            option_value = force_unicode(option_value)
            rendered_cb = cb.render(name, option_value)
            html = (u'%s %s' % (rendered_cb,
escape(force_unicode(option_label))))
            city_list.append({"country":city.country.name, "name":
option_label, "html":html})
        t = Template(self.template_string)
        c = Context({"city_list": city_list})
        return t.render(c)

class CitiesField(MultipleChoiceField):
    widget = CitiesWidget



from django.contrib.admin import widgets
class PersonOptions(admin.ModelAdmin):
        def formfield_for_dbfield(self, db_field, **kwargs):
            if db_field.name == 'citiesLived':
                cities = kwargs['initial']
                kwargs['initial'] = [city.id for city in cities]
                formfield = CitiesField(**kwargs)
                formfield.choices = [(city.id, city.name) for city in
City.objects.all()]
                formfield.widget.render =
widgets.RelatedFieldWidgetWrapper(formfield.widget.render,
db_field.rel, self.admin_site)
                return formfield
            else:
                return
super(PersonOptions,self).formfield_for_dbfield(db_field,**kwargs)


admin.site.register(Country)
admin.site.register(City)
admin.site.register(Person, PersonOptions)





On Nov 22, 12:44 am, kamil <[EMAIL PROTECTED]> wrote:
> sorry
> line "{{ group.grouper.name }}: [
> shoud be:
> "{{ group.grouper.name }}": [
>
> On Nov 21, 11:49 am, kamil <[EMAIL PROTECTED]> wrote:
>
>
>
>
>
> > HiJulien
>
> > in fact you don't even have to write view for it you can use generic
> > view  as follows:
>
> > put in your url.py:
> > ---------------------------------------
>
> > from yourproject.cities.models import City
>
> > urlpatterns = patterns('',
> >     ......
> >     (r'^custom_widget.js',
> > 'django.views.generic.list_detail.object_list', { 'queryset':
> > City.objects.all(),  template_name='custom_widget.html' } ),
> >     ............
> > )
>
> > ------------------------------------------
> > with this line you hook all together: model view template and url
> > recommeded read about generic views :)
>
> > good luck
>
> > On Nov 21, 12:14 am,Julien<[EMAIL PROTECTED]> wrote:
>
> > > Thanks again Kamil for your help!
>
> > > Ok, now I'm trying to put everything together. Sorry I'm just starting
> > > with Django and I am still a bit lost.
>
> > > What I am not sure about is:
> > > - where to put the view?
> > > - how to hook the view to the model
> > > - how to hook the template with the view.
>
> > > Could you provide a full example, based on the summarization below?
>
> > > Thanks a lot!
>
> > > models.py:
> > > ---------------
>
> > > class Country(models.Model):
> > >     name = models.CharField(max_length=50)
> > >     def __unicode__(self):
> > >         return self.name
> > >     class Admin:
> > >         pass
>
> > > class City(models.Model):
> > >     name = models.CharField(max_length=50)
> > >     country = models.ForeignKey(Country)
> > >     def __unicode__(self):
> > >         return self.name
> > >     class Admin:
> > >         pass
>
> > > class Person(models.Model):
> > >     firstName = models.CharField(max_length=30)
> > >     lastName = models.CharField(max_length=30)
> > >     citiesLived = models.ManyToManyField(City, null=True, blank=True)
> > >     def __unicode__(self):
> > >         return self.firstName + " " + self.lastName
> > >     class Admin:
> > >         js = "http://mysite.com/custom_widget.js";
>
> > > custom_widget.html (template)
> > > ------------------------------------------
>
> > > var countries = {
> > >         {% regroup cities by country as grouped %}
> > >         {% for group in grouped %}
> > >                     "{{ group.grouper.name }}: [
> > >                     {% for city in group.list %}
> > >                         '{{city.name}}' ,
> > >                     {% endfor %}  ]
> > >         {% endfor %}
>
> > > }
>
> > > custom_widget.js
> > > ------------------------
>
> > > var countries = {'england': ['London','Manchester'], 'france':
> > > ['Paris'] }
>
> > > document.forms['your_form'].id_country.onchange = function()
> > > { listCities(this.value) };
>
> > > function listCities ( country ) {
> > >     document.forms['your_form'].id_city.options.length = 0
> > >     var l = countries[ country ].length;
> > >     for (var i = 0; l > i; i++) {
> > >         document.forms['your_form'].id_city.options[i] = new
> > > Option( countries[ country ][i], countries[ country ][i]);
> > >     }
>
> > > }
>
> > > On Nov 21, 11:06 am, kamil <[EMAIL PROTECTED]> wrote:
>
> > > > You would achieve it using "regroup" template tag
> > > > First get cities in your view
> > > > and create template with code:
>
> > > > var countries = {
> > > >         {% regroup cities by country as grouped %}
> > > >         {% for group in grouped %}
> > > >                     "{{ group.grouper.name }}: [
> > > >                     {% for city in group.list %}
> > > >                         '{{city.name}}' ,
> > > >                     {% endfor %}  ]
> > > >         {% endfor %}
>
> > > > }
>
> > > > (If it dosnt work straigt away just look at  "regroup" template tag in
> > > > the docs - idea is there)
>
> > > > add url of this template to js list in model admin code and thats
> > > > all :)
>
> > > > On Nov 20, 11:54 am,Julien<[EMAIL PROTECTED]> wrote:
>
> > > > > Hi,
>
> > > > > When I look at the html source code generated for the regular
> > > > > ManyToManyField's widget, it's an html form, not javascript...
> > > > > Are you sure there is no other way?
>
> > > > > Could you please give a full example?
>
> > > > > I've spent quite sometime practicing with the tutorials but I'm a bit
> > > > > lost on this one.
>
> > > > > Thanks a lot!
>
> > > > > On Nov 20, 9:04 pm, kamil <[EMAIL PROTECTED]> wrote:
>
> > > > > > I dont think there is another way that making your hands dirty with
> > > > > > javascript. ;)
> > > > > > If you want to be fancy you can even make it ajax - getting the 
> > > > > > cities
> > > > > > on demand
>
> > > > > > On Nov 20, 9:35 am, kamil <[EMAIL PROTECTED]> wrote:
>
> > > > > > > You can easily generate cities list to the javascript dynamically
> > > > > > > writing the template for separate js file.
>
> > > > > > > On Nov 20, 9:22 am,Julien<[EMAIL PROTECTED]> wrote:
>
> > > > > > > > Hi Kamil,
>
> > > > > > > > Thanks a lot for your suggestion!
>
> > > > > > > > I have a few remarks though. The list of cities is dynamic, in 
> > > > > > > > the
> > > > > > > > sense that you may add, remove cities from the database at any 
> > > > > > > > time
> > > > > > > > using the admin interface.
> > > > > > > > So how can we fetch dynamically the list of available cities to
> > > > > > > > display them in the select widget?
>
> > > > > > > > Also, isn't there a "recommended" way of doing? Hijacking with
> > > > > > > > javascript seems more like a trick. I may be wrong.
>
> > > > > > > > Thanks!  ;)
>
> > > > > > > > On Nov 20, 8:13 pm, kamil <[EMAIL PROTECTED]> wrote:
>
> > > > > > > > > HiJulien
>
> > > > > > > > > The simple way is hijack city select box with javascript.
> > > > > > > > > You add
> > > > > > > > > js=['http://herecomespathtoyourscript']
> > > > > > > > > to admin options in your model
> > > > > > > > > js can look like this:
>
> > > > > > > > > var countries = {'england': ['London','Manchester'], 'france':
> > > > > > > > > ['Paris'] }
>
> > > > > > > > > document.forms['your_form'].id_country.onchange = function()
> > > > > > > > > { listCities(this.value) };
>
> > > > > > > > > function listCities ( country ) {
> > > > > > > > >     document.forms['your_form'].id_city.options.length = 0
> > > > > > > > >     var l = countries[ country ].length;
> > > > > > > > >     for (var i = 0; l > i; i++) {
> > > > > > > > >         document.forms['your_form'].id_city.options[i] = new
> > > > > > > > > Option( countries[ country ][i], countries[ country ][i]);
> > > > > > > > >     }
>
> > > > > > > > > }
>
> > > > > > > > > I hope it helps :)
>
> > > > > > > > > On Nov 19, 11:54 pm,Julien<[EMAIL PROTECTED]> wrote:
>
> > > > > > > > > > Sorry, my "drawing" came out a bit funny in my previous 
> > > > > > > > > > post. Here's
> > > > > > > > > > what I'd like the custom widget to look like:
>
> > > > > > > > > > England
> > > > > > > > > >     [  ] London   [  ] Manchester
> > > > > > > > > > France
> > > > > > > > > >     [  ] Paris
> > > > > > > > > > Russia
> > > > > > > > > >     [  ] Moscow
> > > > > > > > > > USA
> > > > > > > > > >     [  ] Los Angeles   [  ] New York
>
> > > > > > > > > > Thanks for your help!
>
> > > > > > > > > > On Nov 20, 10:51 am,Julien<[EMAIL PROTECTED]> wrote:
>
> > > > > > > > > > > Hi all,
>
> > > > > > > > > > > I'm a Django newbie, and I've been struggling on this for 
> > > > > > > > > > > days. I've
> > > > > > > > > > > also found posts on this group about similar subjects but 
> > > > > > > > > > > none that
> > > > > > > > > > > could directly help me...
>
> > > > > > > > > > > Here are the models I've got:
>
> > > > > > > > > > > class Country(models.Model):
> > > > > > > > > > >     name = models.CharField(max_length=50)
> > > > > > > > > > >     def __unicode__(self):
> > > > > > > > > > >         return self.name
> > > > > > > > > > >     class Admin:
> > > > > > > > > > >         pass
>
> > > > > > > > > > > class City(models.Model):
> > > > > > > > > > >     name = models.CharField(max_length=50)
> > > > > > > > > > >     country = models.ForeignKey(Country)
> > > > > > > > > > >     def __unicode__(self):
> > > > > > > > > > >         return self.name
> > > > > > > > > > >     class Admin:
> > > > > > > > > > >         pass
>
> > > > > > > > > > > class Person(models.Model):
> > > > > > > > > > >     firstName = models.CharField(max_length=30)
> > > > > > > > > > >     lastName = models.CharField(max_length=30)
> > > > > > > > > > >     citiesLived = models.ManyToManyField(City, null=True, 
> > > > > > > > > > > blank=True)
> > > > > > > > > > >     def __unicode__(self):
> > > > > > > > > > >         return self.firstName + " " + self.lastName
> > > > > > > > > > >     class Admin:
> > > > > > > > > > >         pass
>
> > > > > > > > > > > In the admin interface, when adding cities to a person's 
> > > > > > > > > > > profile, you
> > > > > > > > > > > get the usual ManyToMany select box.
>
> > > > > > > > > > > So you may have:
> > > > > > > > > > > New York
> > > > > > > > > > > Manchester
> > > > > > > > > > > Paris
> > > > > > > > > > > London
> > > > > > > > > > > Moscow
> > > > > > > > > > > Los Angeles
>
> > > > > > > > > > > What I'd like to get instead is a custom widget, that is 
> > > > > > > > > > > a list of
> > > > > > > > > > > checkboxes sorted by country, and in Alphabetic order. 
> > > > > > > > > > > E.g.:
> > > > > > > > > > > ________________________________________
> > > > > > > > > > > |
> > > > > > > > > > > England
> > > > > > > > > > > |
> > > > > > > > > > > |    [  ] London   [  ]
> > > > > > > > > > > Manchester                                      |
> > > > > > > > > > > |
> > > > > > > > > > > France
> > > > > > > > > > > |
> > > > > > > > > > > |    [  ]
> > > > > > > > > > > Paris
> > > > > > > > > > > |
> > > > > > > > > > > |
> > > > > > > > > > > Russia
> > > > > > > > > > > |
> > > > > > > > > > > |    [  ]
> > > > > > > > > > > Moscow
> > > > > > > > > > > |
> > > > > > > > > > > |
> > > > > > > > > > > USA
> > > > > > > > > > > |
> > > > > > > > > > > |    [  ] Los Angeles   [  ] New York
> > > > > > > > > > > |
> > > > > > > > > > > |_______________________________________|
>
> > > > > > > > > > > Could you give me some advice on how to
>
> ...
>
> read more >>
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to