Django Python With Gspread: 'choices' must be an iterable containing (actual value, human readable name) tuples

Viewed 169

I am trying to do something that I have never seen done before with django, I am trying to make a model field(path_choices) that shows all of the unique path_names from my google sheet in a choice box so that the user can select one of them. However when I tried to make my choices CharField I am getting the error:


ERRORS:

dashboard.Robot.path_choices: (fields.E005) 'choices' must be an iterable containing (actual value, human readable name) tuples.

Right now the google sheet that I am trying to pull from with gspread only has two path-names, so if anybody has any idea on what is causing this problem or what I can do better with this, the help would be appreciated! My Code (UPDATED CODE):

from django.db import models

class Robot(models.Model):
    name = models.CharField(max_length=100)
    status_choices = [('driving', 'driving'), ('waiting', 'waiting'), ('stuck', 'stuck')]
    status = models.CharField(choices=status_choices, max_length=7, default='waiting')
    path_choices = models.CharField(max_length=255)

My Form:

from django import forms
from django import forms
from .models import Robot
import gspread
from oauth2client.service_account import ServiceAccountCredentials

class RobotForm(forms.ModelForm):
    def _generate_choices():
        scope = ["REDACTED",'REDACTED',"REDACTED","REDACTED"]
        creds = ServiceAccountCredentials.from_json_keyfile_name("dashboard/Files/creds.json", scope)
        client = gspread.authorize(creds)
        sheet = client.open("tutorial").sheet1
        path_name_fetch =  sheet.col_values(1)
        path_names = []
        temp_list = []
        path_options = []
        for i in path_name_fetch:
            if i not in path_names:
                path_names.append(i)
        for path_name_options in path_names:
            temp_list.append(f'{path_name_options}')
            temp_list.append(f'{path_name_options}')
            path_options.append(tuple(temp_list))
        
    path_choices = forms.ChoiceField(choices=_generate_choices())
    class Meta:
        model = Robot
        fields = {'path_choices'}
1 Answers

What you're trying to do may not be in line with the intended use of Choices. Whenever the possibilities of a Choice change at the Model level, new migrations must be made.

Your implementation of Choice for Robot.status is static and in line with the example in the Django documentation.

If instead you wanted to use a dynamic Choice for your path_choices that is retrieved from Google Sheets, I would recommend doing this in the ModelForm using a ChoiceField. According to the documentation, the available choices can come from a callable, which would be your path_options wrapped in a function. path_options should then become a CharField without choices, since you manage those in the submission, rather than at the model level.

Models.py

class Robot(models.Model):
    ...
    path_choices = models.CharField(max_length=255)

ModelForms.py

class RobotForm(ModelForm):
      def _generate_choices():
            # Query GSpread
            choices = [('choice','choice'), ...]
            return choices

      path_choices = forms.ChoiceField(choices=_generate_choices())
      class Meta:
            model = Robot
            fields = ['path_choices', ...]
Related