Django model form how to output select Yes/No from Booleanfield - django

I'm trying to have a yes/no selection on a booleanfield. The default widget is checkboxinput. However if I override the default widget with Select I get a:
NameError: Select is not defined
I think this may be because I need to setup Yes/No to correlate to the boolean values in the booleanfield, but not sure how this should be done?
Model:
class User(models.Model):
online_account = models.BooleanField()
Form:
class AccountForm(forms.ModelForm):
class Meta:
model = User
fields = ('online_account')
labels = {
'online_account': 'Do you have an online account',
}
widgets = {'online_account': Select()}

I found (and tested with Django 1.9.6) this gist. It should do the trick:
from django import forms
class Form(forms.Form):
field = forms.TypedChoiceField(coerce=lambda x: x =='True',
choices=((False, 'No'), (True, 'Yes')))

Just set the choices in the template's boolean field
from django.utils.translation import gettext_lazy as _
CHOICES_BOOLEANO_SIM_NAO = (
(True, _('Sim')),
(False, _('Não'))
)
class modelo(models.Model):
"""Model definition for LoteModalidadeEvento."""
# TODO: Define fields here
e_bool_field= models.BooleanField(verbose_name=_('Este é um campo booleano'), **choices**=CHOICES_BOOLEANO_SIM_NAO)

Related

Which library is used for importing RadioInput (widgets) in forms.py in Django?

I am using radio buttons for user input in forms.py and want to save the rated value in django database.I have the following fields:
from product.models import Rating
from django.forms import forms
from django.forms.fields import ChoiceField
from django.forms import ModelForm
from django import forms
class RatingForm(forms.ModelForm):
class Meta:
model = Rating
fields = ('product', 'user', 'rating')
widgets = forms.ChoiceField(widget=forms.RadioInput(),
required=True)
Model.py
class Rating(models.Model):
CHOICES = (
('5-stars', '5-stars'),
('4-stars', '4-stars'),
('3-stars', '3-stars'),
('2-stars', '2-stars'),
('1-stars', '1-stars'),
)
product=models.ForeignKey(Product,null=True,blank=True, on_delete=models.PROTECT)
user=models.ForeignKey(User,null=True,blank=True, on_delete=models.PROTECT)
rating=models.ChoiceField(choices=CHOICES, max_length=128)
I didn't find any library for importing this widget. Below is the error i am facing:
AttributeError: module 'django.forms' has no attribute 'RadioInput'?
Please if any one can help? Or suggest any other way to do this?
The widget is called RadioSelect, not RadioWidget. See the documentation.
Note however, you must use the widget directly in the widgets attribute, not as part of a field; and widgets is a dictionary of field names to widgets:
widgets = {'rating': forms.RadioSelect}

Better ArrayField admin widget?

Is there any way to make ArrayField's admin widget allow adding and deleting objects? It seems that by default, it is instead displayed just a text field, and uses comma separation for its values.
Besides being inconvenient, AFAICT in the case the base field of the array is a Char/TextField, this doesn't allow any way of including commas in any of the texts in the array.
I take no credit for this (original source), but if you are using PostgreSQL as the database and are happy to use the Postgres-specific ArrayField implementation there is an even easier option: subclass ArrayField on the model and override the default admin widget. A basic implementation follows (tested in Django 1.9, 1.10, 1.11, 2.0, 2.1 & 2.2):
models.py
from django import forms
from django.db import models
from django.contrib.postgres.fields import ArrayField
class ChoiceArrayField(ArrayField):
"""
A field that allows us to store an array of choices.
Uses Django's Postgres ArrayField
and a MultipleChoiceField for its formfield.
"""
def formfield(self, **kwargs):
defaults = {
'form_class': forms.MultipleChoiceField,
'choices': self.base_field.choices,
}
defaults.update(kwargs)
# Skip our parent's formfield implementation completely as we don't
# care for it.
# pylint:disable=bad-super-call
return super(ArrayField, self).formfield(**defaults)
FUNCTION_CHOICES = (
('0', 'Planning'),
('1', 'Operation'),
('2', 'Reporting'),
)
class FunctionModel(models.Model):
name = models.CharField(max_length=128, unique=True)
function = ChoiceArrayField(
base_field=models.CharField(max_length=256, choices=FUNCTION_CHOICES),
default=list)
For OP, or anyone out there looking, between these helpful bits you should be good to go:
1. Extending SelectMultiple or CheckboxSelectMultiple widget to parse arrayfield and
2. Creating or extending admin form to display the arrayfield using the widget above
This is a better version of an already accepted solution. Using "CheckboxSelectMultiple" makes it more usable in the admin page.
class ChoiceArrayField(ArrayField):
def formfield(self, **kwargs):
defaults = {
'form_class': forms.TypedMultipleChoiceField,
'choices': self.base_field.choices,
'coerce': self.base_field.to_python,
'widget': forms.CheckboxSelectMultiple,
}
defaults.update(kwargs)
return super(ArrayField, self).formfield(**defaults)
The Django better admin ArrayField package provides exactly this functionality. The advantage over the solutions above is that it allows you to add new entries dynamically instead of relying on pre-defined choices.
See the documentation here: django-better-admin-arrayfield
It has a drop-in replacement for the ArrayField and a simple mixin to add to the admin model.
# models.py
from django_better_admin_arrayfield.models.fields import ArrayField
class MyModel(models.Model):
my_array_field = ArrayField(models.IntegerField(), null=True, blank=True)
# admin.py
from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin, DynamicArrayMixin):
...
This would show something like:
This is another version using the Django Admin M2M filter_horizontal widget, instead of the standard HTML select multiple.
We use Django forms only in the Admin site, and this works for us, but the admin widget FilteredSelectMultiple probably will break if used outside the Admin. An alternative would be overriding the ModelAdmin.get_form to instantiate the proper form class and widget for the array field. The ModelAdmin.formfields_overrides is not enough because you need to instantiate the widget setting the positional arguments as shown in the code snippet.
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.postgres.fields import ArrayField
from django.forms import MultipleChoiceField
class ChoiceArrayField(ArrayField):
"""
A choices ArrayField that uses the `horizontal_filter` style of an M2M in the Admin
Usage::
class MyModel(models.Model):
tags = ChoiceArrayField(
models.TextField(choices=TAG_CHOICES),
verbose_name="Tags",
help_text="Some tags help",
blank=True,
default=list,
)
"""
def formfield(self, **kwargs):
widget = FilteredSelectMultiple(self.verbose_name, False)
defaults = {
"form_class": MultipleChoiceField,
"widget": widget,
"choices": self.base_field.choices,
}
defaults.update(kwargs)
# Skip our parent's formfield implementation completely as we don't
# care for it.
return super(ArrayField, self).formfield(**defaults)
django-select2 offers a way to render the ArrayField using Select2. In their documentation, the example is for ArrayField:
http://django-select2.readthedocs.io/en/latest/django_select2.html#django_select2.forms.Select2TagWidget
To render the already selected values:
class ArrayFieldWidget(Select2TagWidget):
def render_options(self, *args, **kwargs):
try:
selected_choices, = args
except ValueError: # Signature contained `choices` prior to Django 1.10
choices, selected_choices = args
output = ['<option></option>' if not self.is_required and not self.allow_multiple_selected else '']
selected_choices = {force_text(v) for v in selected_choices.split(',')}
choices = {(v, v) for v in selected_choices}
for option_value, option_label in choices:
output.append(self.render_option(selected_choices, option_value, option_label))
return '\n'.join(output)
def value_from_datadict(self, data, files, name):
values = super().value_from_datadict(data, files, name)
return ",".join(values)
To add the widget to your form:
class MyForm(ModelForm):
class Meta:
fields = ['my_array_field']
widgets = {
'my_array_field': ArrayFieldWidget
}
write a form class for your model and use forms.MultipleChoiceField for ArrayField:
class ModelForm(forms.ModelForm):
my_array_field = forms.MultipleChoiceField(
choices=[1, 2, 3]
)
class Meta:
exclude = ()
model = Model
use ModelForm in your admin class:
class ModelAdmin(admin.ModelAdmin):
form = ModelForm
exclude = ()
fields = (
'my_array_field',
)

Django - CheckboxSelectMultiple without "------" choice

How can I remove "------" from rendered choices?
I use in my model form:
widgets = {
'event_form': forms.CheckboxSelectMultiple(),
}
In model I have IntegerField with choices:
EVENT_FORM_CHOICES = (
(1, _(u'aaaa')),
(2, _(u'bbbb')),
(3, _(cccc')),
(4, _(u'dddd')),
(5, _(eeee'))
)
rendered choices contain --------- as first possible choice. How I can get rid of it?
EDIT:
The only working way i figured out is (in init method):
tmp_choices = self.fields['event_form'].choices
del tmp_choices[0]
self.fields['event_form'].choices = tmp_choices
but it's not very elegant way :)
Update
a similar example maybe useful:
country = ModelChoiceField(reference_class = Country, choices= country_choices,
required=True, empty_label=None, widget=forms.Select)
If you want a solution client side instead:
<script>
$("#selectBox option[value='-----']").remove();
</script>
Django is including the blank choice because the field doesn't have a default value.
If you set a default value in your model, then Django will not include the blank choice.
class MyModel(models.Model):
event_form = models.PositiveSmallIntegerField(choices=EVENT_FORM_CHOICES, default=1)
If you don't want to set a default value in your model, then you can explicitly declare the field and choices in the model form, or change the choices in the model form's __init__ method.
I ran into a similar problem but fixed it this way. First, download and install https://pypi.python.org/pypi/django-multiselectfield. If you don't know how to install, look here: django-multiselectfield can't install. Then, in models.py:
from multiselectfield import MultiSelectField
CHOICES_FOR_ITEM_WITH_CHOICES = (
("choice 1", "choice 1"),
("choice 2", "choice 2"),
("choice 3", "choice 3"),
)
class MyModel(models.Model):
item_with_choices = MultiSelectField(max_length=MAX_LENGTH, null=True, blank=True)
In admin.py:
from .forms import MyModelForm
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
list_display = ('item_with_choices',)
list_filter = ('item_with_choices',)
search_fields = ('item_with_choices',)
admin.site.register(MyModel, MyModelAdmin)
In forms.py (you can name this whatever you like):
from .models import MyModel
class MyModelForm(ModelForm):
class Meta:
model = MyModel
fields = (
'item_with_choices',
)
def clean(self):
# do something that validates your data
return self.cleaned_data
This builds off the answer here: Django Model MultipleChoice

Django - pre-selected radio button from BooleanField

I have been able to get the BooleanField from my main Feature class to be rendered as a radiobutton for its form, but when viewed, the form doesn't have the corresponding value preselected. How do I get it to select the appropriate one, given a boolean value? Thanks
models.py:
class Feature(models.Model):
for_biz = models.BooleanField()
class FeatureForm(ModelForm):
choices = ( (1,'Business'), (0, 'Customers') )
for_biz = forms.TypedChoiceField(
coerce=lambda x: bool(int(x)),
choices=choices,
widget=forms.RadioSelect,
)
class Meta:
model = Feature
fields = (
'for_biz',
)
views.py:
def edit_feature(request, f_id):
f = get_object_or_404(Feature, id=f_id)
form = FeatureForm(instance=f)
....
Make it a PositiveSmallIntegerField with choices. BooleanField doesn't really buy you anything, and as you already saw it only gives you more trouble to deal with.

Django: Admin: changing the widget of the field in Admin

I have a model with a boolean value like that:
class TagCat(models.Model):
by_admin = models.BooleanField(default=True)
This appears as a checkbox in admin.
How could I use this as a radio button in admin?
Also, how do I make it be always with a certain selected value in admin?
Also, I want the default value to be the opposite, when a non-admin user adds a TagCat. This field should be hidden from him.
Can someone tell me how to do this? Django documentation doesn't seem to go in such details.
UPDATE 1: Code that gets me done with 1) (don't forget tot pass CHOICES to the BooleanField in the model)
from main.models import TagCat
from django.contrib import admin
from django import forms
class MyTagCatAdminForm(forms.ModelForm):
class Meta:
model = TagCat
widgets = {
'by_admin': forms.RadioSelect
}
fields = '__all__' # required for Django 3.x
class TagCatAdmin(admin.ModelAdmin):
form = MyTagCatAdminForm
admin.site.register(TagCat, TagCatAdmin)
The radio buttons appear ugly and displaced, but at least, they work
I solved with following info in MyModel.py:
BYADMIN_CHOICES = (
(1, "Yes"),
(0, "No"),
)
class TagCat(models.Model):
by_admin = models.BooleanField(choices=BYADMIN_CHOICES,default=1)
There is another way to do this that is, IMO much easier if you want every field of the same type to have the same widget. This is done by specifying a formfield_overrides to the ModelAdmin. For example:
from django.forms.widgets import Textarea
class MyModelAdmin(admin.ModelAdmin):
formfield_overrides = {
models.TextField: {'widget': Textarea},
}
More in the docs: https://docs.djangoproject.com/en/1.4/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_overrides
Here is a more dynamic extension of mgPePe's response:
class MyAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyAdminForm, self).__init__(*args, **kwargs)
self.fields['by_admin'].label = 'My new label'
self.fields['by_admin'].widget = forms.RadioSelect()
class Meta:
model = TagCat
class MyAdmin(admin.ModelAdmin):
fields = ['name', 'by_admin']
form = MyAdminForm
This way you get full control over the fields.