Mimsy Were the Borogoves

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

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 ->