Создание формы Django из иностранного ключа
Я пытаюсь создать простое приложение Django, которое позволяет пользователям создавать и редактировать рецепты, состоящие из ингредиентов.
models.py
class Recipe(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=1000)
class Ingredient(models.Model):
api_id = models.IntegerField(primary_key=True) # id in Spoonacular database
name = models.CharField(max_length=100) # human-readable name
units = models.CharField(max_length=10) # unit of measure (e.g. 'oz', 'lb'), includes '' for 'number of'
amt = models.PositiveIntegerField() # number of units
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
У меня есть детальное представление, которое отображает ингредиенты для данного рецепта, а также кнопки для добавления и удаления ингредиентов. Первичный ключ рецепта находится в URL.
просмотр страницы деталей с кнопками добавления/удаления ингредиента
Я пытаюсь сделать так, чтобы когда пользователь нажимает на кнопку "Удалить ингредиенты", она перенаправляла его на форму, где он может выбрать, какие ингредиенты из данного рецепта он хочет удалить. Это та часть, которую я не могу заставить работать. Насколько я могу судить, для этого требуется поле ModelChoiceField с набором запросов, где внешний ключ ингредиента совпадает с первичным ключом рецепта ('recipe_pk'). Похоже, что мне нужно передать первичный ключ данного рецепта в форму через **kwargs в конструкторе, а затем заставить форму выбрать соответствующие ингредиенты. Однако это дает мне AttributeError
с описанием 'tuple' object has no attribute 'get'
, когда я пытаюсь отобразить шаблон. Вот снимок экрана:
AttributeError
Является ли это правильным способом создания такой формы, и если да, то есть ли идеи, где это происходит неправильно?
forms.py
# Trying to create a form where a user can select any number of Ingredients
# associated with the Model
class RemoveIngredientForm(forms.Form):
to_remove = forms.ModelChoiceField(queryset=Ingredient.objects.all())
def __init__(self, *args, **kwargs):
super(RemoveIngredientForm, self).__init__(args, kwargs)
recipe = Recipe.objects.get(pk=kwargs['recipe_pk'])
self.fields['to_remove'].queryset = Ingredient.objects.filter(recipe=recipe)
views.py
...
def remove_ingredients(request, **kwargs):
"""
GET: list of existing ingredients
POST: remove selected ingreident from model
"""
if request.method == 'POST':
form = RemoveIngredientForm(request.POST, kwargs=kwargs)
if form.is_valid():
picked = form.cleaned_data.get('picked')
return HttpResponse('How did I get here?') # Haven't gotten to this point yet
recipe = Recipe.objects.get(pk=kwargs['recipe_pk']) #'recipe_pk' is the primary key of the Recipe and lives in the URL
try:
ingredients = Ingredient.objects.filter(recipe=recipe)
except Ingredient.DoesNotExist:
ingredients = None
form = RemoveIngredientForm(**kwargs)
context = {
'recipe': recipe,
'form': form
}
return render(request, 'food/remove_ingredients.html', context=context)
<!-- food/remove_ingredients.html -->
<h1>Removing ingredients from {{recipe.name}}</h1>
<form method='POST'>
{{ form.as_p }}
<input type='submit' value='submit'>
</form>
Поле выбора не будет работать, потому что ему нужен словарь вариантов. Вы хотите отобразить список ингредиентов для определенного рецепта с кнопкой удаления рядом с каждым ингредиентом, которая вызывает просмотр удаления этого ингредиента. Поэтому вы вызываете функцию deleteingredients url с pk рецепта. Затем в своей функции вы передаете в контекст queryset ингредиентов для этого рецепта pk. Затем в своем шаблоне отобразите каждый ингредиент с кнопкой, которая вызывает delete для этого конкретного ингредиента с пк этого ингредиента. Таким образом, форма не нужна вообще. Вам просто нужно другое представление, которое удаляет ингредиент, а затем перенаправляет на рецепт.