首页 技术 正文
技术 2022年11月14日
0 收藏 911 点赞 3,063 浏览 16019 个字

数据库与ORM

1    django默认支持sqlite,mysql, oracle,postgresql数据库。

 <1> sqlite

django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3

<2> mysql

引擎名称:django.db.backends.mysql

2    mysql驱动程序

  • MySQLdb(mysql python)
  • mysqlclient
  • MySQL
  • PyMySQL(纯python的mysql驱动程序)

3     在django的项目中会默认使用sqlite数据库,在settings里有如下设置:

# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databasesDATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}

如果我们需要更改数据库,就需要改其配置信息,一般是mysql

DATABASES = {    'default': {        'ENGINE': 'django.db.backends.mysql',        'NAME': 'django_learn',    #你的数据库名称        'USER': 'root',   #你的数据库用户名        'PASSWORD': '', #你的数据库密码        'HOST': '', #你的数据库主机,留空默认为localhost        'PORT': '3306', #你的数据库端口    }}
NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建USER和PASSWORD分别是数据库的用户名和密码

这样我们的数据库就基本配置完成,我们可以创建一个表来测试一下:

Django 框架 数据库操作

我简单的创建一个数据表:

from django.db import models# Create your models here.class Book(models.Model):   #必须继承
name = models.CharField(max_length=32)
price = models.FloatField()

然后启动:

python manage.py makemigrations

启动后会报错,报错信息如下:

Traceback (most recent call last):
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\backends\mysql\base.py", line 15, in <module>
import MySQLdb as Database
ModuleNotFoundError: No module named 'MySQLdb'The above exception was the direct cause of the following exception:Traceback (most recent call last):
File "manage.py", line 15, in <module>
execute_from_command_line(sys.argv)
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\core\management\__init__.py", line 371, in execute_from_command_line
utility.execute()
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\core\management\__init__.py", line 347, in execute
django.setup()
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\apps\registry.py", line 112, in populate
app_config.import_models()
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\apps\config.py", line 198, in import_models
self.models_module = import_module(models_module_name)
File "G:\PythonLearning\django_project\venv\lib\importlib\__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\contrib\auth\models.py", line 2, in <module>
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\contrib\auth\base_user.py", line 47, in <module>
class AbstractBaseUser(models.Model):
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\models\base.py", line 114, in __new__
new_class.add_to_class('_meta', Options(meta, app_label))
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\models\base.py", line 315, in add_to_class
value.contribute_to_class(cls, name)
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\models\options.py", line 205, in contribute_to_class
self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\__init__.py", line 33, in __getattr__
return getattr(connections[DEFAULT_DB_ALIAS], item)
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\utils.py", line 202, in __getitem__
backend = load_backend(db['ENGINE'])
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\utils.py", line 110, in load_backend
return import_module('%s.base' % backend_name)
File "G:\PythonLearning\django_project\venv\lib\importlib\__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\backends\mysql\base.py", line 20, in <module>
) from err
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?
这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL
所以,我们只需要找到项目名文件下的__init__,在里面写入:

Django 框架 数据库操作

#在该文件写入如下代码
import pymysql
pymysql.install_as_MySQLdb()

这样问题就解决了。

Django 框架 数据库操作

但表并未成功创建,还需运行:

python manage.py migrate

这样就完全成功,可以在数据库上查看我们创建的表了。

ORM表模型

表(模型)的创建:

实例:我们来假定下面这些概念,字段和关系

作者模型:一个作者有姓名。

学生模型:每个学生对应一张身份证,每张身份证对应一位学生,学生和身份证模型之间是一对一的关系(one-to-one)

班级模型:一个班级有多个学生,但一个学生不能在多个班上,学生和班级是一对多关联关系(one-to-many),也被称作外键。

班级老师模型:一个老师可以教多个班级,一个班级可以由多个老师教,老师和班级模型就是多对多的关联关系(many-to-many)

表关系一对一:  

#实质就是在主外键(author_id就是foreign key)的关系基础上,
# 给外键加了一个UNIQUE=True的属性,基本上很少用到一对一,就一张表表示就可以了

单表操作

创建表:
class Student(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
gender = models.BooleanField()

若表已经被创建,想要增加新的字段,直接在我们创建的类里面加,例:

class student(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
gender = models.BooleanField()
sign_date = models.CharField(max_length=10) #新加字段

然后运行:

python manage.py makemigrations

这时会出现选择框,让我们填一下,我们之前上传数据后,该列的默认值:

(venv) G:\PythonLearning\orm_learn>python manage.py makemigrations
You are trying to add a non-nullable field 'sign_date' to student without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 1 #这里选择一,然后填写对应的数值

增加数据(也叫添加表记录):

def one_increate(request):
#添加表数据一 推荐使用
student.objects.create(name="wallace",age=12,gender=True)
student.objects.create(**{
"name":"夏露",
"age":13,
"gender":False
})
#添加表数据二
stu1 = student(name="小米",age=14,gender=False)
stu1.save()
stu2 = student()
stu2.name="小张"
stu2.age=13
stu2.gender=True
stu2.save() return HttpResponse("创建成功")

删除表记录

student.objects.filter(name="wallace").delete()

修改表记录

    #方法一
student.objects.filter(id=6).update(age=20) #找到直接修改对应字段 #方法二
stu1 = student.objects.filter(id=4)[0]   #找到后全部修改一遍,再保存不推荐用
stu1.age=50
stu1.save()

查询表记录

关于查询的API

#  <1>filter(**kwargs):      它包含了与所给筛选条件相匹配的对象,结果是一个QuerySet对象,里面的元素是表对象,也叫表记录#  <2>all():                 查询所有结果,结果是Query对象,里面的元素是表对象,也叫表记录#  <3>get(**kwargs):         返回与所给筛选条件相匹配的对象,返回结果有且只有一个就是表对象,也叫表记录,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
#  <4>exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象,与filter相反,结果是一个QuerySet对象,里面元素是表对象也叫表记录
 

例:

#关于查找的API
def one_filter(request):
#单用filter得到是一个QuerySet的对象,类似列表,可迭代,可切片
stu_obj = student.objects.filter(id=4)
print(stu_obj)
#将里面的数据迭代,结构是一个表记录对象,可以通过点的方式取值
for i in stu_obj:
print(i) #表对象,就是一个表的记录
print(i.name) #取值
return HttpResponse("查询结束")def one_get(request):
#直接得到一个表对象,也就是表记录 如果得到多个会报错
stu_obj=student.objects.get(id=4)
print(stu_obj)
#取值
print(stu_obj.name)
return HttpResponse("查询结束")def one_all(request):
#查询结果是一个QuerySet,类似列表,里面是所有的表对象也叫表记录
stu_obj_list=student.objects.all()
print(stu_obj_list)
#迭代出每一个表记录,通过属性对其取值
for i in stu_obj_list:
print(i) print(i.name)
return HttpResponse("查询结束")
def one_exclude(request):
  #查询结果是QuerySet对象,里面的元素是除了给定条件的所有表对象,也叫表记录
stu_obj = student.objects.exclude(name="wallace")
  #和上面的效果一样
stu_obj1 = student.objects.all().exclude(name="wallace")
print(stu_obj)
for i in stu_obj:
print(i.name)
print(stu_obj1)
return HttpResponse("exclude success")

对查询结构处理的方法:

 values:返回得到的是一个Queryset对象,但里面的元素是一个个字典。单表情况下配合all或直接引用。多表以后介绍

def one_value(request):
#values得到的是一个特殊的QuerySet对象,类似列表,但里面的元素是一个一个的字典
stu_obj = student.objects.values("name")
print(stu_obj)
#得到的是一个特殊的QuerySet对象,类似列表,里面的元素是一个一个字典
stu_obj2 = student.objects.filter(id=3).values("name")
print(stu_obj2)
    
#all取得了全部的表记录,类似于一张表,所以可以去的对应的值
stu_obj3 = student.objects.all().values("name")
print(stu_obj3)
return HttpResponse("value success")  
def one_value_list(request):
#和value类似,返回的是QuerySet对象,不过里面元素是列表的形式
stu_obj = student.objects.values_list()
print(stu_obj)

正向排序order_by_反向排序reverse

#  <6>order_by(*field):      对查询结果排序,默认是正向排序,*field为按照那个字段排序#   <7>reverse():              在排序的基础上加上这个语句,进行反向排序,单独使用无意义

def one_order(request):
#排序
stu_obj = student.objects.all().order_by("age")
stu_obj1 = student.objects.order_by("age")
stu_obj3 = student.objects.filter(age__gt=20).order_by("age")
print(stu_obj3)
for i in stu_obj3:
print(i.age) return HttpResponse("order success")def one_reverse(request):
stu_obj = student.objects.all().order_by("age").reverse()
stu_obj1 = student.objects.filter(age__gte=20).order_by("age").reverse() print(stu_obj)
for i in stu_obj:
print(i.age) return HttpResponse("rever success")

distinct:从返回结果中剔除重复记录

def one_distinct(request):
#取重,这里的去重指的是全部字段都重复时进行去重,除了id字段
stu_obj = student.objects.all().distinct().values("age","name")
stu_obj1 = student.objects.distinct().values("age","name")
print(stu_obj)
print(stu_obj1) return HttpResponse("distinct success")

count:返回数据库中匹配查询(QuerySet)的对象数量

def one_count(request):
#统计得到表对象的数量
stu_obj = student.objects.all().count()
print(stu_obj)
stu_obj1 = student.objects.filter(age__gte=30).count()
print(stu_obj1) stu_obj2 = student.objects.count()
print(stu_obj2) return HttpResponse("success")

first和last:返回得到QuerySet对象中的第一或最后一个表对象

def one_first_last(request):
#fiest返回找到的第一个表对象
stu_obj = student.objects.first()
stu_obj1 = student.objects.filter(age=13).first()
print(stu_obj,stu_obj1)
#last返回的是最后一个表对象
stu_obj2 = student.objects.last()
print(stu_obj2)
return HttpResponse('success')

单表的双下划线操作

        __gt     大于
__lt 小于
__gte 大于等于
__lte 小于等于
__in 在几个值中
__range 在一定范围内

#双下滑线机制,单表
def one__select(request):
#大于13
stu_obj = student.objects.filter(age__gt=13).values("age")
#大于等于13
stu_obj1 = student.objects.filter(age__gte=13).values("age")
print(stu_obj)
print(stu_obj1)
#小于
stu_obj2 = student.objects.filter(age__lt=13).values("age")
#小于等于
stu_obj3 = student.objects.filter(age__lte=13).values("age")
print(stu_obj2)
print(stu_obj3)
#等于13
stu_obj4 = student.objects.filter(age__exact=13).values("age")
print(stu_obj4)
#在多个值里面
stu_obj5 = student.objects.filter(age__in=[12,13,14]).values("age")
print(stu_obj5)
#在一个范围里面
stu_obj6 = student.objects.filter(age__range=[12,14]).values("age")
print(stu_obj6)
return HttpResponse("success")

注意:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。如在遍历或条件

判断的时候,查询数据库的操作才会执行。

查找表字段,也叫对象查找:

def one_obj(request):
stu_obj_list = student.objects.all()
#从Queryset集合中得到表对象,通过对象获取里面的字段
for stu_obj in stu_obj_list:
print(stu_obj.name)
print(stu_obj.age)
print(stu_obj.gender) return HttpResponse("success")

一对多的表操作

在创建多表关系时,先讨论一下,FroeignKey的创建时需注意的地方:

我现在用的是django2.0.2版本,当使用models.ForeignKey设置外键,但是不添加on_delete时,总是会报错:TypeError: __init__() missing 1 required positional argument: 'on_delete'而django自从1.9版本之后,on_delete这个参数就必须设置了看看官网怎么说的吧:ForeignKey.on_delete
当一个ForeignKey 引用的对象被删除时,Django 默认模拟SQL 的ON DELETE CASCADE 的约束行为,并且删除包含该ForeignKey的对象。这种行为可以通过设置on_delete 参数来改变。例如,如果你有一个可以为空的ForeignKey,在其引用的对象被删除的时你想把这个ForeignKey 设置为空:user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)on_delete 在django.db.models中可以找到的值有:
CASCADE
级联删除;默认值。
PROTECT
抛出ProtectedError 以阻止被引用对象的删除,它是django.db.IntegrityError 的一个子类。
SET_NULL¶
把ForeignKey 设置为null; null 参数为True 时才可以这样做。
SET_DEFAULT¶
ForeignKey 值设置成它的默认值;此时必须设置ForeignKey 的default 参数。
SET()¶
设置ForeignKey 为传递给SET() 的值,如果传递的是一个可调用对象,则为调用后的结果。在大部分情形下,传递一个可调用对象用于避免models.py 在导入时执行查询:
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
def get_sentinel_user():
return get_user_model().objects.get_or_create(username='deleted')[0]
class MyModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.SET(get_sentinel_user))
DO_NOTHING¶
Take no action. 如果你的数据库后端强制引用完整性,它将引发一个IntegrityError ,除非你手动添加一个ON DELETE 约束给数据库自动(可能要用到初始化的SQL)。

创建一对多关系表Fromkey:

class Teacher(models.Model):
name = models.CharField(max_length=30)
galary = models.IntegerField()
book = models.ForeignKey("Book",null=True,on_delete=models.SET_NULL) #外键关联我们要关联的表
class Book(models.Model):
name = models.CharField(max_length=30)
price = models.FloatField()

增加表数据:

def one_more_create(request):
#添加方式一(推荐) 通过下划线的方式获取书籍id
teach_obj = Teacher.objects.create(
name="天蚕土豆",
galary=3000,
book_id=1
)
teach_obj2 = Teacher.objects.create(
name="老男孩",
galary=4000,
book_id=2
)
#添加方式二,先获取书籍对象,再将对象赋值给book
book_obj = Book.objects.filter(id=3).first()
teach_obj3 = Teacher.objects.create(
name="wallace",
galary=1000000,
book=book_obj
)
return HttpResponse("SUCCESS")

删除和修改和单表操作一致,这里就不再赘述了。

一对多查询表记录:

方法一:从一个表中得到关联表的对象,然后从对象中取值,分为正向查询和反向查询

def one_more_filter(request):
#正向查询,由多的一方查询一个的一方,通过老师查询出版的书籍名字
#查询方式一
# teach_obj = Teacher.objects.get(name="wallace")
# book_obj = teach_obj.book
# print(book_obj.name)
#查询过程:得到表对象,找到对应的外键字段,这个外键字段就是一个表记录对象
#通过这个表记录对象去查找对应表对象的字段 # 反向查询:一个的一方查多个的一方,比如查询出版某本书籍的老师
book_obj1 = Book.objects.filter(name="傲视九重天").first()
teach_name = book_obj1.teacher_set.all().values("name")
#通过关联的表的类名加上_set得到对应表的对象 classname_set
#类似于在父表也创建了一个隐藏的字段
print(teach_name)
return HttpResponse("查询成功")

方式二:通过(filter,values)的双下滑线机制,理论是将两个表内联结在一起:

def one_more_filter1(request):
#一对多表查询(filter,values)的双下划线的使用:当你要跨表操作时,就通过双下划线来操作
#这种双下划线就像吧两个表内联结在一起,同过下划线获取关联表的字段
#正向 classname__filed
   #查找老师为wallace的所有书的书名
book_name = Teacher.objects.filter(name="wallace").values("book__name")
print(book_name)
  #查找数目为极品家丁的老师:通过老师表,得到书籍名,跨表
teach_name = Teacher.objects.filter(book__name="极品家丁").values("name")
print(teach_name)
#反向
book_name1 = Book.objects.filter(teacher__name="wallace").values("name")
teach_name1 = Book.objects.filter(name="大主宰").values("teacher__name")
print(book_name1)
print(teach_name1)
#更多筛选条件 classname__filed__逻辑条件
teach_name2=Book.objects.filter(name="大主宰",teacher__galary__gt=5000).values("teacher__name")
print(teach_name2) return HttpResponse("success")

多对多表关系

创建多对多表关系:

class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()class Book(models.Model):
title = models.CharField(max_length=100)
price = models.IntegerField()
author = models.ManyToManyField("Author") #通过该字段就可以创建一个Book和Author的多对多表字段#这种方式的创建会自动帮我们生成一张关系表,不需要我们自己手动创建

还有另一种创建方式就是我们自己手动的创建第三张关系表,表中只有两表的主键id,因为这种用的不多所以在这里就不赘述了。

多对多表添加绑定关系,如果是单纯的添加字段,可以直接进行单表添加:

def more_create(request):
#其他字段正常添加,这里添加的是两个表之间的关系
#例如:向给id=3的书,添加作者
'''
第一步:获取该书的对象
第二步: 获取你给书绑定的作者的集合
第三步: 通过书的表对象获取添加多对多表关系的字段用add方式添加
'''
book_obj = Book.objects.get(id=4)
author_obj = Author.objects.all()
#author_obj是一个表集合,所以要加*号
# book_obj.author.add(*author_obj)
#通过remove方法,可以将我们建立的表关系解除
# book_obj.author.remove(*author_obj)
#也可以传人数字,删除的id为时的关联关系,不常用 #同理反向添加也是一样的
author_obj1 = Author.objects.get(id=2)
book_obj1 = Book.objects.all()
author_obj1.book_set.add(*book_obj1)
return HttpResponse("success")

多对多表查询:

def more_to_more(request):
#找书名id为1的作者的名字
book_obj = Book.objects.filter(id=1).values("author__name")
print(book_obj) book_obj2 = Book.objects.filter(id=1).first()
print(book_obj2.author) book_obj3 = Book.objects.get(id=3)
# 这里得到的是一个author的Qureyset的对象
print(book_obj3.author)
# 所以需要对他进行去取值
print(book_obj3.author.all()) # 而反向查找的时候用set的方式查询
author_obj = Author.objects.get(id=3)
print(author_obj.book_set.all())
return HttpResponse("success")

聚合查询和分组查询:

#聚合查询
def aggre_query(request):
#这里是选择对应的作者,查询他所有的出的书的价格
price_avg = Author.objects.filter(id=1).aggregate(Avg("book__price"))
print(price_avg) price_avg2 = Book.objects.all().aggregate(Avg("price"))
price_max = Book.objects.all().aggregate(Max("price"))
price_min = Author.objects.filter(id=1).aggregate(Min("book__price"))
print(price_avg2,price_max,price_min)
return HttpResponse("success")#分组查询
def anno_query(request):
#对作者进行价格的分组,一般分组时会联合聚合函数
price_avg = Author.objects.values("name").annotate(Avg("book__price"))
print(price_avg) return HttpResponse("success")

F查询和Q查询:是一种自定义的查询手段,当聚合和组合无法满足我们时,可以使用F和Q查询

def q_query(request):
#这里得到的是一个Queryset集合
q1 = Author.objects.filter(Q(name__startswith="天")).all()
#可以用逻辑运算符一起作用
q2 = Author.objects.filter(Q(name__startswith="天") | Q(name__startswith="失"))
print(q1.first().name)
return HttpResponse("success")def f_query(request):
#F查询是当你需要对表中的某个字段进行操作时,例如运算等的时候使用
book_obj = Book.objects.update(price=F("price")+100)

admin

admin是django强大功能之一,它能共从数据库中读取数据,呈现在页面中,进行管理。默认情况下,它的功能已经非常强大,如果你不需要复杂的功能,它已经够用,但是有时候,一些特殊的功能还需要定制,比如搜索功能,下面这一系列文章就逐步深入介绍如何定制适合自己的admin应用。

如果你觉得英文界面不好用,可以在setting.py 文件中修改以下选项:

LANGUAGE_CODE = 'zh-hans'

第二步:

admin.site.register(Book)  #将你要使用的数据库进行注册,这是默认样式

第三步:如果希望使用更多的样式,可以进行定制

class Myadmin(admin.ModelAdmin):
list_display = ("title","price")
search_fields = ("title","price")admin.site.register(Book,Myadmin)

定制样式的语句字段:

  list_display:     指定要显示的字段
search_fields: 指定搜索的字段
list_filter: 指定列表过滤器
ordering: 指定排序字段

第二种使用样式方法:不推荐

# @admin.register(Book)
class Myadmin(admin.ModelAdmin):
list_display = ("title","price")
search_fields = ("title","price")#一般是单个表进行样式添加

  

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,105
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,582
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,429
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,200
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,836
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,919