I have a little problem with a dropdown field in django form. I want to add to this field placeholder (or not active first option as placeholder) and some other stuff as no label, class etc.
I wrote something like this, in my forms.py but now my form is broken - don't save values to database.
from .models import Scenario, ScenarioArea
from django import forms
class newScenario(forms.ModelForm):
scenarioArea=forms.ModelChoiceField(label="", queryset=ScenarioArea.objects.values_list("scenarioAreaName", flat=True).distinct(), empty_label=None)
lass Meta:
model = Scenario
fields = ['scenarioArea']
Every other field type is working, but not this dropdown...
Could you help me?
By default the widget used by ModelChoiceField will have an
empty choice at the top of the list. You can change the text of this
label (which is "---------" by default) with the empty_label
attribute, or you can disable the empty label entirely by setting
empty_label to None:
As you're setting empty_label to None, this option is not being shown.
Example
scenario_area = forms.ModelChoiceField(label="", queryset=ScenarioArea.objects.distinct('scenarioAreaName'), empty_label="Placeholder")
Related
I'm trying to replace the standard AdminSplitDateTime widget in my admin site for better functionality (basically I want to display only 'available' dates in my calender which I couldn't find how to do with the default picker).
I decided to use the bootstrap3_datetime widget.
After overriding my field to use the new widget, it doesn't seem to be transferred into the 'clean' method (isn't in self.cleaned_data) for validation.
models.py
publish_time = models.DateTimeField('Date to publish')
admin.py
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
bad_dates = []
#populating bad_dates with application logic
def clean(self):
# This will always return None when using the new widget.
# When working with the default widget, I have the correct value.
publish_time = self.cleaned_data.get('publish_time', None)
publish_time = forms.DateTimeField(widget=DateTimePicker(options=
{"format": "DD-MM-YYYY HH:mm",
"startDate": timezone.now().strftime('%Y-%m-%d'),
"disabledDates": bad_dates,
})
class MyModelAdmin(admin.ModelAdmin):
form = MyForm
admin.site.register(MyModel, MyModelAdmin)
HTML-wise, the widget works well and the text field is populated with the correct date (and with the 'bad_dates' disabled). The problem is that it seems it isn't saved on the form.
I also tried initializing the widget in the init method by doing:
self.fields['publish_time'].widget = DateTimePicker(options=...)
But the result was the same.
What am I missing here?
Is it even possible to modify widgets in the admin site?
Thanks!
Update:
I've analysed the POST request that is sent using each of the widgets.
In the default admin widget, I see that it generates two fields: "publish_time_0" (for date) and "publish_time_1" (for time).
In the bootstrap3 widget, only a single "publish_time" field is sent.
I'm assuming that the admin site understands that the field is a DateTimeField (from models), looks for id_0 and id_1 and that's why it fails.
Does that make sense? Is there anyway around it?
In my application I have a CreateView that must initialize some fields of the model with a default value, different from the default defined inside the model.
I do not want the user to edit the value, thus I put the field in the exclude list
class AForm(ModelForm):
class Meta:
model = AModel
exclude = ['a_field']
class AView(CreateView):
form_class = AForm
The question is: where do I set the value of a_field?
I tried to define clean methods inside AForm, like thus
class AForm(ModelForm):
[...]
def clean(self):
d = super(AForm, self).clean()
d['a_field'] = 10
return d
however a_field is set to whatever its default value defined in the model is, rather than 10. I also tried defining clean_a_field, but that is simply not executed.
If I remove a_field from the exclude list, then the clean and clean_a_field will work, but the form won't validate unless I render some <input name="a_field"> inside the template, which is not optimal.
I managed to solve the issue in a way that makes me satisfied, although I'm still not 100% happy with the code.
a_field is a required (by the model) field, thus it is necessary to render an <input name="a_field"> inside the template. The trick was to make a_field non-required:
class AForm(ModelForm):
a_field = Field(required=False,
widget=forms.HiddenInput)
class Meta:
model = AModel
def clean_a_field(self):
return 10
This way I can avoid rendering the field in my template, and the form will still validate. And even if the form is rendered with {{ form.as_p }}, widget=forms.HiddenInput saves my day.
Exclude the field from the form, then in the view you can set the value before you save the form:
form = AForm(request.POST)
if form.is_valid():
new_record = form.save(commit=False)
new_record.a_field = 10
new_record.save()
You also might want to avoid the exclude list and specify which fields you'd like to include with the fields attr of the form definition. See the first paragraph here in the docs.
You set a default value in the model. From the official document,
a_field = models.CharField(max_length=7, default=''), for example
I have a way to Face this situation. Follow the following process:
Remove 'a_field' from the excludes in AForm.
Do not expose 'a_field' in HTML template. i.e. Don't give the user option to change the value via Form in Template. This would ensure that normal user's wont modify the value.
To prevent completely, over-ride get_form_kwargs in the View.
This would provide or over-ride your desired value to 'a_field' and save that
e.g.
class AView(CreateView):
form_class = AForm
def get_form_kwargs(self):
kwargs = super(AView, self).get_form_kwargs()
if self.request.method in {'POST', 'PUT'}:
# Change post data to over-ride or provide value of 'a_field'
data = self.request.POST.copy()
data.update({
'a_field': 'value'
})
kwargs['data'] = data
return kwargs
I have a wtforms form
class MyForm(Form):
names = SelectField('name', choices=[])
The choices of names field is built dynamically and rendered in the template by an ajax call. When I submit the form, it raises an exception "not a valid choice". I don't want the form to validate the choices of names field for me. How can I disable the validation?
I did something like this to step around the SelectMultipleField validation in WTForms. It should work the same way with a plain SelectField
class NonValidatingSelectMultipleField(SelectMultipleField):
"""
Attempt to make an open ended select multiple field that can accept dynamic
choices added by the browser.
"""
def pre_validate(self, form):
pass
I simply override the built-in validation.
I was stuck with the same issue. The solution provided by Xealot is great. I found that there is an option to set validation to False using validate_choice=False. I have included an example of both the solutions below.
class NonValidatingSelectField(SelectField):
"""
Attempt to make an open ended select multiple field that can accept dynamic
choices added by the browser.
"""
def pre_validate(self, form):
pass
class MyForm(Form):
names = NonValidatingSelectField('name')
names2 = SelectField('name2', validate_choice=False)
By "I don't want the form to validate the choices", I assume you actually mean "I'm going to do it myself later and doubt the form's ability to do it correctly".
But you are in luck! You can subclass an existing form to add choices dynamically.
class MyForm(Form):
# other fields...
def some_handler(request):
name_choices = build_name_choices()
class RealForm(MyForm):
names = SelectField('name', choices=name_choices)
form = RealForm(request.GET)
form.validate()
This also saves you from the tedium of merging the form's validation and error messages with those you generate yourself later.
I am trying to load default values for a MultipleChoiceField in a Form overload.
So if I give the initial variable inside the MultipleChoiceField it works:
class UserPreferences(forms.Form):
my_form = forms.MultipleChoiceField(
widget = forms.CheckboxSelectMultiple(),
choices = MY_CHOICES,
initial = MY_INITIAL_DICT)
But if I try to get initial values from the database related to a user and update initial as follows, my_form initial variable doesn't change and stays empty:
class UserPreferences(forms.Form):
my_form = forms.MultipleChoiceField(
widget = forms.CheckboxSelectMultiple(),
choices = MY_CHOICES)
def __init__(self, user):
initial = make_my_dict_from( MyModel.objects.filter(user=user) )
super(UserPreferences, self).__init__(initial, empty_permitted)
If I do this with a simple ChoiceField it works, but not with MultipleChoiceField.
Thanks for any suggestions!
try this:
super(UserPreferences, self).__init__(initial=initial)
You need to use keyword arguments like this. Python does not look at your variable names, and uses the right slots.
Where does "empty_permitted" come from? Your example does use, but not define this variable.
Let's assume that I have following models:
class ScoutBook(models.Model):
troop = models.ForeignKey('Dictionary', limit_choices_to={'type' : 'Troop'}, related_name='+', blank=True, null=True)
class Dictionary(models.Model):
name = models.CharField(max_length=CHAR_FIELD_MAX_LEN, verbose_name="Nazwa")
active = models.BooleanField(verbose_name="Aktywny")
type = models.CharField(max_length=CHAR_FIELD_MAX_LEN, choices=DICTIONARY_CHOICES)
and I want to implement following logic:
when creating ScoutBook allow users to select only active troops, and when editing allow to select active troops or allow user to leave value unchanged (even if the troop is inactive). If I use limit_choices_to = {..., 'active' = True} troop that is inactive is absent from combo box in django admin.
So to be clear: let's assume that there are four troops in this system: Troop1, Troop2 and InactiveTroop, InactiveTroop2. On model creation I would like user to be able to choose Troop1 and Troop2. If model has troop field set to InactiveTroop2, I would like user to be able to choose between InactiveTroop2, Troop1 and Troop2.
I was looking at the django forms api and I didn't found obvious way do this. Moreover, in the application I'm developing there will be many such fields and many such models --- so solution must be pain free. I would rather not create new Form class for every model. I will be using mostly django admin to enable editing the database, and some read only views that will just list entries.
Ideally I would like to encapsulate this functionality in some custom field --- but fields have access to model instance on validate and save phase --- so I dont have access to it when I produce formfield.
This sounds like something you want to do in a form, not in the object itself. Create a ModelForm and override the ModelChoiceField like this:
from django import forms
class ScoutBookForm(forms.ModelForm):
troop = forms.ModelChoiceField(queryset=Troop.objects.filter(active=True))
class Meta:
model = ScoutBook
You can also override the clean method of ScoutBook to ensure it cannot ever be saved with an inactive Troop, though that may have some unintended consequences (e.g., you wouldn't be able to update a ScoutBook in the admin if the troop had gone inactive at some point in the past).
Well I had to hook into ModelForm creation. Attached Form inspects it's fields and if specific conditions are met it replaces model field queryset.
class DictionayModelForm(ModelForm):
def __init__(self, *largs, **kwargs):
super(DictionayModelForm, self).__init__(*largs, **kwargs)
if self.instance and self.instance.pk is not None:
for f in self.instance._meta.fields:
if isinstance(f, models.ForeignKey) and issubclass(f.rel.to, Dictionary):
model_field = self.fields[f.name]
value = getattr(self.instance, f.name, None)
if value and value not in model_field.choices:
model_field.queryset = Dictionary.objects.filter(Q(**f.rel.limit_choices_to) | Q(id = value.id))