Hi there,

I have been experiencing poor performance with django-admin when
list_editable includes ForeignKey fields. In particular, rendering the
changelist requires O(m*n) database queries where m is the number of
ForeignKey fields included in list_editable and n is the number of
rows in the changelist. I have searched extensively for possible
causes and, after finding nothing and receiving no response on either
django-users ('list_editable duplicate queries') or IRC, I am starting
to think that it is a legitimate bug.

The problem, as I understand it, stems from the fact that the choices
for the ForeignKey widgets are not cached. So, when every ForeignKey
widget on every row is rendered, it queries the database to retrieve
the list of possible values. The result in an unacceptible number of
queries, especially given the default changelist length of n=100.

For my own purposes, I have addressed this issue in three ways:

1. Adding a widget cache to django/forms/widgets.py:~431 -- A patch is
available upon request.

2. Manually rendering the data using a custom display function in the
admin model.

3. Overriding the ForeignKey widget with a custom CachedSelect widget.

The last fix has worked best for me because it does not require
hacking up django, but I think that this issue is worth addressing
properly in Django. So, does this warrant a ticket?

Thanks,

Chad





PS: For the sake of posterity, I have included the code for my
CachedSelect widget. Yes, I am aware that using regular expressions to
parse the key is a nasty hack. If you have any better ideas, please
let me know!

~~~widgets.py~~~

import re

from django.forms.widgets import Select

class CachedSelect(Select):

    cache = {}
    regex = re.compile('^form-(?P<id>[0-9]+)-(?P<model>.*)$')

    def render(self, name, value, attrs=None, choices=()):
        # If name does not match form-<num>-<model>, render widget
regularly
        match = self.regex.match(name)
        if not match:
            return super(CachedSelect, self).render(name, value,
attrs, choices)

        id = match.group('id')
        model = match.group('model')

        # Cache the data if necessary (first hit of changelist or not
cached)
        if id == '0' or not self.cache.has_key(model):
            self.cache[model] = [choice for choice in self.choices]

        self.choices = self.cache[model]
        return super(CachedSelect, self).render(name, value, attrs,
choices)


~~~admin.py~~~

from widgets import CachedSelect
...

class BlahAdmin(admin.ModelAdmin):
    formfield_overrides = {models.ForeignKey:{'widget':
CachedSelect()}}
    ...

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-develop...@googlegroups.com.
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to