Как сохранить данные MultipleChoiceField с помощью Django ModelForm

Я пытаюсь сохранить данные из формы ModelForm, которая имеет поля MultipleChoiceFields. Я хочу, чтобы пользователь мог выбрать несколько timeframes и сохранить эти данные в базе данных.

Пока что при отправке формы с использованием поля MultipleChoiceField я ничего не получаю обратно.

Вот мой models.py:

class InfoFormModel(models.Model):
    YES_NO = (
        ('yes', 'Yes'),
        ('no', 'No'),
    )
    TIMEFRAME = (
        ('1_weeks', '1 Week'),
        ('2_weeks', '2 Weeks'),
        ('3_weeks', '3 Weeks'),
        ('4_weeks_plus', '4 Weeks+'),
    )
    PAGES_NEEDED = (
        ('about_page', 'About Page'),
        ('contact_page', 'Contact Page'),
        ('blog_page', 'Blog Page'),
        ('map_page', 'Map Page'),
        ('ecommerce_page', 'Ecommerce Page'),
    )
    brand_name = models.CharField(
        blank=False, null=False, max_length=500, default='')
    logo = models.CharField(choices=YES_NO, blank=False,
                            null=False, max_length=500, default='no')
    what_is_the_service = models.TextField(
        blank=False, null=False, max_length=5000, default='')
    contact_number = models.BigIntegerField(blank=True, null=True, default='')
    email = models.EmailField(blank=True, null=True,
                              max_length=300, default='')
    timeframe = models.CharField(
        choices=TIMEFRAME, max_length=100, blank=False, null=False, default='')
    aim = models.TextField(blank=False, null=False,
                           max_length=5000, default='')
    products_product_images = models.CharField(
        choices=YES_NO, blank=False, max_length=500, null=False, default='')
    products_info = models.CharField(
        choices=YES_NO, blank=False, null=False, max_length=500, default='')
    pages_needed = models.CharField(
        choices=PAGES_NEEDED, blank=True, null=True, max_length=500, default='')

    def __str__(self):
        return self.brand_name

forms.py:

class InfoForm(forms.ModelForm):
    YES_NO = (
        ('yes', 'Yes'),
        ('no', 'No'),
    )
    TIMEFRAME = (
        ('1_weeks', '1 Week'),
        ('2_weeks', '2 Weeks'),
        ('3_weeks', '3 Weeks'),
        ('4_weeks_plus', '4 Weeks+'),
    )
    PAGES_NEEDED = (
        ('about_page', 'About Page'),
        ('contact_page', 'Contact Page'),
        ('blog_page', 'Blog Page'),
        ('map_page', 'Map Page'),
        ('ecommerce_page', 'Ecommerce Page'),
    )
    brand_name = forms.CharField(label='', widget=forms.TextInput(attrs={'placeholder' : 'Business/Brand Name?'}))
    logo = forms.CharField(label='Do you have a logo?', widget=forms.RadioSelect(choices=YES_NO))
    what_is_the_service = forms.CharField(label='', widget=forms.Textarea(attrs={'placeholder' : 'What service are you providing?'}))
    contact_number = forms.CharField(label='', widget=forms.NumberInput(attrs={'placeholder' : 'Contact number'}))
    email = forms.CharField(label='', widget=forms.EmailInput(attrs={'placeholder' : 'Email address'}))
    timeframe = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple,
                                          choices=TIMEFRAME)
    aim  = forms.CharField(label='', widget=forms.Textarea(attrs={'placeholder' : 'What will be the aim for your website?'}))
    products_product_images = forms.CharField(label='Do you have any product images?', widget=forms.RadioSelect(choices=YES_NO))
    products_info = forms.CharField(label='Do you your product info (eg, product names, pricing, descriptions etc.)?', widget=forms.RadioSelect(choices=YES_NO))
    pages_needed = forms.CharField(label="Select which pages you'll need?", widget=forms.RadioSelect(choices=PAGES_NEEDED))


    class Meta:
        model = InfoFormModel
        fields = (
            'brand_name',
            'logo',
            'what_is_the_service',
            'contact_number',
            'email',
            'timeframe',
            'aim',
            'products_product_images',
            'products_info',
            'pages_needed',
        )

views.py

def home(request):
    submitted = False
    if request.method == 'POST':
        form = InfoForm(request.POST)
        if form.is_valid():
            form.save()
            return render(request, 'thanks.html')
            # return HttpResponseRedirect('/?submitted=True')
    else:
        form = InfoForm()
        if 'submitted' in request.GET:
            submitted = True

    form = InfoForm

    context = {
        'form': form,
        'submitted': submitted,
    }
    return render(request, 'home.html', context)

Любая помощь будет очень признательна. Спасибо!

Я думаю, что существует несоответствие между полями вашей модели и полями формы.

В вашей модели вы определили поле timeframe как:

timeframe = models.CharField(choices=TIMEFRAME, max_length=100, blank=False, null=False, default='')

А в коде вашей формы вы определили поле timeframe как:

timeframe = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=TIMEFRAME)

После проверки данных, MultipleChoiceField попытается присвоить список атрибуту timeframe на InfoFormModel (btw, ужасное именование). Этот атрибут модели должен содержать строку.

Я думаю, что есть два пути решения этой проблемы.

  1. Используйте ArrayField в модели posgtres. ArrayField поддерживает присвоение списков. Не забудьте сделать миграции и запустить их. Также обратите внимание, что это работает, только если вы используете БД PostgreSQL.
from django.contrib.postgres.fields import ArrayField


class InfoFormModel(models.Model):
    ...
    timeframe = ArrayField(CharField(choices=TIMEFRAME, max_length=100), default=[])
    ...
  1. Переопределить MultipleChoiceField для возврата строки вместо списка. Переопределив, вы можете заставить поле возвращать список в виде строки, разделенной запятыми. Это также означает, что в коде модели необходимо убрать из поля квантор choices, поскольку поля не пройдут валидацию в случае сохранения более одного варианта.
Вернуться на верх