Thanks in advance for your help. I am building my first Django project, a ticketing application that will have over 100 items to choose from.
I'm trying to display a list of Items(foreign key objects) for the user to choose from when creating a ticket in an MDBootstrap autocomplete field. The items have both a name and ID and I want both to appear in the autocomplete options when the user is searching.
My form works when I display only the 'item_id' from the Item model in the autocomplete data (found in the script tags at the bottom of the template). But when I display the string with both 'item_name" and 'item_id'(as you see below), my form won't submit and I get the error "Select a valid choice. That choice is not one of the available choices."
How can I display both 'item_name' and 'item_id' from the Item model in the autocomplete options but then have my form submit properly recognizing the 'item_id' for its 'item' foreign key field?
Edited to add Example: Item model has object with item_name 'Large Tractor 7', item_id 'LT7'. I want the autocomplete to populate with 'Large Tractor 7 | ID: LT7'. I can make my form work if I only display 'LT7' because the Ticket model is looking for the item foreign key(i.e. LT7), but if I create the string with more information, the form produces the "Select a valid choice" error and I can't create the ticket.
models (each of these models is in a different app within the django project)
class Item(models.Model):
item_name = models.CharField(max_length=200)
item_id = models.CharField(max_length=200, primary_key=True)
class Ticket(models.Model):
ticket_id = models.AutoField(primary_key=True, null=False, blank=False)
item = models.ForeignKey(Item, on_delete=models.PROTECT, null=False, blank=False, related_name="tickets")
view
def TicketSubmit(request):
items = Item.objects.all()
if request.method == "POST":
item = request.POST.get("item")
form = TicketSubmitForm(request.POST)
photoform = TicketImageForm(request.POST or None)
files = request.FILES.getlist("ticket_photo")
if form.is_valid():
f = form.save(commit=False)
f.item = item
for i in files:
TicketPhoto.objects.create(ticket=f, ticket_photo=i)
messages.success(request, "Success! New Ticket Created")
return HttpResponseRedirect(reverse_lazy("home"))
else:
print(form.errors)
else:
form = TicketSubmitForm()
photoform = TicketImageForm()
context = {"form": form, "photoform": photoform, "items": list(items)}
return render(request, "home.html", context)
form
class TicketSubmitForm(forms.ModelForm):
class Meta:
model = Ticket
fields = ["item", "op_name", "ticket_text"]
widgets = {
"created_at": forms.HiddenInput,
"completed": forms.HiddenInput,
"item": forms.TextInput(
attrs={
"class": "form-control",
"id": "form11",
}
),
"op_name": forms.TextInput(
attrs={
"class": "form-control",
"id": "op_nameInput",
"placeholder": "Enter your name here",
}
),
"ticket_text": forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Write a few words about the issue here",
"rows": "4",
}
),
}
main.js
const basicAutocomplete = document.querySelector('#basic');
const dataFilter = (value) => {
return data.filter((item) => {
return item.toLowerCase().startsWith(value.toLowerCase());
});
};
new mdb.Autocomplete(basicAutocomplete, {
filter: dataFilter
});
template
<form id="ticket_form" role="form" action="" method="post" enctype="multipart/form-data" class="">{% csrf_token %}
<div id="basic" class="form-outline mx-5 mb-2">
{{form.item}}
<label class="form-label" for="form1">Find Item</label>
</div>
#other form fields and submit button
</form>
<script>
const data = [{% for i in items %}'{{ i.item_name }} | ID: {{ i.item_id }}', {% endfor %}];
</script>
I was able to fix this in the following way.
if request.method == "POST":
updated_request = request.POST.copy()
item = request.POST.get("item")
item = item.split("ID:")[1].strip()
updated_request.update({"item": item})
form = TicketSubmitForm(updated_request)
photoform = TicketImageForm(request.POST or None)
files = request.FILES.getlist("ticket_photo")
if form.is_valid():
f = form.save()
for i in files:
TicketPhoto.objects.create(ticket=f, ticket_photo=i)
messages.success(request, "Success! New Ticket Created")
return HttpResponseRedirect(reverse_lazy("home"))
else:
print(form.errors)
else:
form = TicketSubmitForm()
photoform = TicketImageForm()
context = {"form": form, "photoform": photoform, "items": list(items)}
return render(request, "home.html", context)
Thanks @AlexVergara for getting me pointed in the right direction.