首页 技术 正文
技术 2022年11月12日
0 收藏 714 点赞 3,329 浏览 16567 个字

MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动

ORM是“对象-关系-映射”的简称。

Django开发:(3.1)ORM:单表操作

Mysql中的表对应python中的类,表的字段对应类的属性,表的记录对应类的实例化的对象

单表操作

创建表

1. 创建模型

Django开发:(3.1)ORM:单表操作

创建名为app01的app,在app01下的models.py中创建模型:

from django.db import models# Create your models here.class Book(models.Model):  # 类名可以随便起,但一定得继承 models.Model
id = models.AutoField(primary_key=True) # AutoField表示自增字段; primary_key=True 表示是主键
title = models.CharField(max_length=32)
# state = models.BooleanField()
pub_date = models.DateField()
price = models.DecimalField(max_digits=8,decimal_places=2)
publish = models.CharField(max_length=32)

2. 更多字段和参数

每个字段有一些特有的参数,例如,CharField需要max_length参数来指定VARCHAR数据库字段的大小。还有一些适用于所有字段的通用参数。

3. settings配置

若想将模型转为mysql数据库中的表,需要在settings中配置:

DATABASES = {
'default':{
'ENGINE':'django.db.backends.mysql',
'NAME':'orm', # 要连接的数据库,连接前需要先创建好
'USER':'root', # 连接数据库的用户名
'PASSWORD':'tj037778', # 连接数据库的密码
'HOST':'127.0.0.1', # 连接主机
'PORT':3306 # 端口
}
}

注意1:NAME即数据库的名字,在mysql连接前该数据库必须已经创建(ORM只能处理到表这一层,数据库操作不了),而上面的sqlite数据库下的db.sqlite3则是项目自动创建 USER和PASSWORD分别是数据库的用户名和密码。设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。然后,启动项目,会报错:no module named MySQLdb 。这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb 对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名(project)文件下的__init__(ORM/ORM/__init__.py),在里面写入:

import pymysqlpymysql.install_as_MySQLdb()

最后通过两条数据库迁移命令即可在指定的数据库中创建表 :

python manage.py makemigrations
python manage.py migrate
# makemigrations 后并没有在数据库生成表,而是在对应的 migrationsns 文件夹下生成了 py 文件
# migrate 时会执行 migrations文件夹下的 py文件(至于执行哪个py文件,程序会先去 django自带的 django_migrations 这张表中去查,如果migrationsns 文件夹下人某个py文件在 django_migrations 这张表已经存在,则 migrate时则会跳过这个py文件不执行,即已经执行过的py文件会存放在 django_migrations表中)
# 修改数据库的时候,尽量用 makemigrations 和 migrate,不要直接在Navicat 中修改

注意2:确保配置文件中的INSTALLED_APPS中写入我们创建的app名称

# Application definitionINSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
]

注意3:如果报错如下:

django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None

MySQLclient目前只支持到python3.4,因此如果使用的更高版本的python,需要修改如下:

通过查找路径:D:\python\Lib\site-packages\django\db\backends\mysql\base.py

把里面的

if version < (1, 3, 3):
raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)

注释掉 就OK了。

注意4: 如果想打印orm转换过程中的sql,需要在settings中进行如下配置:

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}

添加表记录

urls.py

from django.contrib import admin
from django.urls import pathfrom app01 import viewsurlpatterns = [
path('admin/', admin.site.urls),
path(r'index/',views.index)
]

app01/models.py

from django.db import models# Create your models here.class Book(models.Model):  # 类名可以随便起,但一定得继承 models.Model
id = models.AutoField(primary_key=True) # AutoField表示自增字段; primary_key=True 表示是主键
title = models.CharField(max_length=32)
# state = models.BooleanField()
pub_date = models.DateField()
price = models.DecimalField(max_digits=8,decimal_places=2)
publish = models.CharField(max_length=32)

app01/views.py

from django.shortcuts import render,HttpResponse# Create your views here.# 先把模型导入进来
from app01.models import Bookdef index(request):
# 添加表记录 # 方式一:实例化Book对象
book_obj = Book(id=1,title="python全栈开发",pub_date="2018-6-7",price=100,publish="IT出版社") # pub_date是一个日期类型,一定得按照"年-月-日"的格式
book_obj.save() # save()之后记录才会生成 # 表里面的一条记录就是类的一个对象 # 方式二:用 objects去调用create; create方法有返回值,返回值就是生成的记录对象
book_obj2 = Book.objects.create(title="linux运维",pub_date="2015-6-7",price=100,publish="IT出版社") # id是自增的,所以无需再写
# 这种方式不需要 save();.objects.create(kwargs) 直接就在数据库中生成了一条记录(对象),并把这个对象返回给 book_obj2(我们也就可以打印book_obj2中的属性)
print(book_obj2.pub_date) return HttpResponse("ok")

单表查询:

models.py

from django.db import models# Create your models here.class Book(models.Model):  # 类名可以随便起,但一定得继承 models.Model
id = models.AutoField(primary_key=True) # AutoField表示自增字段; primary_key=True 表示是主键
title = models.CharField(max_length=32)
# state = models.BooleanField()
pub_date = models.DateField()
price = models.DecimalField(max_digits=8,decimal_places=2)
publish = models.CharField(max_length=32) def __str__(self): # 只是控制了对象的打印形式
return self.title

views.py

from django.shortcuts import render,HttpResponse# Create your views here.# 先把模型导入进来
from app01.models import Bookdef index(request): # #######################查询表结构API#######################
# 注意:要知道每个方法的返回值是什么、以及每个方法是谁来调用的 # 1. all() :查询所有结果;返回值是一个QuerySet数据类型(Django自定义的数据类型),调用者是 objects
all_books = Book.objects.all()
print(all_books)
# Book类没有 __str()__方法时的打印结果:
# <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>]>
# Book有 __str()__ 方法时的打印结果:
# <QuerySet [<Book: python全栈开发>, <Book: linux运维>]>
# QuerySet数据类型:相当于 [obj1,obj2,...],可用列表的方式去处理它 for obj in all_books: # 支持遍历
print(obj.title,obj.pub_date) print(all_books[1].publish) # 支持索引 # 2. first(),last()方法;返回值是一个model(模型)对象,调用者是 QuerySet对象
book1 = Book.objects.all().first() # 相当于 Book.objects.all()[0]
book2 = Book.objects.all().last() print(book1,book2) # 3. filter()方法:返回值:QuerySet对象,调用者:管理器(objects)
books = Book.objects.filter(title="python全栈开发",price=100) # filter()的作用相当于sql语句的where;# 返回值:[obj1,obj2,....];多个过滤条件用逗号分隔
print(books)
# filter()方法也可以调用 first(),last()方法
print(books.first()) # 4. get()方法:有且只有一个查询结果时才有意义;如果有多个查询结果或者没有查询结果,报错;所以,返回值:model对象
book_get = Book.objects.get(title="python全栈开发")
print(book_get.price) # 5. exclude():排除条件的过滤,对应filter();返回QuerySet
ret = Book.objects.exclude(title="python全栈开发")
print(ret) # 6. order_by():按照某种条件排序(默认是按照id);返回值:QuerySet,调用者:QuerySet
book_order_asc = Book.objects.all().order_by("id")
print(book_order_asc)
book_order_desc = Book.objects.all().order_by("-id") # 按照降序排列
print(book_order_desc)
book_price_desc = Book.objects.all().order_by("-price") # 按照价格降序排列
print(book_price_desc)
# Book.objects.all().order_by("-price","id") # 先按照价格降序排列,再按照id升序
# 要熟悉orm的链式操作 # 7. reverse() :对查询结果反向排序 # 8. count():计数;返回值:int,调用者:QuerySet
Book.objects.all().count() # 计数里面有多少个元素 # 9. exists():如果QuerySet包含数据,就返回True,否则返回False
ret_exists = Book.objects.all().exists() # 判断 Book.objects.all() 里面是否有数据 # 10. values(*field):field代表字段;values()具有遍历作用,返回一个QuerySet --- 一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列;返回值:QuerySet(列表里面放字典),调用者:QuerySet
# values()方法很重要
ret_values = Book.objects.all().values("price","title")
print(ret_values)
# <QuerySet [{'price': Decimal('100.00'), 'title': 'python全栈开发'}, {'price': Decimal('100.00'), 'title': 'linux运维'}]>
print(ret_values[0].get("price"))
# 100.00 # 11. values_list(*field):它与values()非常相似,它返回的QuerySet里面是一个元组序列,values返回的是一个字典序列
ret_values_list = Book.objects.all().values_list("price", "title")
print(ret_values_list)
# <QuerySet [(Decimal('100.00'), 'python全栈开发'), (Decimal('100.00'), 'linux运维')]> # 12. distinct():从返回结果中剔除重复纪录(通常配合values,values_list一起使用)
ret_distinct = Book.objects.all().values("price").distinct()
print(ret_distinct)
# <QuerySet [{'price': Decimal('100.00')}]>
# 注: Book.objects.all().distinct() 这种写法没有任何意义 # #######################查询表结构之模糊查询(都是双下划线)#######################
# 1. __gt :大于;__lt:小于;返回值:QuerySet
price__gt = Book.objects.filter(price__gt=50,price__lt=200)
print("__gt",price__gt) # 2. __startswith:以什么开头;返回值:QuerySet
obj_start = Book.objects.filter(title__startswith="py")
print(obj_start) # 3. __contains:包含什么;返回值:QuerySet
# 4. __icontains:包含某些元素(不区分大小写)
obj_contains = Book.objects.filter(title__contains="x")
print(obj_contains) # 5. __in = [] :是列表中的一个;返回值:QuerySet
obj_in = Book.objects.filter(price__in=[100,150,200])
print(obj_in) # 6. __year : 某一年的(只有date类型有);返回值:QuerySet
obj_year = Book.objects.filter(pub_date__year=2018)
print(obj_year) # 7. __range = [] : 在某个区间(包含两端)
obj_range = Book.objects.filter(price__range=[50,100])
print("range",obj_range) return HttpResponse("ok")

补充:例如上面的4.get() 方法,如果值不存在,可以捕获到这个异常

from django.core.exceptions import ObjectDoesNotExist  # ObjectDoesNotExist 值不存在的异常类型

单表之删除和编辑

views.py

from django.shortcuts import render,HttpResponse# Create your views here.# 先把模型导入进来
from app01.models import Bookdef index(request):
# #######################删除、修改表记录#######################
# delete() : 删除;调用者:QuerySet对象 或者 model对象;返回值是删除元素的一些信息
# Book.objects.filter(pub_date__year=2018).delete() # 需要都把记录查询出来才能删除;调用者:QuerySet对象
# Book.objects.filter(pub_date__year=2015).first().delete() # 调用者:model对象 # update():编辑记录; 调用者:QuerySet;返回值:更新的条数
Book.objects.filter(title__contains="linux").update(title="linux运营维护") return HttpResponse("ok")

图书管理系统

Django开发:(3.1)ORM:单表操作

settings.py

"""
Django settings for bookms project.Generated by 'django-admin startproject' using Django 2.0.1.For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""import os# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'm6=&s25aszxks#m(5f57mdpi)hc%v7#&e0$kmak48@80xr7t0h'# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = TrueALLOWED_HOSTS = []# Application definitionINSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
]MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]ROOT_URLCONF = 'bookms.urls'TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]WSGI_APPLICATION = 'bookms.wsgi.application'# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
# }
DATABASES = {
'default':{
'ENGINE':'django.db.backends.mysql',
'NAME':'bookms', # 要连接的数据库,连接前需要先创建好
'USER':'root', # 连接数据库的用户名
'PASSWORD':'tj037778', # 连接数据库的密码
'HOST':'127.0.0.1', # 连接主机
'PORT':3306 # 端口
}
}# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validatorsAUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/LANGUAGE_CODE = 'en-us'TIME_ZONE = 'UTC'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = True# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/STATIC_URL = '/static/' # 若存放静态文件的static目录在app目录下,则该句生效,无需定义下面的
# STATICFILES_DIRS = [
# os.path.join(BASE_DIR,"static"),
# ] # 若存放静态文件的static目录在project目录下,则用该定义LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}

urls.py

"""bookms URL ConfigurationThe `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,re_pathfrom app01 import viewsurlpatterns = [
path('admin/', admin.site.urls),
path(r"addbook/",views.addbook),
path(r"books/",views.books), # (\d+)用于匹配获取表记录的主键(书的id)
re_path(r"books/(\d+)/delete",views.bookdel),
re_path(r"books/(\d+)/edit",views.bookedit)
]

views.py

from django.shortcuts import render,HttpResponse,redirect# Create your views here.# 把模型表引入
from app01.models import Bookdef addbook(request):
# 由于templates/addbook.html 中 form标签的 action没写,所以还是提交到了当前页面index.html
if request.method == "POST":
title = request.POST.get("title")
price = request.POST.get("price")
date = request.POST.get("date")
press = request.POST.get("press") # 添加记录
book_obj = Book.objects.create(title=title,price=price,pub_date=date,publish=press) return redirect("/books/") return render(request,"addbook.html")def books(request):
books_list = Book.objects.all() return render(request,"books.html",locals())def bookdel(request,pk):
# 把对应ID的书删除
Book.objects.filter(id=pk).delete() # redirect()的作用就是让浏览器往重定向的路径再发一次请求
return redirect("/books/")def bookedit(request,id):
# 编辑对应ID的书 # 先获取对应书对象
book_obj = Book.objects.filter(id=id).first() # 由于编辑后的内容还是提交到当前页面,所以在这个函数里面要判断请求是否就POST
if request.method == "POST":
# 获取编辑后的内容
title = request.POST.get("title")
price = request.POST.get("price")
date = request.POST.get("date")
press = request.POST.get("press") # 把编辑后的内容更新到表中
Book.objects.filter(id=id).update(title=title,price=price,pub_date=date,publish=press)
# 更新完后重定向到 books.html 页面
return redirect("/books/") return render(request,"editbook.html",{"book_obj":book_obj})

models.py

from django.db import models# Create your models here.class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
pub_date = models.DateField()
price = models.DecimalField(max_digits=8, decimal_places=2)
publish = models.CharField(max_length=32)

books.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>addbook</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" > <style>
.container{
margin-top: 70px;
}
.btn{
margin-bottom: 10px;
}
</style></head>
<body>
<h3>查看书籍</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<a href="/addbook/" rel="external nofollow" class="btn btn-primary">添加书籍</a>
{# .table-striped 类可以给 <tbody> 之内的每一行增加斑马条纹样式;.table-bordered 类为表格和其中的每个单元格增加边框。#}
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>书籍名称</th>
<th>价格</th>
<th>出版日期</th>
<th>出版社</th>
<th>编辑</th>
<th>删除</th>
</tr>
</thead>
<tbody>
{# 遍历 books_list是的每一个对象(表记录),把每条表记录添加到 table的一行中 #}
{% for book in books_list %}
<tr>
<td>{{ book.title }}</td>
<td>{{ book.price }}</td>
{# 利用date过滤器格式化时间样式 #}
<td>{{ book.pub_date|date:"Y-m-d" }}</td>
<td>{{ book.publish }}</td>
{# 动态的为每个a标签添加索引 #}
<td><a href="/books/{{ book.pk }}/edit" rel="external nofollow" class="btn btn-info">编辑</a></td>
{# 把主键id添加到a标签的路径中;.pk表示model对象的主键 #}
<td><a href="/books/{{ book.pk }}/delete" rel="external nofollow" class="btn btn-danger">删除</a></td>
</tr> {% endfor %} </tbody>
</table>
</div>
</div>
</div></body>
</html>

addbook.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>addbook</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" > <style>
.container{
margin-top: 70px;
}
.btn{
margin-top: 10px;
}
</style></head>
<body>
<h3>添加书籍</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post">
{% csrf_token %}
<div>
<label for="">书籍名称</label>
{# form-controlform-control控件,可以为input元素添加CSS定制样式#}
<input type="text" class="form-control" name="title">
</div>
<div>
<label for="">价格</label>
<input type="text" class="form-control" name="price">
</div>
<div>
<label for="">出版日期</label>
{# date类型:能够下拉选择日期 #}
<input type="date" class="form-control" name="date">
</div>
<div>
<label for="">出版社</label>
<input type="text" class="form-control" name="press">
</div>
{# btn btn-success:绿色按钮;pull-right:右移 #}
<input type="submit" class="btn btn-success pull-right">
</form>
</div>
</div>
</div></body>
</html>

editbook.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>addbook</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" > <style>
.container{
margin-top: 70px;
}
.btn{
margin-top: 10px;
}
</style></head>
<body>
<h3>编辑书籍</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
{# form标签的action没写,所以默认还是会提交到当前页面 #}
<form action="" method="post">
{% csrf_token %}
<div>
<label for="">书籍名称</label>
{# 编辑时书名这一栏的内容是该对象原先的title;其它栏同理 #}
<input type="text" class="form-control" name="title" value="{{ book_obj.title }}">
</div>
<div>
<label for="">价格</label>
<input type="text" class="form-control" name="price" value="{{ book_obj.price }}">
</div>
<div>
<label for="">出版日期</label>
{# 此处的日期也需要用date过滤器格式化,要不然显示不出来 #}
<input type="date" class="form-control" name="date" value="{{ book_obj.pub_date|date:'Y-m-d' }}">
</div>
<div>
<label for="">出版社</label>
<input type="text" class="form-control" name="press" value="{{ book_obj.publish }}">
</div>
{# btn btn-success:绿色按钮;pull-right:右移 #}
<input type="submit" class="btn btn-success pull-right">
</form>
</div>
</div>
</div></body>
</html>
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,491
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,907
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,740
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,493
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,132
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,294