Cannot assign "'": "" must be a "" instance. ModelChoiceField

54 Views Asked by At

I have a form where a user can select the title of an object and then save it but when I do hit save, I get an error. I am very certain that this problem is because I am using ModelChoiceField which returns ids of objects instead of the instances of that object.

So, I have two models: InvoiceModel

class Invoice(models.Model):
  line_one = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+', verbose_name="Line 1", blank=True, null=True, default='')
  line_one_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_one_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_one_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)

Invoice.line_one is referenced to Inventory.product_number.


InventoryModel:

class Inventory(models.Model):
  product_number = models.IntegerField(primary_key=True)
  product = models.TextField(max_length=3000, default='', blank=True, null=True)
  title = models.CharField('Title', max_length=120, default='', blank=True, null=True, unique=True)
  amount = models.IntegerField('Unit Price', default=0, blank=True, null=True)

  def __str__(self):
    return self.title

InvoiceForm:

class InvoiceForm(forms.ModelForm):
  line_one = forms.ModelChoiceField(queryset=Inventory.objects.values_list('title', flat=True), label="Line 1")
  line_one_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  class Meta:
    model = Invoice
    fields = ['line_one',#...]

  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.fields['line_one_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]

I actually have 10 lines, [i.e., line_two, etc..] but I am just pasting the code for line_one here for simplicity.

I also want only the amount of the object selected in line_one to be displayed in line_one_unit_price. Please let me know if you know how to achieve that.

views.py:

@login_required
def add_invoice(request):
  form = InvoiceForm(request.POST or None)
  total_invoices = Invoice.objects.count()
  queryset = Invoice.objects.order_by('-invoice_date')[:6]

  if form.is_valid():
    form.save()
    messages.success(request, 'Successfully Saved')
    return redirect('/invoice/list_invoice')
  context = {
    "form": form,
    "title": "New Invoice",
    "total_invoices": total_invoices,
    "queryset": queryset,
}
return render(request, "entry.html", context)

The error that I get:

enter image description here

Thank you!

Edit: I edited the forms.py and removed the line of code that was overriding line_one But now I get an Integrity Error.

**InvoiceForm:**

class InvoiceForm(forms.ModelForm):
  line_one_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  class Meta:
    model = Invoice
    fields = ['line_one',#...]

  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.fields['line_one_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]

Error: IntegrityError at /invoice/add_invoice/ NOT NULL constraint failed: invoicecemgmt_invoice.line_three_id Request Method: POST Request URL: http://127.0.0.1:8000/invoice/add_invoice/ Django Version: 4.0 Exception Type: IntegrityError Exception Value:
NOT NULL constraint failed: invoicecemgmt_invoice.line_three_id

Edit: The full models.py code:

class Invoice(models.Model):
  comments = models.TextField(max_length=3000, default='', blank=True, null=True)
  invoice_number = models.IntegerField(blank=True, null=True)
  invoice_date = models.DateField(auto_now_add=False, auto_now=False, blank=True, null=True)
  name = models.CharField('Customer Name', max_length=120, default='', blank=True, null=True)

  line_one = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+', verbose_name="Line 1", blank=True, default='')
  line_one_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_one_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_one_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  line_two = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+',  verbose_name="Line 2", blank=True, default='')
  line_two_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_two_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_two_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  line_three = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+',  verbose_name="Line 3", blank=True, default='')
  line_three_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_three_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_three_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  line_four = models.ForeignKey(Inventory, on_delete=models.CASCADE,related_name='+',  verbose_name="Line 4", blank=True, default='')
  line_four_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_four_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_four_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  line_five = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+',  verbose_name="Line 5", blank=True, default='')
  line_five_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_five_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_five_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  line_six = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+',  verbose_name="Line 6", blank=True, default='')
  line_six_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_six_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_six_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  line_seven = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+',  verbose_name="Line 7", blank=True, default='')
  line_seven_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_seven_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_seven_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  line_eight = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+',  verbose_name="Line 8", blank=True, default='')
  line_eight_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_eight_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_eight_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  line_nine = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+',  verbose_name="Line 9", blank=True, default='')
  line_nine_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_nine_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_nine_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  line_ten = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='+',  verbose_name="Line 10", blank=True, default='')
  line_ten_quantity = models.IntegerField('Quantity', default=0, blank=True, null=True)
  line_ten_unit_price = models.IntegerField('Unit Price(₹)', default=0, blank=True, null=True)
  line_ten_total_price = models.IntegerField('Line Total(₹)', default=0, blank=True, null=True)
  
  phone_number = models.CharField(max_length=120, default='', blank=True, null=True)
  total = models.IntegerField(default='0', blank=True, null=True)
  balance = models.IntegerField(default='0', blank=True, null=True)
  timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
  last_updated = models.DateTimeField(auto_now_add=False, auto_now=True, blank=True)
  paid = models.BooleanField(default=False)
  invoice_type_choice = (
  ('Invoice', 'Invoice'),
  ('Receipt', 'Receipt'),
  )
  invoice_type = models.CharField(max_length=50, blank=True, null=True, choices=invoice_type_choice)
def __unicode__(self):
    return self.invoice_number

forms.py:

class InvoiceForm(forms.ModelForm):
                       
  line_one_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  line_two_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  line_three_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  line_four_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  line_five_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  line_six_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  line_seven_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  line_eight_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  line_nine_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")
  line_ten_unit_price = forms.CharField(widget=forms.Select, label="Unit Price(₹)")

  class Meta:
      model = Invoice
      fields = ['name', 'phone_number', 'invoice_date', 'invoice_number',
            'line_one', 'line_one_quantity', 'line_one_unit_price', 'line_one_total_price',
            'line_two', 'line_two_quantity', 'line_two_unit_price', 'line_two_total_price',
            'line_three', 'line_three_quantity', 'line_three_unit_price', 'line_three_total_price',
            'line_four', 'line_four_quantity', 'line_four_unit_price', 'line_four_total_price',
            'line_five', 'line_five_quantity', 'line_five_unit_price', 'line_five_total_price',
                            'line_six', 'line_six_quantity', 'line_six_unit_price', 'line_six_total_price',
            'line_seven', 'line_seven_quantity', 'line_seven_unit_price', 'line_seven_total_price',
            'line_eight', 'line_eight_quantity', 'line_eight_unit_price', 'line_eight_total_price',
            'line_nine', 'line_nine_quantity', 'line_nine_unit_price', 'line_nine_total_price',
            'line_ten', 'line_ten_quantity', 'line_ten_unit_price', 'line_ten_total_price',
            'total', 'paid', 'invoice_type'
            ]

      widgets = {
          'line_one_quantity': TextInput(),
          'line_two_quantity': TextInput(),
          'line_three_quantity': TextInput(),
          'line_four_quantity': TextInput(),
          'line_five_quantity': TextInput(),
          'line_six_quantity': TextInput(),
          'line_seven_quantity': TextInput(),
          'line_eight_quantity': TextInput(),
          'line_nine_quantity': TextInput(),
          'line_ten_quantity': TextInput(),
    }

def clean_invoice_number(self):
    invoice_number = self.cleaned_data.get('invoice_number')
    if not invoice_number:
        raise forms.ValidationError('This field is required')
    return invoice_number


def clean_name(self):
    name = self.cleaned_data.get('name')
    if not name:
        raise forms.ValidationError('This field is required')
    return name

def clean_line_one(self):
    line_one = self.cleaned_data.get('line_one')
    if not line_one:
        raise forms.ValidationError('This field is required')
    return line_one

def clean_line_one_quantity(self):
    line_one_quantity = self.cleaned_data.get('line_one_quantity')
    if not line_one_quantity:
        raise forms.ValidationError('This field is required')
    return line_one_quantity

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.fields['line_one_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]
    self.fields['line_two_unit_price'].widget.choices= [(i.amount, i.amount) for i in Inventory.objects.all()]
    self.fields['line_three_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]
    self.fields['line_four_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]
    self.fields['line_five_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]
    self.fields['line_six_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]
    self.fields['line_seven_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]
    self.fields['line_eight_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]
    self.fields['line_nine_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]
    self.fields['line_ten_unit_price'].widget.choices = [(i.amount, i.amount) for i in Inventory.objects.all()]
1

There are 1 best solutions below

1
Rohit Rahman On

In the queryset attribute inside the ModelChoiceField, you should send pk along with the attribute you want to display. For example:

forms.ModelChoiceField(queryset=Inventory.objects.values('pk','title'), label="Line 1")