Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, Swift, BASIC, and whatever else I happen to feel like hacking at.

42 Astoundingly Useful Scripts and Automations for the Macintosh

Work faster and more reliably. Add actions to the services menu and the menu bar, create drag-and-drop apps to make your Macintosh play music, roll dice, and talk. Create ASCII art from photos. There’s a script for that in 42 Astounding Scripts for the Macintosh.

Custom ModelForms with django-ajax-selects

Jerry Stratton, February 1, 2013

I have a Model for URLs that contains:

[toggle code]

  • class Destination(models.Model):
    • path = models.CharField(max_length=140)
    • host = models.ForeignKey(Host, default=1)
    • query = models.CharField(max_length=240, blank=True)
    • secure = models.BooleanField(default=False)
    • class Meta:
      • ordering = ['host__name', 'path']
      • unique_together = (('path', 'host', 'secure', 'query'),)
    • def save(self, *args, **kwargs):
      • if self.path.startswith('/'):
        • self.path = self.path.lstrip('/')
      • return super(Destination, self).save(*args, **kwargs)

The problem is when the stripping results in an existing URL. Model saves, whether in models.Model or admin.ModelAdmin, happen after validation, so instead of a nice “this URL already exists” I get a server error. Django thinks this isn’t a duplicate because it already passed its validation, and then I went ahead and changed it.

This is easily fixed in the Admin model, by using a custom ModelForm:

[toggle code]

  • class DestinationForm(forms.ModelForm):
    • class Meta:
      • model = Destination
    • def clean_path(self):
      • path = self.cleaned_data['path']
      • if path.startswith('/'):
        • path = path.lstrip('/')
      • return path
  • class DestinationAdmin(admin.ModelAdmin):
    • form = DestinationForm

Now, adding a new Destination results in the correct “Destination with this Path, Host, Secure and Query already exists.”

But it still fails in the “+add” pop-up form. I’m using django-ajax-selects, and the error is in the views.py for django-ajax-selects:

[toggle code]

  • Traceback (most recent call last):
  • File "/Library/Python/2.7/site-packages/django/core/handlers/base.py", line 111, in get_response
    • response = callback(request, *callback_args, **callback_kwargs)
  • File "/Library/Python/2.7/site-packages/django_ajax_selects-1.2.5-py2.7.egg/ajax_select/views.py", line 65, in add_popup
    • if 'opener.dismissAddAnotherPopup' in response.content:
  • File "/Library/Python/2.7/site-packages/django/template/response.py", line 123, in _get_content
    • raise ContentNotRenderedError('The response content must be '
  • ContentNotRenderedError: The response content must be rendered before it can be accessed.

A search on the ContentNotRenderedError shows a similar error on StackOverflow, with a solution that appears to work.

In add_popup in /Library/Python/2.7/site-packages/django_ajax_selects-1.2.5-py2.7.egg/ajax_select/views.py (on Red Hat Linux, ajax_select is in /usr/lib/python2.6/site-packages/django_ajax_selects-1.2.5-py2.6.egg) change it to be:

[toggle code]

  • def add_popup(request,app_label,model):
    • response = admin.add_view(request,request.path)
    • if request.method == 'POST':
      • if (hasattr(response,'is_rendered') and response.is_rendered or not hasattr(response,'is_rendered')) and response.content:
        • if 'opener.dismissAddAnotherPopup' in response.content:
          • return HttpResponse( response.content.replace('dismissAddAnotherPopup','didAddPopup' ) )
    • return response

I wouldn’t be surprised if there’s a better check to be made than response.is_rendered; probably something like admin.form.is_valid(), but that requires the ModelForm as a parameter, and I can’t see where to get it.

The developers are aware of the issue and this particular solution.

  1. <- XDomainRequest onload
  2. AppleScript shell ->