Django Form - Как создать шаблон пользовательской формы с циклом для одного поля?
Я создаю веб-приложение, которое использует шаблонизатор Django. При этом одна из моих моделей называется Order
. Модель Order
, помимо других моделей, также использует модель Food
- это отношение ManyToMany
. Модель Food
использует модель Category
для определения категорий еды (которая должна содержать такие категории еды, как пицца, бургеры, вино, пиво и т.д.) - это тоже отношение ManyToMany
.
Сейчас я пытаюсь создать форму, в которой пользователю будет показана форма с разделенными категориями. На данный момент все продукты из модели Food
просто показываются один за другим, один под другим. Они не разделены на категории в шаблоне формы. В настоящее время они отображаются по умолчанию в виде простого списка продуктов, где пользователь может выбрать любой из них. Я хочу, чтобы пользователь мог выбирать любые блюда, но отображать их по категориям (все пиццы в категории "Пицца", все бургеры в категории "Бургеры" и т.д.).
Моя models.py
выглядит следующим образом:
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"
Мой create_order.html
(шаблон с формой, выглядит следующим образом):
<form method="post">
{% csrf_token %}
{{ form | crispy }}
<button type="submit" class="btn btn-success">Submit</button>
</form>
Я также использую crispy_forms
, если это имеет какое-то значение.
Я открыт для любых предложений о том, как сделать категоризацию продуктов в шаблоне формы.
Большое спасибо
для этого необходимо внести следующие изменения
- в
models.py
добавьтеrelated_name='foods'
к категории в модели Food следующим образом:
category = models.ManyToManyField(Category, related_name='foods')
мы будем использовать это related_name
позднее
- в
forms.py
добавьте поле категории в форму заказа, чтобы получилось что-то вроде этого:
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())
- теперь мы будем использовать AJAX и вам нужно импортировать jQuery, добавьте этот код в ваш js файл: .
$(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);
});
Этот код будет обновлять выбор блюд по выбранной категории, и чтобы это работало, нам нужно добавить некоторый код в наш 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()
Этот код получит id выбранной категории и вернет список продуктов, которые имеют эту категорию
- ок мы почти закончили, осталось добавить эту строку в
urls.py
path('foodbycat/', views.ajaxGetFoodByCategory, name='foodby-ctg'),
Необходимо проверить, что добавленный путь к urls.py
совпадает с url в js-коде