“‘BillInvoice’实例需要有主键值才能使用此关系”错误

问题描述 投票:0回答:1

我的 Django 项目遇到问题,我正在尝试创建

BillInvoice
及其关联的
BillLineItem
实例。我遇到的错误是:

"'BillInvoice' instance needs to have a primary key value before this relationship can be used"

背景:

我有一个表单视图(

CreateInvoiceView
),它使用表单集处理发票及其行项目的创建。我的代码的相关部分如下:

这是我的模型.py

用户资料

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)

账单行项目

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)

M 浏览量.py

创建发票视图

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
实例之前,
BillLineItem
实例似乎没有分配主键。

要求:

有人可以帮我确定可能出了什么问题吗?具体来说:

  1. 我在处理

    BillInvoice
    实例的保存时是否存在问题?

  2. 我的视图或模型定义中是否需要进行任何调整,以确保在关联

    BillInvoice
    实例之前使用主键保存
    BillLineItem
    实例?

我尝试过的:

立即保存发票:我确保在创建

BillInvoice
实例后立即保存它,因此在尝试创建任何
BillLineItem
实例之前它会获得分配的主键。这应该确保
invoice
在被订单项引用时具有 ID。

创建发票视图

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))
javascript python html css django
1个回答
0
投票

我面临同样的问题,我能够实现的解决方案是在启动创建视图表单时保存实例 我使用的是 Wagtail,所以它与 Django 有点不同,但我认为你可以在启动表单时做同样的事情。 这是代码示例:

class CreateInvoiceView(FormView):

    def __init__(self, *args: list, **kwargs: dict) -> None:
        if not kwargs["instance"].pk:
            invoice = kwargs["instance"]
            invoice.save()
        super().__init__(*args, **kwargs)
© www.soinside.com 2019 - 2024. All rights reserved.