django model choices make no effect on saved data - django

Following the django documentation I try to make a choices field in my
models.py:
class MyModel(Model):
ACTION_CHOICES = (
('d', 'delete'),
('c', 'create'),
('u', 'update'),
)
action = CharField(max_length=1, choices=ACTION_CHOICES)
But when I run it with
action = MyModel(action='create')
action.save()
and check the database the action is not c, but create. Furthermore even restriction to max_length doesn't affect on it. Ok, I moved on and tried to make it in this way:
models.py
class MyModel(Model):
ACTION_CHOICES = (
(1, 'delete'),
(2, 'create'),
(3, 'update'),
)
action = IntegerField(max_length=1, choices=ACTION_CHOICES)
code.py:
action = MyModel(action='create')
action.save()
But I got an error that str field can't be saved like int (database has the int field, but MyModel.action is str) What Do I miss here? Aren't choices supposed to convert value on save?
Best regards,

There are several questions in your post:
1) The choices can only restrict what can be entered through a Django form. When you manually instantiate your object you can insert any value. So
action = MyModel(action='create')
actually inserts the string create into the action column.
2) Normally max_length should stop you from inserting a string longer than 1 character into that column, but I guess you are using SQLite which has no restriction on VARCHAR lengths.
3) Django has no easy conversion from the display values to choice keys. You can have a look at django-model-utils for a wrapper on choices.

When you define choices
ACTION_CHOICES = (
(1, 'delete'),
(2, 'create'),
(3, 'update'),
)
the values stored in the db are 1, 2, 3, while 'delete', 'create' etc.. are just labels, just to explain, the prev choices results in such select input element:
<select>
<option value="1">delete</option>
<option value="2">create</option>
<option value="3">update</option>
</select>
Choices are not supposed to convert value on save, are supposed to map db values with labels, and used when rendering the form widget and when retrieving the field representation (get_field_display). But if you set directly the value of a column it doesn't play a role.
If you want to use "talking keys" you can define them as class properties, like
class MyModel(models.Model):
DELETE_ACTION = 1
CREATE_ACTION = 2
UPDATE_ACTION = 3
ACTION_CHOICES = (
(DELETE_ACTION, 'delete'),
(CREATE_ACTION, 'create'),
(UPDATE_ACTION, 'update'),
)
Then you can do
action = MyModel(action=MyModel.CREATE_ACTION)
and it would be fine

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]

Django - Use Radio Buttons or Drop Down List for input

I made a model something like this:
class Enduser(models.Model):
user_type = models.CharField(max_length = 10)
Now I want user_type to have only one of the given values, say any one from ['master', 'experienced', 'noob']
Can I do this with Django?
Also, how can I display a list of radio buttons or drop-down list/select menu to chose one of these values?
You can take advantage of the choices attribute for CharField:
class Enduser(models.Model):
CHOICES = (
(u'1',u'master'),
(u'2',u'experienced'),
(u'3',u'noob'),
)
user_type = models.CharField(max_length = 2, choices=CHOICES)
This will save values 1,2 or 3 in the db and when retrieved the object, it will map it to master, experienced or noob. Take a look at the docs for more info.
Hope this helps!
Use model field choices:
CHOICES = (
('foo', 'Do bar?'),
...
)
class Enduser(models.Model):
user_type = models.CharField(max_length = 10, choices=CHOICES)

Django get derived form CHOICES in templates

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]

Django set form initial data in view

I'm trying to populate a django modelform with initial data provided from an external source. To achieve that I start by pull all the needed data from the external source:
url =('http://myapi.example.com')
data = urllib2.urlopen(url)
result = json.load(data)
api_data_name = result['properties']['name']
api_data_type = result['properties']['type']
Followed by populating a dict which will serve as initial data to my form:
data = {}
for field in my_model._meta.fields:
if field.name == 'name':
data[field.name] = api_data_name
form = MyEditForm(initial=data)
Then I'm passing the form to the template and the initial data is populating my text fields as expected, but now I need to be able to set a value of a select field based on a string I receive from my external source and Im not getting how can I achieve that, since doing something like:
if field.name == 'type':
data[field.name] = api_data_type
Wont do the job cause the select element has "0", "1", "2", etc as options value and not the long description i get from api_data_type variable.
How can I get the long_description from all the options <option value="1">long_description</option> of my select field in my view so i can compare each one with api_data_type?
Heres a sample of my models.py and forms.py:
#models.py
TYPE = (
('0',_(u'Type1')),
('1',_(u'Type2')),
('2',_(u'Type3')),
)
class MyModel(models.Model):
...
type=models.CharField(max_length=30,choices=TYPE,blank=True)
...
#forms.py
class MyEditForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
...
'type': Select(attrs={'class':'select-small span2'}),
...
}
Found out how to accomplish what I asked.
# For select fields
if field.name == 'classification':
for choice in field.choices:
if choice[1].lower() == api_poi_classification.lower():
data[field.name] = choice[0]
And for any of ya trying to populate many-to-many fields (as checkboxes in my case)
# Many to many fields populate
for field in hotel_poi._meta.many_to_many:
if field.name == 'views':
if u'Vista' in api_poi_review_fields:
api_vistas = api_poi_review[u'Vista']
# The api_vistas string comes from api in the format (v1; v2; v3; v4)
views = api_vistas.split(';')
choices = field.get_choices()
temp = []
for view in views:
for choice in choices:
if view.lower().strip() == choice[1].lower().strip():
temp.append(choice[0])
data[field.name]=temp
All of this could be avoided if I had direct database access... In that case i would just need to set an object instance like m = MyModel.objects.filter(id=1) and call form = MyEditForm(instance=m)
But that was not the case and that's what makes this question a bit particular.

how to display fixed dropdown in django admin?

I would like to display priority information in a drop down. Currently i am using a integer field to store the priority, but i would like to display high/medium/low instead of letting user type in a priority.
A way to approximate this is to use a Priority database which stores 3 elements, 1:high, 2:medium, 3:low, but it seems like an overkill.
Any easier way would be much appreciated!
Jason
You can specify choices for a field http://www.djangoproject.com/documentation/models/choices/.
PRIORITY_CHOICES = ((1, 'High'),
(2, 'Medium'),
(3, 'Low'))
class MyModel(models.Model):
priority = models.IntegerField(choices=PRIORITY_CHOICES)
You could write your model like this:
from django.db import models
class Priority(models.Model):
PRIORITY_LOW = 3
PRIORITY_MEDIUM = 2
PRIORITY_HIGH = 1
PRIORITY_CHOICES = (
(PRIORITY_LOW, 'low'),
(PRIORITY_MEDIUM, 'medium'),
(PRIORITY_HIGH, 'high'),
)
priority = models.IntegerField(choices=PRIORITY_CHOICES)
You read more from the documentation
You should be able to add choices to the model element
So:
class myModel(models.Model):
mydata = models.CharField(max_length=4, choices= ((u'H', u'High',), (u'M', u'Medium'), (u'L', u'Low')))
Would store H,M,L in the DB, but present High, Medium, Low. Admin defaults fields with the choices attribute to a drop down selector