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, если это имеет какое-то значение.

Я открыт для любых предложений о том, как сделать категоризацию продуктов в шаблоне формы.

Большое спасибо

для этого необходимо внести следующие изменения

  1. в models.py добавьте related_name='foods' к категории в модели Food следующим образом:
    category = models.ManyToManyField(Category, related_name='foods')

мы будем использовать это related_name позднее

  1. в 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())
  1. теперь мы будем использовать AJAX и вам нужно импортировать jQuery, добавьте этот код в ваш js файл:
  2. .
$(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

  1. добавьте эту функцию в 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 выбранной категории и вернет список продуктов, которые имеют эту категорию

  1. ок мы почти закончили, осталось добавить эту строку в urls.py
path('foodbycat/', views.ajaxGetFoodByCategory, name='foodby-ctg'),

Необходимо проверить, что добавленный путь к urls.py совпадает с url в js-коде

Вернуться на верх