You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django多选复选框实现、模型表单分离及数据库报错求助

解决方案:实现多选服务复选框并修复数据库问题

一、核心问题分析

  1. 模型设计错误:原DescricaoAtendimento模型的外键关系完全颠倒,且CadastroEmpreendedor中仅定义了服务选项元组但未作为字段使用,导致无法存储多选服务数据。
  2. 数据库报错原因:添加非空外键字段时,数据库现有空行无默认值,且该模型本身设计冗余,直接删除即可解决。
  3. 表单与模型不匹配:表单绑定了错误的模型,无法关联创业者的服务选择逻辑。

二、修正方案(两种可选)

方案1:灵活可扩展的多对多关联(推荐)

适合后续需要动态添加/修改服务选项的场景,单独创建服务模型存储选项。

1. 重写models.py
from django.db import models
from django_cpf_cnpj.fields import CPFField, CNPJField    

# 单独存储服务选项,支持后台动态管理
class Servico(models.Model):
    codigo = models.CharField(max_length=50, unique=True, verbose_name='Código')
    nome = models.CharField(max_length=120, verbose_name='Nome do Serviço')

    def __str__(self):
        return self.nome

class CadastroEmpreendedor(models.Model):
    cnpj = CNPJField('CNPJ')
    cpf = CPFField('CPF')
    nome = models.CharField('Nome', max_length=120)
    nascimento = models.DateField('Data de Nascimento')
    email = models.EmailField('Email', max_length=100)
    telefone_principal = models.CharField('Telefone Principal', max_length=11)
    telefone_alternativo = models.CharField('Telefone Alternativo', max_length=11, blank=True)
    # 多对多关联服务模型,实现一个创业者选多个服务
    servicos = models.ManyToManyField(Servico, verbose_name='Serviços Solicitados')

    def __str__(self) -> str:
        return self.nome
2. 填充初始服务数据

运行python manage.py shell,手动添加原有的服务选项:

from app_name.models import Servico

Servico.objects.create(codigo='ABERTURA MEI', nome='FORMALIZAÇÃO')
Servico.objects.create(codigo='ALTERAÇÃO CADASTRAL', nome='ALTERAÇÃO CADASTRAL')
Servico.objects.create(codigo='INFORMAÇÕES', nome='INFORMAÇÕES')
# 依次添加剩余选项:EMISSÃO DAS、PARCELAMENTO等

方案2:固定选项的多选字段

适合服务选项长期固定的场景,使用第三方库实现多选功能。

1. 安装依赖
pip install django-multiselectfield
2. 重写models.py
from django.db import models
from django_cpf_cnpj.fields import CPFField, CNPJField    
from multiselectfield import MultiSelectField

class CadastroEmpreendedor(models.Model):
    # 服务选项元组
    ABERTURA = 'ABERTURA MEI'
    ALTERACAO = 'ALTERAÇÃO CADASTRAL'
    INFO = 'INFORMAÇÕES'
    DAS = 'EMISSÃO DAS'
    PARC = 'PARCELAMENTO'
    EMISSAO_PARC = 'EMISSÃO DE PARCELA'
    CODIGO = 'CÓDIGO DE ACESSO'
    REGULARIZE = 'REGULARIZE'
    BAIXA = 'BAIXA MEI'
    CANCELADO = 'REGISTRO BAIXADO'
    
    descricao_atendimento_opcoes = (
        (ABERTURA, 'FORMALIZAÇÃO'),
        (ALTERACAO, 'ALTERAÇÃO CADASTRAL'),
        (INFO, 'INFORMAÇÕES'),
        (DAS, 'EMISSÃO DAS'),
        (PARC, 'PARCELAMENTO'),
        (EMISSAO_PARC, 'EMISSÃO DE PARCELA'),
        (CODIGO, 'CÓDIGO DE ACESSO'),
        (REGULARIZE, 'REGULARIZE'),
        (BAIXA, 'BAIXA MEI'),
        (CANCELADO, 'REGISTRO BAIXADO'),
    )
    
    cnpj = CNPJField('CNPJ')
    cpf = CPFField('CPF')
    nome = models.CharField('Nome', max_length=120)
    nascimento = models.DateField('Data de Nascimento')
    email = models.EmailField('Email', max_length=100)
    telefone_principal = models.CharField('Telefone Principal', max_length=11)
    telefone_alternativo = models.CharField('Telefone Alternativo', max_length=11, blank=True)
    # 多选字段绑定选项元组
    servicos = MultiSelectField(choices=descricao_atendimento_opcoes, verbose_name='Serviços Solicitados')

    def __str__(self) -> str:
        return self.nome

三、调整表单与视图

1. 重写forms.py(两种方案通用)

from django import forms
from .models import CadastroEmpreendedor

class EmpreendedorForm(forms.ModelForm):
    class Meta:
        model = CadastroEmpreendedor
        # 按需指定字段,或用__all__包含所有字段
        fields = ['cnpj', 'cpf', 'nome', 'nascimento', 'email', 'telefone_principal', 'telefone_alternativo', 'servicos']
        widgets = {
            # 用复选框渲染服务选项
            'servicos': forms.CheckboxSelectMultiple(),
            # 可选:给其他字段添加Bootstrap样式
            'nome': forms.TextInput(attrs={'class': 'form-control'}),
            'email': forms.EmailInput(attrs={'class': 'form-control'}),
        }

2. 修正views.py

from django.shortcuts import render
from django.contrib import messages
from .forms import EmpreendedorForm

def cadastro_empreendedor(request):
    if request.method == 'POST':
        form = EmpreendedorForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, 'Cadastro salvo com sucesso!')
            form = EmpreendedorForm()
        else:
            # 错误提示用error而非success
            messages.error(request, 'Erro ao salvar cadastro!')
    else:
        form = EmpreendedorForm()
    context = {'form': form}
    return render(request, 'empreendedor.html', context)

四、解决数据库报错并迁移

  1. 删除冗余模型:删除原models.py中的DescricaoAtendimento类,以及forms.py中相关引用。
  2. 清理迁移记录(可选):如果之前已迁移过错误模型,可直接删除数据库中的descricaoatendimento表,或回滚迁移。
  3. 执行迁移:
python manage.py makemigrations
python manage.py migrate

五、Bootstrap4模板实现

  1. 安装依赖:
pip install django-bootstrap4
  1. 配置settings.py:在INSTALLED_APPS中添加'bootstrap4'。
  2. 编写empreendedor.html:
{% load bootstrap4 %}
<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cadastro de Empreendedor</title>
    {% bootstrap_css %}
</head>
<body>
    <div class="container mt-4">
        <h2>Cadastro de Empreendedor</h2>
        {% if messages %}
            {% for message in messages %}
                <div class="alert alert-{{ message.tags }} mt-3">
                    {{ message }}
                </div>
            {% endfor %}
        {% endif %}
        <form method="POST" class="mt-3">
            {% csrf_token %}
            {% bootstrap_form form %}
            {% buttons %}
                <button type="submit" class="btn btn-primary">Salvar Cadastro</button>
            {% endbuttons %}
        </form>
    </div>
    {% bootstrap_javascript jquery='full' %}
</body>
</html>

内容的提问来源于stack exchange,提问作者João Marcos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 11:55:20