Python/Django BooleanField model with RadioSelect form default to empty - django

I'm using a Django ModelForm where my model contains a BooleanField and the form widget associated with that BooleanField is a RadioSelect widget. I'd like the the RadioSelect widget that renders to have no options selected so the user has to explicitly make a choice, but the form validation to fail if they make no selection. Is there a way to do this?
models.py
myboolean = models.BooleanField(choices=YES_NO)
forms.py
class myModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(myModelForm, self).__init__(*args, **kwargs)
self.fields['myboolean'].widget = forms.RadioSelect(choices=YES_NO)

Your code actually does what you need. It renders the radio buttons with no options selected and generate the error message if nothing is selected.
A small note about your form code. I would do it like this:
class myModelForm(forms.ModelForm):
myboolean = forms.BooleanField(widget=forms.RadioSelect(choices=YES_NO))
class Meta:
model = MyModel

Unfortunately, this is less of a Django issue than an HTML question. The HTML specification (RFC1866) says:
At all times, exactly one of the radio buttons in a set is checked. If none of the <INPUT> elements of a set of radio buttons specifies `CHECKED', then the user agent must check the first radio button of the set initially.
However, browsers have historically ignored this and implemented radio buttons in different ways.
HTML also makes this difficult because the "checked" attribute of the <INPUT> tag doesn't take a parameter, so you can't use a customized Django widget that sets this attribute to False or No.
A possible workaround is to put in a little Javascript that runs as part of the document's onLoad event that finds all the radio buttons on the page and sets the 'checked' attribute to false (using JQuery, for example).

see this:
http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#a-full-example

I creates custom field with default widget.
Cut of my models.py:
class Order(models.Model):
...
# transport = models.CharField(choices=transport.choices,max_length=25,null=True)
transport = SelectField(choices=transport.choices,max_length=25,null=True)
...
Field definition:
from django.db import models
from django.forms import widgets
class SelectField(models.CharField):
def formfield(self, **kwargs):
if self._choices:
defaults = {'widget': widgets.RadioSelect}
defaults.update(kwargs)
return super(SelectField, self).formfield(**defaults)

Related

Save Django ModelForm with CheckboxSelectMultiple with no boxes checked

A Django Form using a CheckboxSelectMultiple widget posts back nothing for the field using the CheckboxSelectMultiple widget if no boxes are checked.
class AlarmForm(forms.ModelForm):
class Meta:
model = models.Alarm
fields = ['phone_contacts', 'disabled']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
user = self.instance.user
self.fields["phone_contacts"].widget = forms.widgets.CheckboxSelectMultiple()
self.fields['phone_contacts'].queryset = user.phonecontact_set
If the user viewing the form checks no checkboxes, the idea is that the model's phonecontact_set field ( a ManyToManyField) should be cleared. Instead, if no boxes are check, that field isn't saved at all. This is apparently because unchecked check boxes include no values in the form POST in the web browser.
Still, it seems there must be a way to make Django do this without completely re-implementing the save functionality, but I can't seem to find the Django way to do this..

Overriding ProfileForm fields in Mezzanine

I have a User Profile ManyToManyField that by default, renders as a multi-select. On the user's profile page, I would like to override this with a TextInput field that I can add auto-complete jquery to, but I'm having a hard time seeing where to do this in Mezzanine.
TIA,
Joel
This is a Django question more than a Mezzanine question:
Widgets are what you need to set.
You're going to need a form definition, which you didn't mention. In the form definition override the default widget section as shown below. You can add class defs and more here too.
field_name = forms.CharField(widget=forms.TextInput(attrs={'style': 'display:block;','class': 'form-control'}),required=True)
It can also be done after the declaration:
field_name.widget = forms.TextInput()
It can also be done in the init within the form class:
def __init__(self, *args, **kwargs):
self.fields['field_name'].widget = forms.TextInput()
more here:
https://docs.djangoproject.com/en/1.7/ref/forms/widgets/

Override ChoiceField widget in Django ModelForm without re-specifying choices

I am trying to override the default widget for a Django ChoiceField while preserving the choices generated in the model. Here is the relevant part of my model:
class UserGroup(models.Model):
icon = models.CharField(max_length=100, choices=<function call>)
And of my form:
class UserGroupForm(forms.ModelForm):
icon = models.ChoiceField(widget=IconPicker)
class Meta:
model = UserGroup
fields = [ 'icon', ]
Overriding the ChoiceField's widget like this clobbers the form.fields['icon'].choices attribute normally inherited from the model and sets it to [] because Django. If I remove the icon field definition from the form, the choices are preserved - but of course the widget defaults to a Select.
(The function which generates the choices for the model field is not accessible from the form code unfortunately.)
The best I have come up with so far for is to change the icon form field definition to
icon = ChoiceField(choices=UserGroup._meta.get_field_by_name('icon')[0].choices,
widget=IconPicker)
but this is clunky and I would rather have the choices automatically passed on as in the introspected ChoiceField behavior. (I tried subclassing ChoiceField to IconChoiceField which was identical but for a default widget of IconPicker but Django converts it back to a TypedChoiceField with the default Select widget because of this issue.)
Is there a way to override the ChoiceField's widget attribute while preserving the behavior of inheriting choices from the model?
I think the reason you are losing the choices you specified in your model i.e. "sets it to []" is not "because Django", but because you override the icon field with the line icon = models.ChoiceField(widget=IconPicker)
Please note that if you are using a modelform then it is not necessary to override the widget at init, instead the widget should be specified in the Meta class in the widgets dictionary.
class UserGroupForm(forms.ModelForm):
class Meta:
model = UserGroup
fields = [ 'icon', ]
widgets = {
'icon': IconPicker
}
As for overriding the choices you can simply do self.fields['icon'].choices = UserGroupForm.ICON_CHOICES, but I don't think you need to override choices in this instance.
Figured it out. Just needed a bit of self-reference in UserGroupForm's __init__:
def __init__(self, *args, **kwargs):
super(UserGroupForm, self).__init__(*args, **kwargs)
self.fields['icon'].widget = IconPicker(choices=self.fields['icon'].choices)

Django Admin: Add text at runtime next to a field

I want to add a text next to a field of the django admin interface.
The warning needs to created at runtime inside a python method. I know python and the django ORM well, but I don't know how to get the text next the field.
The text should be a warning. Raising ValidationError in clean() is not a solution, since
the user can't edit the page any more. It should be just a warning message.
You can use custom ModelForm subclass for the admin, adding help_text attribute for the field in question at its initialization, and style it appropriately.
# forms.py
class YourModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(YourModelForm, self).__init__(*args, **kwargs)
self.fields['field_in_question'].help_text = generate_warning()
# admin.py
class YourModelAdmin(admin.ModelAdmin):
form = forms.YourModelForm
# And here you can specify custom CSS / JS which would make
# `help_text` for that particular field look like a warning.
# Or you can make it generic--say, style (and maybe reposition w/js) all tags
# like <span class="warning"> that occur within the help text of any field.
class Media:
css = {"all": ("admin_warning.css", )}
js = ("admin_warning.js", )
If you want to do it in changelist view, you can write in model method, which returns string in format you want, and put name of that method in list_display in admin.
class MyModel(models.Model):
myfield = models.CharField(max_length=100)
def myfield_with_warning(self):
return '%s - %s' % (self.myfield, '<span class="warn">My warning message</p>'
myfield_with_warning.short_description = 'My field verbose name'
myfield_with_warning.allow_tags = True
class MyModelAdmin(models.ModelAdmin):
list_display = ('myfield_with_warning',)
If it's not what you need, write more precisely, where do you want to display warning message.
I think the simplest way would be to override the specific admin page for that model. This is described here in the Django documentation. The template you need to override is probably change_form.html. Within these template displayed object is available in the template variable original.
I would add a method or property to you model, that generates and returns the error message and call this method from the template.
Edit: Have a look at contrib/admin/templates/admin/change_form.html there is a include for includes/fieldset.html that displays the the fields of the admin site. You could put some code there that chckes if the model has some special named attribute and if so it is displayed. You could them simply override that change_form.html for all models with your custom one.

Adding an attribute to the <input> tag for a django ModelForm field

I have a django model that I'm displaying as a form using a ModelForm. The defaults work very well for me for the most part.
However, I would like my html <input ...> tags to have one additional attribute, namely I would like to include a placeholder attribute, like the following:
<input placeholder="{{field.label}}" ... />
What is the easiest way to add this attribute to my html? As far as I can tell it appears I need to implement my own filter to output the field, but this seems like overkill when all i want to do is leave everything alone but add one additional attribute.
See the documentation
class AuthorForm(ModelForm):
class Meta:
model = Author
widgets = {
'name': TextInput(attrs={'placeholder': 'name'}),
}
You could always create your own widget that derives from TextInput and includes the placeholder attribute, and use the widgets dictionary to simply map fields to your new widget without specifying the placeholder attribute for every field.
Personally I prefer to use this method:
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['email'].widget.attrs['placeholder'] = self.fields['email'].label or 'email#address.nl'
It required more code if you don't have __init__ yet, but you don't need to specify the widget.