Django多选复选框实现、模型表单分离及数据库报错求助
解决方案:实现多选服务复选框并修复数据库问题
一、核心问题分析
- 模型设计错误:原
DescricaoAtendimento模型的外键关系完全颠倒,且CadastroEmpreendedor中仅定义了服务选项元组但未作为字段使用,导致无法存储多选服务数据。 - 数据库报错原因:添加非空外键字段时,数据库现有空行无默认值,且该模型本身设计冗余,直接删除即可解决。
- 表单与模型不匹配:表单绑定了错误的模型,无法关联创业者的服务选择逻辑。
二、修正方案(两种可选)
方案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)
四、解决数据库报错并迁移
- 删除冗余模型:删除原
models.py中的DescricaoAtendimento类,以及forms.py中相关引用。 - 清理迁移记录(可选):如果之前已迁移过错误模型,可直接删除数据库中的
descricaoatendimento表,或回滚迁移。 - 执行迁移:
python manage.py makemigrations python manage.py migrate
五、Bootstrap4模板实现
- 安装依赖:
pip install django-bootstrap4
- 配置settings.py:在
INSTALLED_APPS中添加'bootstrap4'。 - 编写
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
相关产品推荐
相关产品推荐

