Django get derived form CHOICES in templates - django

Hi I have a Django form:
BAR_CHOICES = (
('1', 'Cheers'),
('2', 'The Blue Oyster'),
...
)
class ProjectInfoForm(forms.Form):
foo = forms.ChoiceField(
CHOICES = BAR_CHOICES,
)
...
of course when I save this data to a model the values are stored as integers in the db. Now, when I grab these values from the saved data they are integers. What is the best approach for getting the choice strings they are associated with. Should I import the BAR_CHOICES into the template?

Django provides a way to directly fetch the related string. Take a look here
Update:
Example:
BAR_CHOICES = (
('1', 'Cheers'),
('2', 'The Blue Oyster'),
...
)
class Project(models.Model):
foo = models.PositiveIntegerField(choices=BAR_CHOICES)
...
In the template you could simply do {{project.get_foo_display}}

If you want to render the choices in a template, you can iterate over form.field.choices.
If you want the string representation of a selected value in the template, you're going to have to write something in your view or form that can get you that information.
You could emulate get_FOO_display in your form..
def get_foo_display(self):
return dict(BAR_CHOICES)[self.foo]

Related

Search bar in the models.CharField containing choiches Django

I wanted to know if there is a way to insert a search bar in the Django choices, that is instead of manually searching the various choices if it is possible to use a filter bar to search for our choice in Django Admin - Models.
Well I think django-filter library provide you with maybe good facilities for this purpose, I have briefly given you some examples from it's documentation below:
ChoiceFilter
This filter matches values in its choices argument. The choices must be explicitly passed when the filter is declared on the FilterSet.
class User(models.Model):
username = models.CharField(max_length=255)
first_name = SubCharField(max_length=100)
last_name = SubSubCharField(max_length=100)
status = models.IntegerField(choices=STATUS_CHOICES, default=0)
STATUS_CHOICES = (
(0, 'Regular'),
(1, 'Manager'),
(2, 'Admin'),
)
class F(FilterSet):
status = ChoiceFilter(choices=STATUS_CHOICES)
class Meta:
model = User
fields = ['status']
TypedChoiceFilter
The same as ChoiceFilter with the added possibility to convert value to match against. This could be done by using coerce parameter. An example use-case is limiting boolean choices to match against so only some predefined strings could be used as input of a boolean filter:
import django_filters
from distutils.util import strtobool
BOOLEAN_CHOICES = (('false', 'False'), ('true', 'True'),)
class YourFilterSet(django_filters.FilterSet):
...
flag = django_filters.TypedChoiceFilter(choices=BOOLEAN_CHOICES,
coerce=strtobool)
MultipleChoiceFilter
The same as ChoiceFilter except the user can select multiple choices and the filter will form the OR of these choices by default to match items. The filter will form the AND of the selected choices when the conjoined=True argument is passed to this class.
Multiple choices are represented in the query string by reusing the same key with different values (e.g. ‘’?status=Regular&status=Admin’’).
TypedMultipleChoiceFilter
Like MultipleChoiceFilter, but in addition accepts the coerce parameter, as in TypedChoiceFilter.
See also:
django-filter [Docs]
django-filter [Github]

How to get values instead of Key from Choices when you use objects.filter(id=pk).values() on Django

Is it possible to get the Choice VALUE instead of KEY, when you use Django model.objects.filter()
In my case, I have
models.py
BIN_TYPE=(
('BLUE', 'Blue'),
('BROW', 'Brown'),
('GREY','Grey'),
('OTHER','Other')
)
class BinCollection(models.Model):
bin_type = models.CharField(max_length=25, choices=BIN_TYPE)
views.py
bin_collections':BinCollection.objects.filter(id=pk).values('bin_type')
From HTML side, I can use obj.get_bin_type_display() . However, I need this data on views for further calculations.
If I was using a table, bin_type__values would work but not for choice. Any ideas?

Filter on a field with choices

I have this field:
operation = models.CharField(max_length=10, choices=OPERATIONS)
Having this filter works:
class OperationFilter(django_filters.Filter):
def filter(self, qs, value):
try:
qs = qs.filter(operation=value.upper())
except:
pass
return qs
With url:
/api/v1/operation/?operation=CREATE
But having the default filter (without an extra OperationFilter) fails with:
{
"operation": [
"Select a valid choice. %(value)s is not one of the available choices."
]
}
Why is a filter on a field with choices failing?
For other, non-choice fields, default filters are working fine:
/api/v1/operation/?recipient=recipient-19
EDIT
The OPERATIONS:
from enum import Enum
def enum_as_choices(enum_class):
"""From an enum class, generate choices for a django field"""
return ((entry, entry.value) for entry in enum_class)
class OperationType(Enum):
CREATE = 'CREATE'
STATUS = 'STATUS'
EXPAND = 'EXPAND'
DELETE = 'DELETE'
OPERATIONS = enum_as_choices(OperationType)
You are using django_filters package, I suggest reading docs, since you already have support for this
https://django-filter.readthedocs.io/en/master/ref/filters.html#choicefilter
Just point out your choices to the value suggested by the other answers (or check the example in docs)
The choices you written would be converted to this pythonic representation:
(
('OperationType.CREATE', 'CREATE'),
('OperationType.STATUS', 'STATUS'),
('OperationType.EXPAND', 'EXPAND'),
('OperationType.DELETE', 'DELETE')
)
As you can see the actual values stored in your operation field (in DB) are 'OperationType.CREATE', etc.
So you should change your choices to normal constant choices or you should filter by something like 'OperationType.CREATE' which is not a good option IMO.
also you can change your enum_as_choices method like this:
def enum_as_choices(enum_class):
"""From an enum class, generate choices for a django field"""
return ((entry.name, entry.value) for entry in enum_class)
You haven't defined a blank/default choice in your OPERATIONS. To do so, add something like this:
OPERATIONS = (
('', 'NONE'),
# the rest of your choices here...
)
But you would also need to update your model to be:
operation = models.CharField(max_length=10, choices=OPERATIONS, default='NONE')

Defining field order in a dynamic Django form

I have written a factory function to create dynamic forms, as described towards the end of James Bennett's helpful post http://www.b-list.org/weblog/2008/nov/09/dynamic-forms/.
The form works perfectly well, but the items are shown in a somewhat random looking order, due (I assume) to the fact that fields is a dictionary (please see code sample below). How can I prescribe a defined display order for the form fields?
def make_form(assessment):
'''
Factory function to build and return dynamic AssessmentForms
'''
entries = assessment.entry_set.all()
fields = {}
for entry in entries:
fields[entry.name] = forms.ChoiceField(
required=False,
initial=entry.rating,
choices=CHOICES,
widget=forms.RadioSelect()
)
return type('AssessmentForm', (forms.BaseForm,), { 'base_fields': fields })
Yuji Tomita is right. You can use for example:
from django.utils.datastructures import SortedDict
fields = SortedDict()
for entry in entries:
fields[entry.name] = forms.ChoiceField(
required=False,
initial=entry.rating,
choices=CHOICES,
widget=forms.RadioSelect()
)
Use a SortedDict (djangos implementation of OrderedDict 2.7+)
https://github.com/django/django/blob/master/django/utils/datastructures.py

Utilization of get_FOO_display()

I want to show the human-readable name for the type selected but I
keep getting the stored value.
TYPE_CHOICES = (
('0', 'Basic'),
('1', 'Full'),
('2', 'Intermediate'),
)
class ServiceType(models.Model):
type = models.IntegerField(max_length=1, choices=TYPE_CHOICES)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def __unicode__(self):
return '%s' % (self.get_type_display())
It seems that you have your answer, but as another link, I'd just like to point out James Bennett's thoughts on this:
Handle choices the right way
I think it is a pretty convenient way to do things, and removes the 'magic number' aspect of things. Worth a read IMO, even if you go for a different option.
From his article (quoted in case it disappears):
class Entry(models.Model):
LIVE_STATUS = 1
DRAFT_STATUS = 2
HIDDEN_STATUS = 3
STATUS_CHOICES = (
(LIVE_STATUS, 'Live'),
(DRAFT_STATUS, 'Draft'),
(HIDDEN_STATUS, 'Hidden'),
)
# ...some other fields here...
status = models.IntegerField(choices=STATUS_CHOICES, default=LIVE_STATUS)
Now we can just import the Entry model and query like so:
live_entries = Entry.objects.filter(status=Entry.LIVE_STATUS)
draft_entries = Entry.objects.filter(status=Entry.DRAFT_STATUS)
You probably want to use ChoiceField instead of IntegerField in your model. It sounds like you are seeing an input tag with type=text in your admin but want a select tag. The default widget associated with a IntegerField is TextInput which would explain what you are seeing.
Another option is to write your own admin and explicitly call out that you want type to be a ChoiceField in the admin. Something like this:
class ServiceTypeAdmin(admin.ModelAdmin):
# ...
type = fields.ChoiceField(choices=TYPE_CHOICES)
admin.site.register(ServiceType, ServiceTypeAdmin)
I would personally start by switching the IntegerField to a ChoiceField. Way less work involved.
I had that same problem, and couldn't figure out why it works, but if you change the field type to CharField the get_type_display should work fine.
TYPE_CHOICES = (
('B', 'Basic'),
('F', 'Full'),
('I', 'Intermediate'),
)
class ServiceType(models.Model):
type = models.CharField(max_length=1, choices=TYPE_CHOICES)
amount = models.DecimalField(max_digits=10, decimal_places=2)
Rookie mistake, I've changed the tuple values from ('0', 'Basic) to (0, 'Basic') and it worked. I didn't realize that I was saving a char value as an integer value.
Thanks for your help.
Trick
use TypedChoiceField()
The answer of your questoin lies in using TypedChoiceField, not ChoiceField.
you are getting type field from a django form, using the cleaned_data from a ChoiceField. The problem with this is that the output from a ChoiceField is a string, not an integer.
if you use get_type_display() right after saving the form, u would probably get the value, but when u have try to retrieve the value from the DB, you would get integer instead of string(because your are saving type as Integer field), here you wont be able to get value with get_type_display.
Having now looked into this, I see that you should have used the TypedChoiceField, to ensure that the output from cleaned_data is always an integer or string.
first of all change IntergerField to Char field or SmallIntergetField.
Hope this helps.
Code
type = models.SmallIntegerField(choices=TYPE_CHOICES)
in forms.py
type = TypedChoiceField(coerce=int, required=False, empty_value=0, choices=TYPE_CHOICES)
another possibility is that you could use MODELFORM and provide the widgets for the field.
Forms.py
class abc(forms.Modelform)
class Meta:
model = FOO
widgets = {
'type': forms.TypedChoiceField(coerce=int, required=False, empty_value=0, choices=TYPE_CHOICES),
type shadows with an in-built bulletin, that's why get_type_display() has no effects. Avoid using column names as type instead use service_type or something else and use get_service_type_display() i.e get_<column_name>_display().