How to get multiple values from a model field into a form choice field?

Question:

I have a model called Listing that has a field called categories that stores all the different categories. There is also a form with a field called categories that should show a choice field to the user, where the choices should be the values stored in the Listing.categories model field. So I tried to loop through it but that is not possible as the choice field values are stored in a dict format.

So how do I get the values from the model field into the choice field?

models.py

class Category(models.Model):
    name = models.CharField(max_length=50)


class Listing(models.Model):
    ...
    category = models.ForeignKey(Category, on_delete=models.PROTECT, null=True)

forms.py:

from .models import Listing

for i in Listing.category:
    category_choices = (
        (i, Listing.category)
    )


class NewListing(forms.Form):
    ...
    category = forms.ChoiceField(choices=category_choices)
Asked By: tices

||

Answers:

You can use a ModelChoiceField in order to pass a queryset and allow the user to choose between all models in this queryset.
You would then get something like:

category = forms.ModelChoiceField(queryset=Category.objects.all())
Answered By: Balizok

For me I have done it in this way:

Models (choice fields are in your models)

   CATEGORYCHOICES=(
    (1, 'New'),
    (2, 'Refurbished'),
    (3, 'Opened'),
    (4, 'Used'),
)
    
    class Listing(models.Model):
    ...
    categories = models.IntegerField(choices=CATEGORYCHOICES,default=0)

Then in your forms.py you will need to use "form select"

class NewListing(ModelForm):
    class Meta:
        model = Listing
        fields = ('field_name1','field_name2')
        
        labels ={
            'field_name1': '',
            'field_name2': '',
        }
        widgets = {
                    'field_name1':forms.Select(attrs={'class':'form-control'}),
                    'field_name2': forms.Select(attrs={'class':'form-control'}),
          }

Does it make sense?

You will obvioulsy need to process that data in your views.py. Let me know if you have a question, I am happy to share the code I have for this.

Answered By: PhilM

If Category is a model then your condition_choices should be redundant, why use a model if those options are hardcoded? Instead of just using forms.Form, you should use a model form forms.ModelForm. You can then pass your category choices through queryset:

class ListingForm(forms.ModelForm):
   self.categories = Category.objects.all()
   super(ListingForm, self).__init__(*args, **kwargs)
   self.fields['categories'].queryset = self.categories
Answered By: 0sVoid

The class definition as shown here is executed before the models ar initialized. So, you have to do something dynamic to construct the form class or to patch its choices.

I’ve just tried this at the console and it seems to work. Define BaseForm with all the fields that you don’t want to fiddle with a run-time:

class BaseForm( forms.Form): 
    something = forms.WhateverField( args)
    ...

omitting your dynamic choicefield. At runtime, say in the get_form_class method of a class-based view, build the form you want using 3-argument type.

I’m not too clear on what queryset returns the set of categories, so adapt this code

class SomethingView( FormView): 
    ...
    def get_form_class( self):

    # interrogate the DB to get a list of categories, or categories and labels.

         choices = list( enumerate(categories) ) # [ (0,'cat0'), (1,'cat1'), ...]
         choicefield = forms.ChoiceField( choices=choices, ...)

         return type('My_runtime_form',
             (BaseForm, ),
             { 'category': choicefield }
         ) 

If you had a DB table Categories containing a pair of values choice_value and choice_label then you could obtain choices as

choices = Category.objects.filter( ... # maybe via a ManyToMany relation
    ).values_list('choice_value', 'choice_label')
Answered By: nigel222