Динамическое отображение вариантов выбора Django

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

Я пытаюсь создать приложение для расчета стоимости рецепта. У меня есть 3 модели, а именно Recipe, RecipeIngredient и Ingredient

Пользователь будет добавлять ингредиенты в базу данных, а затем создавать рецепты с использованием этих ингредиентов с помощью модели RecipeIngredient.

При создании Ingredient пользователь выбирает единицу измерения, например (граммы). Теперь, когда пользователь переходит к созданию Recipe (см. скриншот ниже), я хочу отобразить только те единицы, которые относятся к данному ингредиенту, а не все, например, пользователь добавил говядину, и единицей измерения были граммы. Теперь, когда пользователь добавляет RecipeIngredient к Recipe, я хочу, чтобы он мог выбрать только категорию "Вес" в примере (см. скриншот ниже). Причина в том, что граммы не могут быть преобразованы в миллилитры, поэтому выбор не должен быть возможным.

Если что-то непонятно или вам нужна дополнительная информация, просто дайте мне знать.

Модели

Модели рецептов

from django.db import models
from django.urls import reverse
from django.contrib.auth import get_user_model

from ingredients.models import Ingredient

class Recipe(models.Model):
    """Store recipe information and costs"""

    class Meta:
        """Meta definition for Recipe."""

        verbose_name = "Recipe"
        verbose_name_plural = "Recipes"
        ordering = ("name",)

    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    name = models.CharField(max_length=155)
    yield_count = models.PositiveIntegerField()
    yield_units = models.CharField(max_length=155, default="servings")

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse("recipe:detail", kwargs={"id": self.pk})

    def get_update_url(self):
        return reverse("recipe:update", kwargs={"id": self.pk})


class RecipeIngredient(models.Model):
    """Holds more information about how much of a certain ingredient is used inside a recipe"""

    class Meta:
        """Meta definition for RecipeIngredient"""

        verbose_name = "RecipeIngredient"
        verbose_name_plural = "RecipeIngredients"

    # NOTE: User should first choose the ingredient
    recipe = models.ForeignKey(Recipe, related_name="ingredients", on_delete=models.CASCADE)
    ingredient = models.ForeignKey(Ingredient, related_name="ingredient", on_delete=models.CASCADE)

    # NOTE: Then the amount an unit should be asked based on that
    amount = models.DecimalField(max_digits=20, decimal_places=2)
    unit = models.CharField(max_length=10, choices=Ingredient.UNIT_CHOICES, default=None)

    def __str__(self):
        """Unicode representation of RecipeIngredient"""
        return self.ingredient.__str__()

Модель ингредиента

from django.db import models
from django.urls import reverse
from django.contrib.auth import get_user_model

from pint import UnitRegistry

from .choices import ALL_UNIT_CHOICES


class Ingredient(models.Model):
    """Stores data about an ingredient to be used by a Recipe model."""

    class Meta:
        """Meta definition for Ingredient."""

        verbose_name = "Ingredient"
        verbose_name_plural = "Ingredients"
        ordering = ("name",)

    # Unit choices that will display in a dropdown
    # It will be ordered by group (eg. Weight, Volume)
    UNIT_CHOICES = ALL_UNIT_CHOICES

    ureg = UnitRegistry()

    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    name = models.CharField(max_length=50, unique=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    size = models.DecimalField(max_digits=10, decimal_places=2)
    unit = models.CharField(max_length=10, choices=UNIT_CHOICES, default=None)

    def __str__(self):
        """Unicode representation of Ingredient."""
        return self.name

    def get_absolute_url(self):
        """Return absolute url for Ingredient"""
        return reverse("ingredient:detail", kwargs={"id": self.pk})

    def get_update_url(self):
        """Return the url for update page"""
        return reverse("ingredient:update", kwargs={"id": self.pk})

    def get_delete_url(self):
        return reverse("ingredient:delete", kwargs={"id": self.pk})

    def save(self, *args, **kwargs):
        self.unit = self.ureg.Unit(self.unit)

        super(Ingredient, self).save(*args, **kwargs)

Виды

def create_recipe(request):
    template_name = "recipes/create.html"

    form = CreateRecipeForm(request.POST or None)
    ingredient_choice_form = IngredientChoiceForm(request.POST or None, request=request)
    Formset = modelformset_factory(RecipeIngredient, form=CreateRecipeIngredientForm, extra=0)
    formset = Formset(request.POST or None, queryset=Recipe.objects.none())

    context = {
        "form": form,
        "ingredient_choice_form": ingredient_choice_form,
        "formset": formset,
    }

    print(request.POST)

    return render(request, template_name, context)

# This is the view being calle by HTMX everytime an ingredient is selected
def get_recipe_ingredient_details(request):
    template_name = "recipes/hx_snippets/add_recipe_ingredient_details.html"

    ingredient_id = request.GET.get("ingredients")

    if not ingredient_id:
        return HttpResponse("")

    ingredient = Ingredient.objects.get(id=ingredient_id)
    choices = get_unit_group(ingredient.unit)

    Formset = modelformset_factory(RecipeIngredient, form=CreateRecipeIngredientForm, extra=1)
    formset = Formset(
        request.POST or None,
        queryset=RecipeIngredient.objects.none(),
        initial=[
            {
                "ingredient": ingredient,
                "unit": choices,
            },
        ],
    )

    context = {
        "formset": formset,
        "ingredient": ingredient,
    }

Шаблоны

Шаблон, возвращенный запросом HTMX

<p>
    {% for form in formset %}
        {{ form }}
    {% endfor %}
</p>

главный шаблон создания

{% block content %}
    <h1>Create Recipe</h1>

    <form action="" method="post">
        {% csrf_token %}

        {{ form.as_p }}

        <h3>Add ingredients</h3>
        {{ formset.management_form }}
        <div id="ingredient-form"></div>
        {{ ingredient_choice_form.as_p }} <-- this is an HTMX get request that calls the get_recipe_ingredient_details view
        
        <p><button type="submit">Create</button></p>
    </form>
{% endblock content %}


{% block scripts %}
    <script src="{% static 'js/recipes/addFormsetField.js' %}"></script>
{% endblock scripts %}

Скриншот

enter image description here

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