When creating a flatpage, I want the user to select a template from a predefined list. In order to keep the Flatpage
model untouched, I prefer ChoiceField
over ModelChoiceField
(the latter provides the PK of the template, but I need the name for the template_name field):
class NewFlatpageForm(FlatpageForm):
template_name = forms.ChoiceField(choices = [])
def __init__(self, *args, **kwargs):
self.base_fields['template_name'].choices = ProjectTemplate.objects.values_list('path', 'name')
super(NewFlatpageForm, self).__init__(*args, **kwargs)
I override __init__
or Django populates choices at server start and does not update the list then.
I don't have any admin experience, but I did similar things using the fields
attribute when not using admin. However in this case, I got an exception telling fields
is not an attribute of the form. __dict__
showed me there's a base_fields
attribute and using it works. So, why use base_fields here, and why is fields
not present and finally am I doing something hacky?
fields
doesn't exist until after you've called super
. So just swap the order of the lines, so that super
comes first.
A lesson from my own experience: modifying basefields means that your modifications stick around "forever" (until python exits). In your case, that's probably not a problem, as you are always using the same field name, and you are replacing its values with the assignment from ProjectTemplate...
In my case, I wanted completely different fields based on parameters in the constructor. Since my field names were usually different, each time I instantiated a form, I added new fields but didn't eliminate the ones from the last time.
By calling super early (as indicated here) and then making my dynamic changes to self.fields instead of self.basefields, I was able to eliminate the problem of an ever growing list of fields. It makes perfect sense now, but I wasn't familiar with all of the syntax details and was hacking through instead of trying to understand it first.
In addition to Joe Germuska. If you truly need to change the form based on the request, you can use a deepcopy to make sure nothing is changed by reference:
def get_form(self, request, obj=None, **kwargs):
form = super(ResourceAdmin, self).get_form(request, obj, **kwargs)
form = copy.deepcopy(form)
if obj:
form.base_fields['email'] = EmailField(initial=obj.user.email)
if not request.user.is_superuser:
form.base_fields['user'].widget = HiddenInput(attrs={'class': 'hide_form_row'})
return form