hey Lachlan, So this had driven me a bit crazy some years ago ..
On Wed, May 28, 2014 at 8:42 AM, Lachlan Musicman <data...@gmail.com> wrote: > Hola, > > I am trying to get two different "extras" to work. > > I have a form with an inline_formset, which I would like to be able to > add multiple formsets of on the fly. > > Each of those formsets has another FK to a model with a very large > dataset, so I would also like to implement some sort of > autocomplete/ajax/select2/typeahead solution to prevent an unusably > large select > I had two additional extras :): i> The FK could also be an m2m that should be handled by select2. ii> There should also be an Add + button next to the select2 to add a new instance, which opens up a pop-up to add a new item, like it does in the Django admin. And then I needed this on a whole bunch of models / forms, so I needed it abstracted / generalized in some way. Unfortunately, the solution I came up with involves some ugly hacks, so I'm slightly embarassed by it, haha - its also for a much older version of Django, etc. (1.3, iirc). However, I still think this is a really kick-ass and useful thing to be able to do simply - I'd be happy to try and catch up with you off-list (which we've been meaning to do anyways, ha!) and see if we can have some fun rolling a little django app for this or so. > Here is my basic set up. > > models.py > ---------------- > class PartNumber(models.Model): > name = models.CharField("Description", max_length=100) > supplier_part_number = models.CharField(max_length=30, > unique=True, blank=True, null=True) > > class PurchaseOrder(models.Model): > po_number = models.CharField('PO number', max_length=10, unique=True) > ordered_date = models.DateField(default=today) > > class PurchaseOrderPart(models.Model): > part_number = models.ForeignKey(PartNumber, related_name='purchases') > po_number = models.ForeignKey(PurchaseOrder, related_name='partslist') > delivery_date = models.DateField(null=True, blank=True) > qty_ordered = models.IntegerField('Quantity > ordered',validators=[MinValueValidator(1)]) > cost = models.DecimalField('Unit Cost', > max_digits=10,decimal_places=2,blank=True,null=True) > > > forms.py > ------------- > > class PurchaseOrderPartForm(forms.ModelForm): > class Meta: > fields = ('part_numbers', 'delivery_date', 'qty_ordered', 'cost') > model = PurchaseOrderPart > widgets={ > 'part_numbers': forms.Select(attrs={'class':'form-control'}), > 'delivery_date': CalendarWidget(attrs={'class':'input-append > form-control'}), > 'qty_ordered': forms.NumberInput(attrs={'class':'form-control'}), > 'cost': forms.NumberInput(attrs={'class':'form-control'}), > } > > POPartFormset = inlineformset_factory(PurchaseOrder, > PurchaseOrderPart, form=PurchaseOrderPartForm, extra=1, > can_delete=True) > > > I have successfully implemented jquery.formset.js ( > https://github.com/elo80ka/django-dynamic-formset ) to create "add" > and "remove" buttons - this allows for a variable number of > PurchaseOrderParts on a PurchaseOrder. > > > When I then implemented django-select2 I was mostly successful, except > for one problem - the way that django-select2 implements field is by > adding some js on render. > > Since the jquery.formset.js was creating forms on the fly, any > formsets added via it's add button did not have the required > django-select2 container, and hence had no widget at all. > > jquery.formset.js has an "added" function that allows for something > like this, but then I would need to glue it all together and it > doesn't seem clean enough. > So, I used a similar approach - jquery.formset.js and select2. What I did was I created a little jquery plugin / wrapper around select2 to be able to instantiate it with a simple call to like $('#select_field_id_foo').mySelect2();. I then also created a custom widget for the autocompletes, which would ensure their custom options were in the html to be read by the plugin. So, this is the custom widget class I made: http://code.camputer.org/itf/annotate/head%3A/itf/app/forms.py#L42 This is its template: http://code.camputer.org/itf/annotate/head%3A/itf/templates/formwidgets/select2.html Then I can simply instantiate the widget in my form definition like so: group = forms.ModelChoiceField(TheatreGroup.objects.all(), widget=AutocompleteAddWidget(model_class=TheatreGroup)) See for eg: http://code.camputer.org/itf/annotate/head%3A/itf/itfprofiles/forms.py#L129 So far so good. Where the ugly hackery lies is the javascript - as you mention, to work around the problem of instantiating when adding a new formset and other problems when using the Django Admin JS for the pop-up forms, I landed up hacking in the jquery.formset.js code itself, which I'm not proud of. I think this can probably be accomplished cleaner using the 'added' callback or so, as you mention (and make your single point of entry / instantiation a custom jquery plugin / wrapper around select2). So anyways, first, this is the custom jquery plugin / wrapper I made for select2: http://code.camputer.org/itf/annotate/head%3A/itf/static/js/itfSelect2.js That reads some params output in the HTML for the select, and instantiates select2 appropriately. Was a bit of a yak-shave to get the initial value to display correctly, etc. and this was a while ago, so I don't remember details of why some of those things are that way, but I do know this code still works, and as I said, I'd be excited to collaborate with you on releasing something 'clean' for this use-case. So, anyways, to complete the story .. this is the ugliness where I modified the jquery.formset.js code, which is obviously a bit nasty to be doing :/ - http://code.camputer.org/itf/annotate/head%3A/itf/static/js/jquery.formset.js#L90 There's additional JS bits and ugly hacks to get the "add" button working, but I'll save you the gory details since you don't seem to need that :-) Additionally, this is the view I came up with to handle outputting the autocomplete data on the back-end - it accepts a ContentType id for the model it needs to autocomplete for, and returns JSON for the model appropriately. It depends on some methods being defined on the model: http://code.camputer.org/itf/annotate/head%3A/itf/app/views.py#L19 So, this was a while ago when I honestly had little idea about the internals of django forms, etc. so its probably not the best way, though I did manage to get everything to work the way I wanted in the end. As I said, I'd love to collaborate with you to wrap something neatly for this use-case - please feel free to hit me up on chat or anything, and hopefully we can post back on the list when we have something working - unless, of course, there is already a straightforward way to do this :) > I had previously looked at typeahead, django-ajax-selects and > django-autocomplete-light but struggled to implement them. I will > likely go back to try again. > Nooooooooooo hahaha - I personally like select2, its nice -- it does have some problems on mobile / touch interfaces which I'm not sure are resolved, so if you need that, it maybe worth looking at other options. I've had to use typeahead on another project, and have been using django-ajax-selects in the admin for small things, but overall I really like the customizability of select2 - it really feels like the cleanest of them and I've really enjoyed using it. Of course, this is also based on my impressions from at least a year ago, so things may have changed. > Surely I'm not the first person that's had this need - can anyone > offer tips on a good or better solution? > Haha, I thought I was some sort of crazy person for needing this a few years ago - glad someone else has bumped into it - I think a straightforward clean solution to be able to do this would be really, really nice to have - it does seem like such an obvious thing one would want. I haven't revisited this in some time, but I do remember being completely obsessed with this for a month or so a few years ago. Would love to go back to it and use some of the experience gained then to make something actually semi-elegant now - do let me know. Talk soon! -Sanjay > cheers > L. > > > > -- > The idea is that a beautiful image is frameable. Everything you need > to see is there: It’s everything you want, and it’s very pleasing > because there’s no extra information that you don’t get to see. > Everything’s in a nice package for you. But sublime art is > unframeable: It’s an image or idea that implies that there’s a bigger > image or idea that you can’t see: You’re only getting to look at a > fraction of it, and in that way it’s both beautiful and scary, because > it’s reminding you that there’s more that you don’t have access to. > It’s now sort of left the piece itself and it’s become your own > invention, so it’s personal as well as being scary as well as being > beautiful, which is what I really like about art like that. > ----------------------------------------------------------------------------------------------------------- p.s. I really like the quotes you attach, thanks, hah :-) -- You received this message because you are subscribed to the Google Groups "Django users" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscr...@googlegroups.com. To post to this group, send email to django-users@googlegroups.com. Visit this group at http://groups.google.com/group/django-users. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAG3W7ZGeKQwBfPzySwz5D1%3DNd-m%3D91%3DzPNOsmHpcxqpaquY%3D3w%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.