HereHere
HereHere

Reputation: 762

Can't save ManyToMany using MultipleChoiceField

I'm trying to use custom CHOICE in MultipleChoiceField. I can save everything but not Employees.

models.py

class Employee(models.Model):
    employee_id = models.CharField(max_length=20, unique=True)

    def __str__(self):
        return '%s' % self.employee_id


class Task(models.Model):
    employee = models.ManyToManyField(Employee, blank=True, null=True)
    task = models.CharField(max_length=100)
    comment = models.CharField(max_length=200)

    def __str__(self):
        return '%s' % self.task

forms.py

class TaskCreateForm(forms.ModelForm):

    CHOICES = (
        ('123', 'Adam'),
        ('321', 'John'),
        ('666', 'Lucy'),
    )

    employee = forms.MultipleChoiceField(choices=CHOICES, required=True)


    def __init__(self, custom_choices=None, *args, **kwargs):
        super(TaskCreateForm, self).__init__(*args, **kwargs)
        if custom_choices:
            self.fields['employee'].choices = custom_choices

    class Meta:
        model = Task

So basically I added custom_choices, because I want to show in MultipleChoiceField Employee's names ('Adam', 'John' ,'Lucy') and save their

employee_id ('123','321', 666').

views.py

class TaskCreateView(CreateView):
    model = Task
    form_class = TaskCreateForm
    template_name = "task/create.html"

    def form_valid(self, form):
        self.object = form.save()
        return redirect('/task_page/')

I can see form on my page and I can choose employees, but employee_id is not saving. Only other fields task and comment are being saved.

Update for clarification:

There are no names in my models. I just want to show custom names for employee_id. That's why I need ('123','Adam') - I want to save '123' but display in my form 'Adam'

Upvotes: 0

Views: 774

Answers (2)

René Fleschenberg
René Fleschenberg

Reputation: 2548

Well, the basic solution still stays the same:

queryset=Employee.objects.filter(employee_id__in=['some_id', 'other_id'])

The key point is that you should use a ModelMultipleChoiceField.

If you want to display the names rather than the IDs, there are a couple of ways to do that, e.g. a get_name method on the model and a custom ModelMultipleChoiceField, as described here: https://stackoverflow.com/a/3167840/1180983

So that would look roughly like this:

EMPLOYEE_NAMES = {
    '123': 'Adam',
    '321': 'John',
}

class Employee(models.Model):
    employee_id = models.CharField(max_length=20, unique=True)

    def get_name(self):
        return EMPLOYEE_NAMES.get(self.employee_id, 'unknown')

class EmployeeMultipleChoiceField(ModelMultipleChoiceField):
    def label_from_instance(self, obj):
        return obj.get_name()

class TaskCreateForm(forms.ModelForm)
    employees = EmployeeMultipleChoiceField(
        queryset=Employee.objects.filter(employee_id__in=['123', '321'])
    )

Upvotes: 1

René Fleschenberg
René Fleschenberg

Reputation: 2548

Your ModelForm does not know what to do with the employee field on save(), because you do not tell it anywhere, and that field is not connected to any model. Use a ModelMultipleChoiceField instead.

If you need to restrict the choices the ModelMultipleChoiceField allows, use the queryset argument:

employees = forms.ModelMultipleChoiceField(
    queryset=Employee.objects.filter(name__in=['Alice', 'Bob'])
)

Also, think about where to use plural and where to use singular names, so you do not confuse yourself (employees instead of employee above).

Upvotes: 0

Related Questions