在Django框架中,有两种定义表单的方式。
-
在HTML中可使用
<form>
标签定义表单域,此表单域中再包含单选或多行文本框、密码框、单选按钮、复选按框、下拉选择框等元素。这种方式的特点是使用纯粹的HTML和CSS知识实现,学习的成本较低,更适用于对前端HTML比较擅长的同学。 -
在Python代码中通过Form类或其子类也可以定义表单。因表单类生成的表单不包含
<form>
标签,使用时须将其嵌入到<form>
标签中。这种方式的特点是可复用表单类或表单类的部分属性,这种方式适用于喜欢写Python代码的同学。当然,这种实现方式同样可以使用CSS美化、装修。
本次学习的重点是表单类的实现方式。
7.1 继承Form类的表单
准备工作:创建Django项目chapter07,并创建应用app01,在此应用下创建forms.py模块,用于定义所有的表单。
第一步:在forms.py中定义表单类LoginForm
类,
#forms.py
from django import forms
class LoginForm(forms.Form):
username = forms.CharField(max_length=20, min_length=2, required=True,
label='用户名',
error_messages={
'min_length': '用户名不能小于2个字符'
})
password = forms.CharField(widget=forms.PasswordInput, label='密码')
第二步:定义视图函数
from django.http import HttpResponse
from django.shortcuts import render
from app01.forms import LoginForm
def login(request):
# Post请求
if request.method == 'POST':
# 根据接收到的数据创建表单类对象
loginForm = LoginForm(request.POST)
if loginForm.is_valid():
# 取数据的方法
username = loginForm.cleaned_data.get('username')
password = loginForm.cleaned_data.get('password')
return HttpResponse(f'{username}--{password}')
else:
print(loginForm.errors)
return HttpResponse('表单验证失败')
# get 请求
elif request.method == 'GET':
# 创建空表单
loginForm = LoginForm()
return render(request, 'index.html', {'loginForm': loginForm})
第三步:定义路由,项目urls.py中
from django.urls import path
from app01.views import login
urlpatterns = [
path('login/', login),
]
第四步:在templates中创建index.html
模板
<body>
<h2>表单示例</h2>
<form action="" method="post">
{% csrf_token %}
{{ loginForm }}
<input type="submit" value="提交">
</form>
</body>
7.2 继承ModelForm类的表单
为了方便对比,此处先用HTML表单实现,再改造为ModelForm表单。
7.2.1 HTML表单实现方式
在7.1项目基础上创建goods应用
python manage.py startapp goods
项目底下创建templates文件夹,在文件夹里创建goods.html文件
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品列表</title>
</head>
<body>
<div>
<table id="myTable" cellpadding="1" cellspacing="0" border="1"
style="width:100%;max-width: 100%;margin-bottom: 20px ">
<caption align="top" style="font-size: 26px">商品列表</caption>
<thead>
<tr>
<th>序号</th>
<th>名字</th>
<th>价格</th>
<th>库存</th>
<th>销量</th>
<th>管理</th>
</tr>
</thead>
<tbody>
{% for row in goods %}
<tr align="center">
<td>{{ forloop.counter }}</td>
<td>{{ row.name }}</td>
<td>{{ row.price }}</td>
<td>{{ row.stock }}</td>
<td>{{ row.sales }}</td>
<td>
<a href="goods/{{ row.id }}">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div>
{# 表单一:添加 #}
<form method="post" action="/info/" cellpadding="1" cellspacing="0"
border="1">
{% csrf_token %}
<input type="submit" value="添加">
商品:<input type="text" name="good_name">
价格:<input type="text" name="good_price">
库存:<input type="text" name="good_stock">
销量:<input type="text" name="good_sales">
</form>
</div>
<div>
{# 表单二:修改 #}
<form method="post" action="/goods/" cellpadding="1" cellspacing="0"
border="1">
{% csrf_token %}
<input type="submit" value="修改">
序号:<input type="text" name="good_num">
商品:<input type="text" name="good_name">
价格:<input type="text" name="good_price">
库存:<input type="text" name="good_stock">
销量:<input type="text" name="good_sales">
</form>
</div>
</body>
</html>
goods应用下的models里定义goods模型
from django.db import models
class Goods(models.Model):
"""商品SKU"""
# 可以利用null和blank属性使部分字段留空
create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
name = models.CharField(max_length=50, verbose_name='名字')
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='价格')
stock = models.IntegerField(default=0, verbose_name='库存')
sales = models.IntegerField(default=0, verbose_name='销量')
class Meta:
db_table = 'tb_goods'
verbose_name = '商品'
verbose_name_plural = verbose_name
def __str__(self):
return f'name: {self.name}, price: {self.price}, stock: {self.stock}, sales: {self.sales}'
生成迁移文件并执行迁移
python manage.py makemigrations python manage.py migrate
在goods应用下的views.py文件里定义视图类,展示商品和删除商品的逻辑。然后分别在类GoodView和UpdateDestoryGood中实现商品展示和删除的功能。
from django.shortcuts import render, redirect, reverse
from django.views import View
from django import http
from .models import Goods
class GoodView(View):
"""商品视图类"""
def get(self, request):
"""展示商品"""
goods = Goods.objects.all()
context = {
'goods': goods,
}
return render(request, 'goods.html', context)
def post(self, request):
"""添加商品"""
good = Goods()
try:
good.name = request.POST.get('good_name')
good.price = request.POST.get('good_price')
good.stock = request.POST.get('good_stock')
good.sales = request.POST.get('good_sales')
good.save()
# return redirect('goods/info/') # 快捷方式
return redirect(reverse('goods:info'))
except Exception as e:
print(e)
return http.HttpResponseForbidden('数据错误')
class UpdateDestoryGood(View):
"""编辑或删除商品"""
def get(self, request, gid):
"""删除商品数据"""
try:
good = Goods.objects.get(id=gid)
good.delete()
except Exception as e:
print(e)
return http.HttpResponseForbidden('删除失败')
return redirect(reverse('goods:info'))
def post(self, request, gid=0):
"""编辑商品"""
num = request.POST.get('good_num')
print(type(num))
one_goods = Goods.objects.all()[int(num) - 1]
print(one_goods)
one_goods.name = request.POST.get('good_name')
one_goods.price = request.POST.get('good_price')
one_goods.stock = request.POST.get('good_stock')
one_goods.sales = request.POST.get('good_sales')
# one_goods.save()
Goods.objects.filter(id=one_goods.id).update(price=one_goods.price, stock=one_goods.stock,
sales=one_goods.sales, name=one_goods.name)
return redirect(reverse('goods:info'))
配置根路由urls.py
from django.urls import path, include
from app01.views import login
urlpatterns = [
path('login/', login),
path('', include('goods.urls')),
]
(2)goods/urls.py
from django.urls import path, re_path
from goods import views
app_name = 'goods'
urlpatterns = [
# 展示商品数据、添加商品
path('info/', views.GoodView.as_view(), name='info'),
# 修改删除商品
re_path('goods/(\d*)', views.UpdateDestoryGood.as_view()),
]
利用数据文件goods.sql插入商品数据,
INSERT INTO tb_goods (create_time, update_time, name, price, stock, sales) VALUES
('2019-09-11 17:28:21.804713', '2019-09-25 11:09:04.532866', 'Apple MacBook Pro 13.3英寸笔记本 银色', 11388, 5, 5),
('2019-09-12 06:53:54.575306', '2019-09-23 11:44:03.825103', 'Apple MacBook Pro 13.3英寸笔记本 深灰色', 11398, 0, 1),
('2019-09-14 02:14:04.599169', '2019-09-20 02:28:13.579856', 'Apple iPhone 11 Pro 256GB 香芋紫', 6499, 6, 4),
('2019-09-14 02:20:33.355996', '2019-09-14 17:27:12.736139', 'Apple iPhone 11 Plus 256GB 金色', 7988, 8, 2),
('2019-09-14 02:45:23.341909', '2019-09-14 17:27:17.181609', 'Apple iPhone 11 Plus 64GB 深空灰色', 6688, 10, 0),
('2019-09-14 02:49:40.912682', '2019-09-25 11:09:35.936530', 'Apple iPhone 11 Plus 256GB 深空灰色', 7988, 0, 5),
('2019-09-14 02:55:11.172604', '2019-09-14 17:27:28.772353', 'Apple iPhone 11 Plus 64GB 银色', 6688, 3, 0),
('2019-09-14 02:56:17.331169', '2019-09-14 17:27:34.536772', 'Apple iPhone 11 Plus 256GB 银色', 7988, 9, 1),
('2019-09-14 03:09:00.909709', '2019-09-14 17:27:40.624770', '华为 HUAWEI Pro 30 6GB+64GB 钻雕金', 3388, 4, 0),
( '2019-09-14 03:13:40.226704', '2019-09-25 11:06:55.087206', '华为 HUAWEI Pro 30 6GB+128GB 钻雕金', 3788, 3, 0),
( '2019-09-14 03:16:27.620102', '2019-09-25 10:56:51.267674', '华为 HUAWEI Pro 30 6GB+128GB 钻雕蓝', 3788, 5, 0),
( '2019-09-14 03:17:25.671905', '2019-09-14 17:28:06.649098', '华为 HUAWEI Pro 30 6GB+64GB 钻雕蓝', 3388, 5, 0),
( '2019-09-14 03:18:04.588296', '2019-09-14 17:28:23.886231', '华为 HUAWEI Pro 30 6GB+64GB 玫瑰金', 3388, 5, 0),
( '2019-09-14 03:19:03.691772', '2019-09-25 11:10:51.316291', '华为 HUAWEI Pro 30 6GB+128GB 玫瑰金', 3788, 0, 4),
( '2019-09-20 02:27:04.955931', '2019-09-20 02:27:04.956931', 'Apple iPhone 11 Pro 256GB 香芋紫', 6499, 6, 3);
7.2.2 使用表单类
1、在goods应用中创建forms.py,在其中定义表单类,代码如下:
from django import forms
from django.forms import ModelForm
from django.forms import formset_factory
from . import models
from .models import Goods
class GoodForm(ModelForm):
class Meta:
model = Goods
fields = ['name', 'price', 'sales', 'stock']
2、goods.html修改为
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品列表</title>
</head>
<body>
<div>
<table id="myTable" cellpadding="1" cellspacing="0" border="1"
style="width:100%;max-width: 100%;margin-bottom: 20px ">
<caption align="top" style="font-size: 26px">商品列表</caption>
<thead>
<tr>
<th>序号</th>
<th>名字</th>
<th>价格</th>
<th>库存</th>
<th>销量</th>
<th>管理</th>
</tr>
</thead>
<tbody>
{% for row in goods %}
<tr align="center">
<td>{{ forloop.counter }}</td>
<td>{{ row.name }}</td>
<td>{{ row.price }}</td>
<td>{{ row.stock }}</td>
<td>{{ row.sales }}</td>
<td>
<a href="goods/{{ row.id }}">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div>
{# 表单一:添加 #}
<form method="post" action="/info/" cellpadding="1" cellspacing="0"
border="1">
{% csrf_token %}
<input type="submit" value="添加">
{# 商品:<input type="text" name="good_name">#}
{# 价格:<input type="text" name="good_price">#}
{# 库存:<input type="text" name="good_stock">#}
{# 销量:<input type="text" name="good_sales">#}
{{ form }}
{# {{ form.as_table }}#}
</form>
</div>
<div>
{# 表单二:修改 #}
<form method="post" action="/goods/" cellpadding="1" cellspacing="0"
border="1">
{% csrf_token %}
<input type="submit" value="修改">
{# 序号:<input type="text" name="good_num">#}
{# 商品:<input type="text" name="good_name">#}
{# 价格:<input type="text" name="good_price">#}
{# 库存:<input type="text" name="good_stock">#}
{# 销量:<input type="text" name="good_sales">#}
序号: <input type="text" name="good_num">
{{ form }}
{# {{ form.as_table }}#}
</form>
</div>
</body>
</html>
goods应用下视图文件修改
from django.shortcuts import render, redirect, reverse
from django.views import View
from django import http
from app01.forms import GoodForm
from .models import Goods
class GoodView(View):
"""商品视图类"""
def get(self, request):
"""展示商品"""
# goods = Goods.objects.all()
# context = {
# 'goods': goods,
# }
# return render(request, 'goods.html', context)
# 使用表单
goods = Goods.objects.all()
form = GoodForm()
context = {
'goods': goods,
'form': form,
}
return render(request, 'goods.html', context)
def post(self, request):
"""添加商品"""
# good = Goods()
# try:
# good.name = request.POST.get('good_name')
# good.price = request.POST.get('good_price')
# good.stock = request.POST.get('good_stock')
# good.sales = request.POST.get('good_sales')
# good.save()
# # return redirect('goods/info/') # 快捷方式
# return redirect(reverse('goods:info'))
# except Exception as e:
# print(e)
# return http.HttpResponseForbidden('数据错误')
# 使用表单
good = Goods()
# 使用已提交的数据实例化NameForm
form = GoodForm(request.POST)
# 判断表单是否已验证,获取已验证的数据
if form.is_valid():
good_data = form.cleaned_data
good.name = good_data['name']
good.price = good_data['price']
good.stock = good_data['stock']
good.sales = good_data['sales']
try:
good.save()
except:
return http.HttpResponseForbidden('数据错误')
return redirect(reverse('goods:info'))
class UpdateDestoryGood(View):
"""编辑或删除商品"""
def get(self, request, gid):
"""删除商品数据"""
try:
good = Goods.objects.get(id=gid)
good.delete()
except Exception as e:
print(e)
return http.HttpResponseForbidden('删除失败')
return redirect(reverse('goods:info'))
def post(self, request, gid=0):
"""编辑商品"""
# num = request.POST.get('good_num')
# print(type(num))
# one_goods = Goods.objects.all()[int(num) - 1]
# print(one_goods)
# one_goods.name = request.POST.get('good_name')
# one_goods.price = request.POST.get('good_price')
# one_goods.stock = request.POST.get('good_stock')
# one_goods.sales = request.POST.get('good_sales')
# # one_goods.save()
# Goods.objects.filter(id=one_goods.id).update(price=one_goods.price, stock=one_goods.stock,
# sales=one_goods.sales, name=one_goods.name)
# return redirect(reverse('goods:info'))
# 使用表单
goods = Goods.objects.all()
count = goods.count()
form = GoodForm(request.POST)
good_num = request.POST.get('good_num')
if form.is_valid():
good_data = form.cleaned_data
for i in range(1, count + 1):
if i == int(good_num):
good = goods[i - 1]
good.name = good_data['name']
good.price = good_data['price']
good.stock = good_data['stock']
good.sales = good_data['sales']
try:
good.save()
break
except Exception as e:
return http.HttpResponseForbidden('编辑失败')
return redirect(reverse('goods:info'))