fabio
fabio

Reputation: 1365

Pass choices from views to form

I use the form Bannerform to create new Banner object trough the add_banner views (and template). I use the class Options to definite which affiliation objects to permit in the form Bannerform (affiliation field).

I'm trying to pass choices from the views to the form but it give me ValueError: too many values to unpack (expected 2)

My code worked when new_affiliation was a ForeignKey but now I need more values. I think I must definite 'choices' in the views or I will have problem on the first migration (it appear to call the database tables from the models.py but not from the views.py, so if I put Options.objects.get(id=1) on the models.py it give error because the tables don't exist yet).

My form.py:

from django import forms
from core.models import Options, Banner, Affiliation #somethings other

class BannerForm(forms.ModelForm):
    name = forms.CharField(max_length=32)
    affiliation = forms.ChoiceField('choices')
    #affiliation = forms.ModelChoiceField('choices') #same error
    class Meta:
        model = Banner
        exclude = (#some fields)

My models.py:

from django.db import models
from django.contrib.auth.models import User
from django import forms

class Options(models.Model):
    new_affiliation = models.ManyToManyField('Affiliation')
    #new_affiliation = models.ForeignKey('Affiliation') #this worked (with some changes in views)

class Affiliation(models.Model):
    name = models.CharField(max_length=32, unique=True)

class Banner(models.Model):
    name = models.CharField(max_length=32, unique=True)
    affiliation = models.ForeignKey(Affiliation)

My views.py:

def add_banner(request):
    if request.method == 'POST':
        #some code here
    else:
        options = Options.objects.get(id=1)
        print(options.new_affiliation.all()) #controll
        choices = options.new_affiliation.all()
        print(choices) #controll
        form = BannerForm(choices, initial={            
            #some code regarding other fields
            })       
    return render(request, 'core/add_banner.html', {'form': form})

My add_banner.html:

<form role="form" id="banner_form" enctype="multipart/form-data "method="post" action="../add_banner/">

  {% csrf_token %}
  {% for hidden in form.hidden_fields %}
  {{ hidden }}
  {% endfor %}

  {% for field in form.visible_fields %}
    {{ field.errors }}
    {{ field.label }}
    {{ field }}      
    {{ field.help_text }}
    <br />
  {% endfor %}

Any help will be apreciated.

Updated. I changed only views.py:

def add_banner(request):
    if request.method == 'POST':
        #some code here
    else:
        options = Options.objects.get(id=1)
        print(options.new_affiliation.all()) #controll
        choices = tuple(options.new_affiliation.all())
        print(choices) #controll
        form = BannerForm(choices, initial={            
            #some code regarding other fields
            })       
    return render(request, 'core/add_banner.html', {'form': form})

But still give error.

Update 2. If I pass choices directly from form.py it works: My views.py:

def add_banner(request):
    if request.method == 'POST':
        #some code here
    else:
        form = BannerForm(request.POST or None, initial={
            #some code regarding other fields
            })
    return render(request, 'core/add_banner.html', {'form': form})

My forms.py:

class BannerForm(forms.ModelForm):
    options = Options.objects.get(id=1)
    choices = options.new_affiliation.all()
    name = forms.CharField(max_length=32)
    affiliation = forms.ModelChoiceField(choices)

Unluckly this give problems on the first migration (see above).

I'm trying to pass choices using some init method...

My forms.py:

class BannerForm(forms.ModelForm):
    name = forms.CharField(max_length=32)
    affiliation = forms.ModelChoiceField(choices)

    def __init__(self, *args, **kwargs):
        options = Options.objects.get(id=1)
        choices = options.new_affiliation.all()
        #choices = kwargs.pop('choices')
        super(RegentForm, self).__init__(*args, **kwargs)
        self.fields['affiliation'] = choices

but it say that choices is not definite

Upvotes: 4

Views: 4237

Answers (2)

fabio
fabio

Reputation: 1365

Done!

My form.py:

class BannerForm(forms.ModelForm):
    name = forms.CharField(max_length=32, label='Nome')

    def __init__(self, *args, **kwargs):
        options = Options.objects.get(id=1)
        choices = options.new_affiliation.all()
        super(BannerForm, self).__init__(*args, **kwargs)
        self.fields['affiliation'] = forms.ModelChoiceField(choices)
        self.fields['affiliation'].initial = choices

    class Meta:
        model = Banner

My views.py:

def add_banner(request):
    if request.method == 'POST':
        #some code here
    else:
        form = BannerForm(request.POST or None, initial={
        #some code here
            })
    return render(request, 'core/add_banner.html', {'form': form})

Thank you

Upvotes: 2

Michael Fourre
Michael Fourre

Reputation: 3015

From what I can see here, it looks like you are getting an error "too many values to unpack" because you are not sending "choices" as the correct type. A ChoiceField takes choices only as a tuple, as seen in the documentation for models. If you are looking to define choices based on a QuerySet, you'll have to convert it into a tuple which can be interpreted as valid "choices". In one of my projects for example, I needed to prepare a set of years as a tuple so that I could allow users to select from a list of pre-determined years. I specified the following function to do this:

def years():
    response = []
    now = datetime.utcnow()
    for i in range(1900, now.year + 1):
        response.append([i, str(i)])
    return tuple(response)

Since tuples are meant to be immutable, it usually isn't a good idea to cast them, just based on principle. However, in this case it seems necessary as a measure to declare that you are okay with the possible variability with these statements.

In your specific situation, you might consider doing something like this:

choices = tuple(options.new_affiliation.all().values())

I have not tested this code, and I frankly am not completely familiar with your project and may be mistaken in some part of this response. As a result, it may require further tweaking, but give it a try. Based on your error, this is definitely where the program is breaking currently. Update here if you make any progress.

Upvotes: 2

Related Questions