Django ModelForm 可以在一次提交中保存多个新模型吗?
Can a Django ModelForm save multiple new models on a single submit?
我正在学习 Django 并建立一个网站来保存和显示自助餐厅的食品。我有一个 Model Meal 和另一个 FoodItem,其中多个 FoodItem 是 Meal 的子项。
class Meal(models.Model):
date = models.DateField(default=datetime.date.today)
BFAST = 0
LUNCH = 1
DIN = 2
MEAL_TIME_CHOICES = [
(BFAST, 'Breakfast'),
(LUNCH, 'Lunch'),
(DIN, 'Dinner'),
]
meal_time = models.IntegerField(
choices = MEAL_TIME_CHOICES,
default = MEAL_TIME_CHOICES[0]
)
class FoodItem(models.Model):
meal = models.ForeignKey(Meal, on_delete=models.CASCADE)
name = models.CharField(max_length=80)
我想创建一个 ModelForm,它具有 FoodItems 的多个文本输入,以便在一次提交表单时可以保存多种食物并将其与同一餐关联。
我现在的尝试只能一次提交一个食品。
class FoodItemForm(forms.ModelForm):
class Meta:
model = FoodItem
fields = ('name',)
如能以正确的方式执行此操作,我们将不胜感激!
创建名为 menu
:
的应用
|menu
├───migrations
| ...
├───templates
│ detail.html
│ list.html
│ __init__.py
│ admin.py
│ apps.py
│ forms.py
│ models.py
│ tests.py
│ urls.py
│ views.py
|project <--- the name of your "project"
│ asgi.py
│ settings.py
│ urls.py
│ wsgi.py
│ __init__.py
│templates <--- not used
│db.sqlite3
│manage.py
将 menu
应用程序添加到已安装的应用程序:
project/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'menu.apps.MenuConfig',
]
project/urls.py(项目级)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('menu.urls')),
]
menu/urls.py
from django.urls import path
from .views import list_view, detail_view
app_name = 'menu'
urlpatterns = [
path('', list_view, name='list'),
path('<pk>/', detail_view, name='detail'),
]
menu/models.py
import datetime
from django.db import models
class Meal(models.Model):
BFAST = 0
LUNCH = 1
DIN = 2
MEAL_TIME_CHOICES = [
(BFAST, 'Breakfast'),
(LUNCH, 'Lunch'),
(DIN, 'Dinner'),
]
meal_name = models.CharField(max_length=100)
date = models.DateField(default=datetime.date.today)
meal_time = models.IntegerField(choices=MEAL_TIME_CHOICES,default=0)
def __str__(self):
return self.meal_name
class FoodItem(models.Model):
meal = models.ForeignKey(Meal, on_delete=models.CASCADE)
food_item = models.CharField(max_length=80)
def __str__(self):
return self.food_item
确保 makemigrations
和 migrate
.
menu/forms.py
from django import forms
from django.forms.models import modelformset_factory
from .models import Meal, FoodItem
MealFormset = modelformset_factory(FoodItem, fields=('food_item',),
extra=5, can_delete=True)
menu/admin.py
from django.contrib import admin
from .models import Meal, FoodItem
class MealAdmin(admin.ModelAdmin):
pass
class FoodAdmin(admin.ModelAdmin):
pass
admin.site.register(Meal, MealAdmin)
admin.site.register(FoodItem, MealAdmin)
menu/views.py
from django.shortcuts import render, redirect
from .forms import MealFormset
from .models import Meal
def list_view(request):
context = {
'objects': Meal.objects.all(),
}
return render(request, 'list.html', context=context)
def detail_view(request, pk):
instance = Meal.objects.get(pk=pk)
formset = MealFormset(request.POST or None)
context = {
'object': instance,
'formset': formset,
}
if request.method == 'POST':
if formset.is_valid():
food_items = formset.save(commit=False)
if formset.deleted_forms:
for obj in formset.deleted_forms:
obj.instance.delete()
for food_item in food_items:
food_item.meal = instance
food_item.save()
return redirect('menu:list')
return render(request, 'detail.html', context=context)
menu/templates/list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>List</title>
</head>
<body>
<div style="margin: auto; width: 50%; border: 1px solid black; padding: 20px;">
<a href="{% url 'admin:index' %}">Admin Panel</a>
<h1>Meals</h1>
{% for meal in objects %}
<div>
<a href="{% url 'menu:detail' meal.pk %}">{{ meal }}</a>
</div>
{% endfor %}
</div>
</body>
</html>
menu/templates/detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Food Items</title>
</head>
<body>
<div style="margin: auto; width: 50%; border: 1px solid black; padding: 20px;">
<form method="POST">
{% csrf_token %}
{{ formset.management_form }}
{% for field in formset %}
<div style="padding: 5px 40px;">{{ field }}</div>
{% endfor %}
<input type="submit" value="Submit">
</form>
</div>
</body>
</html>
可能不完全您想要的,但说明了一些关键概念(我认为)。
我正在学习 Django 并建立一个网站来保存和显示自助餐厅的食品。我有一个 Model Meal 和另一个 FoodItem,其中多个 FoodItem 是 Meal 的子项。
class Meal(models.Model):
date = models.DateField(default=datetime.date.today)
BFAST = 0
LUNCH = 1
DIN = 2
MEAL_TIME_CHOICES = [
(BFAST, 'Breakfast'),
(LUNCH, 'Lunch'),
(DIN, 'Dinner'),
]
meal_time = models.IntegerField(
choices = MEAL_TIME_CHOICES,
default = MEAL_TIME_CHOICES[0]
)
class FoodItem(models.Model):
meal = models.ForeignKey(Meal, on_delete=models.CASCADE)
name = models.CharField(max_length=80)
我想创建一个 ModelForm,它具有 FoodItems 的多个文本输入,以便在一次提交表单时可以保存多种食物并将其与同一餐关联。
我现在的尝试只能一次提交一个食品。
class FoodItemForm(forms.ModelForm):
class Meta:
model = FoodItem
fields = ('name',)
如能以正确的方式执行此操作,我们将不胜感激!
创建名为 menu
:
|menu
├───migrations
| ...
├───templates
│ detail.html
│ list.html
│ __init__.py
│ admin.py
│ apps.py
│ forms.py
│ models.py
│ tests.py
│ urls.py
│ views.py
|project <--- the name of your "project"
│ asgi.py
│ settings.py
│ urls.py
│ wsgi.py
│ __init__.py
│templates <--- not used
│db.sqlite3
│manage.py
将 menu
应用程序添加到已安装的应用程序:
project/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'menu.apps.MenuConfig',
]
project/urls.py(项目级)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('menu.urls')),
]
menu/urls.py
from django.urls import path
from .views import list_view, detail_view
app_name = 'menu'
urlpatterns = [
path('', list_view, name='list'),
path('<pk>/', detail_view, name='detail'),
]
menu/models.py
import datetime
from django.db import models
class Meal(models.Model):
BFAST = 0
LUNCH = 1
DIN = 2
MEAL_TIME_CHOICES = [
(BFAST, 'Breakfast'),
(LUNCH, 'Lunch'),
(DIN, 'Dinner'),
]
meal_name = models.CharField(max_length=100)
date = models.DateField(default=datetime.date.today)
meal_time = models.IntegerField(choices=MEAL_TIME_CHOICES,default=0)
def __str__(self):
return self.meal_name
class FoodItem(models.Model):
meal = models.ForeignKey(Meal, on_delete=models.CASCADE)
food_item = models.CharField(max_length=80)
def __str__(self):
return self.food_item
确保 makemigrations
和 migrate
.
menu/forms.py
from django import forms
from django.forms.models import modelformset_factory
from .models import Meal, FoodItem
MealFormset = modelformset_factory(FoodItem, fields=('food_item',),
extra=5, can_delete=True)
menu/admin.py
from django.contrib import admin
from .models import Meal, FoodItem
class MealAdmin(admin.ModelAdmin):
pass
class FoodAdmin(admin.ModelAdmin):
pass
admin.site.register(Meal, MealAdmin)
admin.site.register(FoodItem, MealAdmin)
menu/views.py
from django.shortcuts import render, redirect
from .forms import MealFormset
from .models import Meal
def list_view(request):
context = {
'objects': Meal.objects.all(),
}
return render(request, 'list.html', context=context)
def detail_view(request, pk):
instance = Meal.objects.get(pk=pk)
formset = MealFormset(request.POST or None)
context = {
'object': instance,
'formset': formset,
}
if request.method == 'POST':
if formset.is_valid():
food_items = formset.save(commit=False)
if formset.deleted_forms:
for obj in formset.deleted_forms:
obj.instance.delete()
for food_item in food_items:
food_item.meal = instance
food_item.save()
return redirect('menu:list')
return render(request, 'detail.html', context=context)
menu/templates/list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>List</title>
</head>
<body>
<div style="margin: auto; width: 50%; border: 1px solid black; padding: 20px;">
<a href="{% url 'admin:index' %}">Admin Panel</a>
<h1>Meals</h1>
{% for meal in objects %}
<div>
<a href="{% url 'menu:detail' meal.pk %}">{{ meal }}</a>
</div>
{% endfor %}
</div>
</body>
</html>
menu/templates/detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Food Items</title>
</head>
<body>
<div style="margin: auto; width: 50%; border: 1px solid black; padding: 20px;">
<form method="POST">
{% csrf_token %}
{{ formset.management_form }}
{% for field in formset %}
<div style="padding: 5px 40px;">{{ field }}</div>
{% endfor %}
<input type="submit" value="Submit">
</form>
</div>
</body>
</html>
可能不完全您想要的,但说明了一些关键概念(我认为)。