I'm building a csv import form for my Django application and want to display the to-be-imported rows in a ModelFormSet
for validational purposes.
Therefore I added a view to the relevant ModelAdmin
which reads the lines from the csv and prints a ModelFormSet(queryset=an_empty_queryset, initial={data_from_the_csv})
.
The problem is that the model references three other models via ForeignKey
fields and for each field in each form in the formset a database query is issued in order to populate the ModelChoiceField
's options.
Why doesn't Django cache the form (as it is used several times) or is there already a way to accomplish this I don't know about yet?
Django formsets just delegate all the details of form creation to the form objects themselves, and the individual form instances aren't aware of the others, so it isn't unexpected that each will have to query for its own choices.
Caching could also have unintended side effects--for example, the form's __init__
function could be dependent on the initial
data it received, making the cached form
object incorrect.
The best way to reduce the number of queries would be to retrieve the choice querysets once and then pass them to your form classes in their constructor. This will require defining a custom ModelForm
and a custom ModelFormSet
.
Your form will need a constructor that accepts the choices directly:
from django.forms.models import ModelForm
class MyForm(ModelForm):
def __init__(self, my_field_choices=None, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['my_field'].choices = my_field_choices
And your formset will need to override a method to run the querysets and pass them into the forms as they're constructed:
from django.forms.models import BaseModelFormSet
class MyFormSet(BaseModelFormSet):
def _construct_forms(self):
# instantiate all the forms and put them in self.forms
self.forms = []
# Define each of your choices querysets
my_field_choices = Model.object.filter(...)
#Add your querysets to a dict to pass to the form
form_defaults = {'my_field_choices': my_field_choices, }
for i in xrange(min(self.total_form_count(), self.absolute_max)):
self.forms.append(self._construct_form(i, **form_defaults))
(see the Django source to look into how this would work)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With