如何在Django中实现客户与服务订单分表存储及表单提交?
解决Django客户与服务订单分离存储的问题
问题背景
原本的Django应用将客户与服务订单字段存储在同一张表中,现在需要将客户数据拆分到独立的Clientes表,避免重复存储同一客户信息。需求是:在同一页面展示客户和服务订单的所有字段,提交时如果客户不存在则同时创建客户和服务订单;如果客户已存在,则仅创建关联该客户的服务订单。当前修改视图后只能显示客户字段,服务订单字段消失,需实现正确的表单处理逻辑。
当前现有代码
1. 新增订单视图(NovaOS)
@login_required def NovaOS(request): if request.method == 'POST': form = OrdemServicoForm(request.POST) if form.is_valid(): OS = form.save(commit=False) OS.user = request.user OS.save() return redirect('/os/list/') else: form = OrdemServicoForm() return render(request, 'serviceorders/addos.html', {'form': form})
2. 客户表单(forms.py)
class ClienteForm(forms.ModelForm): class Meta: model = Clientes fields = '__all__' widgets = { #Dados do cliente 'STATUSCliente': forms.Select(attrs={'class': 'form-control', 'placeholder': 'Status'}), 'cliente_razaosocial': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Razão Social', 'autofocus': True}), 'cliente_cpfcnpj': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'CPF/CNPJ'}), 'cliente_rgie': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'RG/I.E.'}), 'cliente_email': forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'E-mail'}), 'cliente_telefone_fixo': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Tel. fixo', 'data-mask':"(00)0000-0000"}), 'cliente_telefone_celular': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Tel. celular', 'data-mask':"(00)00000-0000"}), 'cliente_endereco': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Endereço'}), 'cliente_endereco_num': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Número'}), 'cliente_endereco_CEP': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'CEP'}), 'cliente_endereco_Bairro': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Bairro'}), 'cliente_endereco_Cidade': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Cidade'}), 'cliente_endereco_UF': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'UF'}), 'cliente_endereco_Complemento': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Complemento'}), } labels = { 'cliente_razaosocial': 'Nome/Razão social:', 'cliente_cpfcnpj': 'CPF/CNPJ:', 'cliente_rgie': 'RG/IE:', 'cliente_email': 'E-mail:', 'cliente_telefone_fixo': 'Fixo:', 'cliente_telefone_celular': 'Celular:', 'cliente_endereco': 'Endereço:', 'cliente_endereco_num': 'Num:', 'cliente_endereco_Complemento': 'Complemento:', 'cliente_endereco_CEP': 'CEP:', 'cliente_endereco_Bairro': 'Bairro:', 'cliente_endereco_Cidade': 'Cidade:', 'cliente_endereco_UF': 'UF:', }
3. 客户模型(models.py)
class Clientes(models.Model): STATUSCliente = ( ('Ativo', 'Ativo'), ('Inativo', 'Inativo'), ) #Dados Cliente cliente_razaosocial = models.CharField(max_length=100) cliente_cpfcnpj = models.CharField(max_length=20) cliente_rgie = models.CharField(max_length=20) cliente_email = models.CharField(max_length=50) cliente_telefone_fixo = models.CharField(max_length=20) cliente_telefone_celular = models.CharField(max_length=20) cliente_endereco = models.CharField(max_length=100) cliente_endereco_num = models.CharField(max_length=10) cliente_endereco_CEP = models.CharField(max_length=10) cliente_endereco_Bairro = models.CharField(max_length=30) cliente_endereco_Cidade = models.CharField(max_length=30) cliente_endereco_UF = models.CharField(max_length=20) cliente_endereco_Complemento = models.CharField(max_length=50) STATUSCliente = models.CharField(max_length=10, choices=STATUSCliente, default='Ativo') def __str__(self): return self.cliente_razaosocial
实现方案
1. 完善服务订单模型的外键关联
首先确保OrdemServico模型中添加指向Clientes的外键,这样服务订单才能关联到对应的客户:
from django.contrib.auth.models import User class OrdemServico(models.Model): cliente = models.ForeignKey(Clientes, on_delete=models.CASCADE, related_name='ordens_servico') user = models.ForeignKey(User, on_delete=models.CASCADE) # 添加你的服务订单其他字段,比如服务类型、描述等 servico_tipo = models.CharField(max_length=50) servico_descricao = models.TextField() # ... 其他字段 def __str__(self): return f"OS #{self.id} - {self.cliente.cliente_razaosocial}"
2. 修改视图,同时处理两个表单
更新NovaOS视图,同时实例化客户表单和服务订单表单,提交时验证两个表单的有效性,通过CPF/CNPJ判断客户是否存在,存在则复用,不存在则创建新客户,最后关联到服务订单:
@login_required def NovaOS(request): if request.method == 'POST': cliente_form = ClienteForm(request.POST) os_form = OrdemServicoForm(request.POST) if cliente_form.is_valid() and os_form.is_valid(): # 用CPF/CNPJ作为唯一标识查找客户,不存在则创建 cpfcnpj = cliente_form.cleaned_data['cliente_cpfcnpj'] cliente, created = Clientes.objects.get_or_create( cliente_cpfcnpj=cpfcnpj, defaults=cliente_form.cleaned_data ) # 保存服务订单,关联客户和当前登录用户 os_instance = os_form.save(commit=False) os_instance.cliente = cliente os_instance.user = request.user os_instance.save() return redirect('/os/list/') else: # GET请求时初始化两个空表单 cliente_form = ClienteForm() os_form = OrdemServicoForm() # 把两个表单传递给模板 return render(request, 'serviceorders/addos.html', { 'cliente_form': cliente_form, 'os_form': os_form })
3. 更新模板,渲染两个表单
修改addos.html模板,同时渲染客户表单和服务订单表单的字段:
<form method="post"> {% csrf_token %} <div class="section"> <h3>Dados do Cliente</h3> {{ cliente_form.as_p }} </div> <div class="section"> <h3>Dados da Ordem de Serviço</h3> {{ os_form.as_p }} </div> <button type="submit" class="btn btn-primary">Salvar Ordem</button> </form>
4. 可选优化:自动填充已有客户信息
如果想提升用户体验,可以添加AJAX功能,当用户输入CPF/CNPJ时自动查询并填充已有客户的信息:
- 前端监听
cliente_cpfcnpj输入框的blur事件,发送AJAX请求到后端接口 - 后端接口根据
CPF/CNPJ返回客户数据,前端用JavaScript填充表单字段
示例后端接口:
from django.http import JsonResponse def get_cliente_by_cpfcnpj(request): cpfcnpj = request.GET.get('cpfcnpj') try: cliente = Clientes.objects.get(cliente_cpfcnpj=cpfcnpj) data = { 'cliente_razaosocial': cliente.cliente_razaosocial, 'cliente_email': cliente.cliente_email, 'cliente_telefone_celular': cliente.cliente_telefone_celular, # 其他需要填充的字段 } return JsonResponse(data) except Clientes.DoesNotExist: return JsonResponse({}, status=404)
内容的提问来源于stack exchange,提问作者Elinton Soares
相关产品推荐
相关产品推荐

