Django创建BillInvoice与关联BillLineItem时主键缺失错误排查
问题:创建BillInvoice及其关联BillLineItem时出现主键错误
上下文
模型代码
UserProfile
class UserProfile(models.Model): ROLE = [ ('Select Role', 'Select Role'), ('Admin', 'Admin'), ('CBUMT', 'CBUMT'), ('Pharmacist', 'Pharmacist'), ('Doctor', 'Doctor'), ('Nurse', 'Nurse'), ('Referral Provider', 'Referral Provider'), ('Member', 'Memeber'), ('Dependent', 'Dependent'), ] GENDER = [ ('None', 'None'), ('Male', 'Male'), ('Female', 'Female'), ] user = models.OneToOneField(User, on_delete=models.CASCADE) id_number = models.CharField(max_length=30, blank=True, null=True) is_medical_staff = models.BooleanField(default=False) role = models.CharField(max_length=50, choices=ROLE, default='CBUMT') gender = models.CharField(max_length=20, choices=GENDER, default='None') date_of_birth = models.DateField(blank= False, null=False, default=timezone.now) phone = models.CharField(max_length=20, blank=True, null=True) qualification = models.CharField(max_length=100, blank=True, null=True) bio = models.TextField(blank=True, null=True) profile_pic = models.ImageField(null=True, blank=True, upload_to='images/', default='img/placeholder.jpg') @property def age(self): today = date.today() age = today.year - self.date_of_birth.year - ((today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day)) return age def __str__(self): return str(self.user) + ' | ' + self.role
BillInvoice
class BillInvoice(models.Model): TYPE_CHOICES = [ ('Medicine', 'Medicine'), ('Auxillary', 'Auxillary'), ('Mixed', 'Mixed'), ] patient = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='invoices', null=True) invoice_type = models.CharField(max_length=50, choices=TYPE_CHOICES, default='Medicine') reference = models.ForeignKey(Referral, on_delete=models.CASCADE, related_name='invoice_references', null=True) amount = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True) date = models.DateField(auto_now_add=True) due_date = models.DateField(null=True, blank=True) status = models.BooleanField(default=False) prepared_by = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='prepared_invoices', null=True, blank=True) def __str__(self): return f'Invoice for {self.patient} on {self.date}' def update_amount(self): total_amount = sum(item.amount for item in self.lineitems.all()) self.amount = total_amount def get_status(self): return self.status def save(self, *args, **kwargs): if not self.id and not self.due_date: self.due_date = datetime.now() + timedelta(days=30) self.update_amount() # Update the amount before saving super().save(*args, **kwargs)
BillLineItem
class BillLineItem(models.Model): patient = models.ForeignKey(BillInvoice, on_delete=models.CASCADE, related_name='lineitems') service = models.TextField() description = models.TextField() quantity = models.IntegerField() rate = models.DecimalField(max_digits=9, decimal_places=2) amount = models.DecimalField(max_digits=9, decimal_places=2) def __str__(self): return str(self.patient)
视图代码(初始版本)
class CreateInvoiceView(FormView): template_name = 'referral/invoice_create.html' form_class = BillInvoiceForm success_url = reverse_lazy('ereferral:invoice-list') # Adjust as needed def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if self.request.POST: context['formset'] = LineItemFormset(self.request.POST) else: context['formset'] = LineItemFormset() context['title'] = "Invoice Generator" context['heading_message'] = 'Formset Demo' return context def form_valid(self, form): context = self.get_context_data() formset = context['formset'] if formset.is_valid(): # Retrieve the UserProfile instance patient_profile = form.cleaned_data.get("patient") # Assuming this returns a UserProfile instance # Save the BillInvoice instance to get the primary key invoice = BillInvoice( patient=patient_profile, # Assuming 'patient' is a ForeignKey to UserProfile invoice_type=form.cleaned_data["invoice_type"], reference=form.cleaned_data["reference"], amount=0 # Initial total_amount, will update later ) invoice.save() # Now the invoice instance has a primary key total = 0 for item_form in formset: if item_form.cleaned_data: # Ensure the form is not empty service = item_form.cleaned_data.get('service') description = item_form.cleaned_data.get('description') quantity = item_form.cleaned_data.get('quantity') rate = item_form.cleaned_data.get('rate') if service and description and quantity and rate: amount = float(rate) * float(quantity) total += amount BillLineItem.objects.create( invoice=invoice, # Associating the line item with the invoice service=service, description=description, quantity=quantity, rate=rate, amount=amount ) # Update the total amount of the invoice after creating all line items invoice.total_amount = total invoice.save() try: generate_PDF(self.request, id=invoice.id) except Exception as e: logger.error(f"PDF generation error: {e}") return super().form_valid(form) else: return self.form_invalid(form) def form_invalid(self, form): context = self.get_context_data() formset = context['formset'] logger.error("Form or formset is invalid") logger.error("Form errors: %s", form.errors) logger.error("Formset errors: %s", formset.errors) return self.render_to_response(self.get_context_data(form=form)) # Define the formset outside the view class to avoid redefinition LineItemFormset = formset_factory(LineItemForm, extra=1)
错误详情
保存BillLineItem实例时触发错误:
'BillInvoice' instance needs to have a primary key value before this relationship can be used,提示BillInvoice实例在关联明细项前未分配主键。
已尝试操作
调整后的视图代码:
class CreateInvoiceView(FormView): template_name = 'referral/invoice_create.html' form_class = BillInvoiceForm success_url = reverse_lazy('ereferral:invoice-list') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if self.request.POST: context['formset'] = LineItemFormset(self.request.POST) else: context['formset'] = LineItemFormset() context['title'] = "Invoice Generator" context['heading_message'] = 'Formset Demo' return context def form_valid(self, form): context = self.get_context_data() formset = context['formset'] if formset.is_valid(): try: # Save the BillInvoice instance invoice = form.save(commit=False) patient_profile = form.cleaned_data.get("patient") invoice.patient = patient_profile invoice.total_amount = 0 # Initialize total amount invoice.save() # Log the invoice primary key to ensure it is saved logger.debug(f"Invoice saved with ID: {invoice.id}") total = 0 for item_form in formset: if item_form.cleaned_data: service = item_form.cleaned_data.get('service') description = item_form.cleaned_data.get('description') quantity = item_form.cleaned_data.get('quantity') rate = item_form.cleaned_data.get('rate') if service and description and quantity and rate: amount = float(rate) * float(quantity) total += amount BillLineItem.objects.create( invoice=invoice, service=service, description=description, quantity=quantity, rate=rate, amount=amount ) # Update the total amount of the invoice after creating all line items invoice.total_amount = total invoice.save() try: generate_PDF(self.request, id=invoice.id) except Exception as e: logger.error(f"PDF generation error: {e}") return super().form_valid(form) except Exception as e: logger.error(f"Error saving invoice or line items: {e}") return self.form_invalid(form) else: logger.error("Formset is invalid") logger.error(formset.errors) return self.form_invalid(form) def form_invalid(self, form): context = self.get_context_data() formset = context['formset'] logger.error("Form or formset is invalid") logger.error("Form errors: %s", form.errors) logger.error("Formset errors: %s", formset.errors) return self.render_to_response(self.get_context_data(form=form))
需要排查的问题
- 处理BillInvoice实例保存的方式是否存在问题?
- 是否需要调整视图或模型定义,确保BillInvoice实例在关联BillLineItem前已保存并拥有主键?
内容的提问来源于stack exchange,提问作者Kennerdol
相关产品推荐
相关产品推荐

