Reputation: 432
I'm creating a web app which uses Django's templating engine. Having said that, one of my models is called Order
. The Order
model, aside from the other models, also uses a model Food
- this is a ManyToMany
relationship. Food
model uses model Category
to define food categories (which should contain food categories like Pizza, Burgers, Wine, Beers, etc.) - this is a ManyToMany
relationship, as well.
Now, I'm trying to create a form where the user will be shown a form with separated categories. As of now, all of the foods from the Food
model are just shown one by one, one below another one. They are not categorized in the form template. Currently, they are shown by default, as a simple list of foods where user can choose any of them. I want the user to choose any foods they want, but display them categorically (all of the pizzas under the Pizza category, all of the burgers under the Burgers category, etc.).
My models.py
looks like this:
class Category(models.Model):
"""
Model for food categories
"""
name = models.CharField(max_length=100)
description = models.TextField(max_length=300)
date_created = models.DateTimeField(auto_now=True)
def __str__(self) -> str:
return f"{self.name} / {self.description[0 : 50]}..."
class Food(models.Model):
"""
Model for foods
"""
name = models.CharField(max_length=150)
description = models.TextField(max_length=400)
category = models.ManyToManyField(Category)
price = models.FloatField()
date_created = models.DateTimeField(auto_now=True)
def __str__(self) -> str:
return f"{self.name} / {', '.join(self.category.values_list('name', flat=True))} / {self.price} EUR"
class Order(models.Model):
"""
Model for orders
"""
order_statuses = [
("PR", "Status 1"),
("PS", "Status 2"),
]
customer_name = models.CharField(max_length=255)
customer_email = models.EmailField(blank=True)
ordered_foods = models.ManyToManyField(Food)
table = ForeignKey(Table, on_delete=models.CASCADE)
status = models.CharField(choices=order_statuses,
default="PR",
max_length=2)
total_price = models.FloatField(editable=False, default=0)
date_created = models.DateTimeField(auto_now=True)
date_completed = models.DateTimeField(blank=True,
default=datetime.datetime.strptime('2099-12-31 00:00:00', '%Y-%m-%d %H:%M:%S'))
def __str__(self) -> str:
return f"{self.id} / {self.customer_name} / {self.table.name} {self.table.number} / STATUS: {self.get_status_display()} / {self.total_price} EUR"
My create_order.html
(the template with form, looks like this):
<form method="post">
{% csrf_token %}
{{ form | crispy }}
<button type="submit" class="btn btn-success">Submit</button>
</form>
I'm also using crispy_forms
if it makes any difference.
I'm open to any suggestions on how to make the categorization of the foods in the form template.
Thank you very much
Upvotes: 2
Views: 228
Reputation: 2639
to do this you need to make these changes
models.py
add related_name='foods'
to category in Food model like this: category = models.ManyToManyField(Category, related_name='foods')
we will use that related_name
later
forms.py
add category field to your order form so you will have something like this:class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = [
'customer_name',
'customer_email',
'category',
'ordered_foods',
'ordered_foods',
'status',
]
# add this line and don't forget to add category to the field or use '__all__'
category = forms.ModelChoiceField(queryset=Category.objects.all())
$(document).ready(function(){
var $category = $('#id_category');
function updateFoodChoices() {
var output = [];
output.push(`<option value="" disabled selected>Please select category</option>`)
$('#id_ordered_foods').html(output.join(''));
var selected = $category.val();
if (selected) {
$.ajax({
type: 'GET',
url: `/foodbycat/?cat_id=${selected}`,
success: function(response) {
const foods = response.data
$('#id_ordered_foods').empty()
var output = [];
foods.forEach(food => {
output.push(`<option value="${food.id}">${food.name} / ${food.price} EUR</option>`);
});
$('#id_ordered_foods').html(output.join(''));
},
error: function(error) {
console.log('error', error)
}
});
}
}
updateFoodChoices();
$category.change(updateFoodChoices);
});
This code is going to update the food choices by selected category and to let this work we need to add some code to our views.py
views.py
:def ajaxGetFoodByCategory(request):
cat_id = request.GET.get('cat_id')
if cat_id is not None:
category = get_object_or_404(Category, id=cat_id)
foods = category.foods.all()
# data = serializers.serialize('json', category.foods.all())
data = []
for food in foods:
item = {
'id': food.id,
'name': food.name,
'price': str(food.price),
}
data.append(item)
print(data)
return JsonResponse({'data': data})
else:
return HttpResponseBadRequest()
This code is going to get the id of selected category and return a list of food that have that category
urls.py
path('foodbycat/', views.ajaxGetFoodByCategory, name='foodby-ctg'),
You need to check that the added path to urls.py
matches with the url in the js code
Upvotes: 1