小白IT:Django框架 您所在的位置:网站首页 z005怎么查表 小白IT:Django框架

小白IT:Django框架

2023-10-01 19:50| 来源: 网络整理| 查看: 265

文章目录 一、模型层之ORM1.什么是ORM2.ORM和原生sql语句对比python中的模型对象写法 3.settings配置数据库配置连接的数据库(Mysql)修改数据库驱动可视化插件 二、单表操作1.创建模型2.ORM中的字段和参数常用简单字段更多约束参数choices字段约束补充在视图函数中使用 时间字段的独有约束 3.同步指令makemigrations命令migrate命令几个注意事项注意点1:配置应用名称注意点2:mysql版本兼容问题注意点3:设置显示对应sql语句注意点4:mysql严格模式设置 三、单表操作值添加表记录1.添加单条表记录2.批量添加表记录3.update_or_create方法 四、单表操作之查询表(重点)1.常用查询方法all()filter(**kwargs)get(**keargs)exclude(**kwargs)order_by(*field)reverse()count()first()last()exists()values(*field) ***values_list(*field)distinct()小结 2.基于双下滑线的模糊查询filter条件中的模糊查询关于日期查询 五、单表操作之删除记录1.两种对象调用delete2.关于联级删除 六、单表操作之修改记录update()注意

一、模型层之ORM 1.什么是ORM

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

ORM是“对象-关系-映射”的简称;(Object Relational Mapping,简称ORM),ORM是Django中的一个模型,能够独立出来使用的有另一个模块sqlalchemy。

ORM本质就是将类对象的语法翻译成sql语句的一个引擎,我们需要掌握的就是如何使用ORM,写类对象关系语句。

类对象–>sql–>pymysql–>mysql服务端–>磁盘 2.ORM和原生sql语句对比

在这里插入图片描述

mysql中的原生sql语句写法

sql中的表 -- 创建表: CREATE TABLE employee( id INT PRIMARY KEY auto_increment , name VARCHAR (20), gender BIT default 1, birthday DATA , department VARCHAR (20), salary DECIMAL (8,2) unsigned, ); -- sql中的表纪录 -- 添加一条表纪录: INSERT employee (name,gender,birthday,salary,department) VALUES("alex",1,"1985-12-12",8000,"保洁部"); -- 查询一条表纪录: SELECT * FROM employee WHERE age=24; -- 更新一条表纪录: UPDATE employee SET birthday="1989-10-24" WHERE id=1; -- 删除一条表纪录: DELETE FROM employee WHERE name="alex" python中的模型对象写法 from django.db import models # python的类 class Employee(models.Model): id=models.AutoField(primary_key=True) name=models.CharField(max_length=32) gender=models.BooleanField() birthday=models.DateField() department=models.CharField(max_length=32) salary=models.DecimalField(max_digits=8,decimal_places=2) # python的类对象 # 添加一条表纪录: emp=Employee(name="alex",gender=True,birthday="1985-12-12",epartment="保洁部") emp.save() # 查询一条表纪录: Employee.objects.filter(age=24) # 更新一条表纪录: Employee.objects.filter(id=1).update(birthday="1989-10-24") # 删除一条表纪录: Employee.objects.filter(name="alex").delete() 3.settings配置数据库 配置连接的数据库(Mysql)

在settings文件找到DATABASES,注释掉默认的配置,添加你需要连接的mysql数据库。

具体配置参考如下;

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 指定存储数据库 'NAME': 'day057_single_table_lesson', # 指定连接的数据库名 'USER': 'root', # 用户名 'PASSWORD': '123', # 密码 'HOST': 'localhost', # 数据库主机 'PORT': 3306, # 数据库端口 } } 修改数据库驱动

在Django中的默认是使用的是自带的db.sqlite3数据库,用户名和密码也是自动创建的,如果我们需要使用mysql数据库,需要先创建mysql数据库,并激活。

然后启动项目,发现会报错no module named MySQLdb 。这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb 对于py3有很大问题,所以我们需要的驱动是PyMySQL,只需要在项目名文件下的__init__总写入:

import pymysql pymysql.install_as_MySQLdb() 可视化插件

如果我们需要在pycharm中更加直观的查看我们的表结构和表数据,可以在pycharm安装mysql的可视化插件,安装方法如下图,点开后有个download字样,点击安装即可。

配置完这些,就可以愉快的连接mysql数据库玩耍了。

在这里插入图片描述

二、单表操作 1.创建模型

在项目下创建一个名为app01的应用; 在这里插入图片描述

然后在app01下的models.py中创建模型;

from django.db import models # Create your models here. class Book(models.Model): # 创建一个book类 id = models.AutoField(primary_key=True) # 如果表里面没有写主键,表里面会自动生成一个自增主键字段,叫做id字段,orm要求每个表里面必须要写一个主键 title = models.CharField(max_length=32) # 和varchar(32)是一样的,32个字符 pub_date = models.DateField() # 必须存这种格式"2018-12-12" price = models.DecimalField(max_digits=8, decimal_places=2) # max_digits最大位数,decimal_places小数部分占多少位 author = models.CharField(max_length=20,null=True) 2.ORM中的字段和参数

ORM中的字段和mysql中是对应的,每个字段有一些特有的参数,参数使用来限制字段,常用的字段和参数

常用简单字段

charField:字符串字段, 用于较短的字符串.

CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.

IntegerField:用于保存一个整数.

FloatField:一个浮点数.

必须提供两个参数:max_digits:总位数(不包括小数点和符号);decimal_places:小数位数;

AutoField:一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段;

booleanField:一个表示真或者假的字段

TextField:一个带有检查Email合法性的 CharField,不接受 maxlength 参数.

DateField:一个日期字段. 共有下列额外的可选参数:

参数:Argument:描述;auto_now:当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 “last-modified” 时间戳;

DateTimeField:一个日期时间字段. 类似 DateField 支持同样的附加选项.

ImageField:类似 FileField, 不过要校验上传对象是否是一个合法图片。

两个可选参数:height_field和width_field,限制图片的高度和宽度

FileField:一个文件上传字段. 要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径。

注意:在一个 model 中使用 FileField 或 ImageField 需要以下步骤: 1.在你的settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件.(出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对WEB服务器用户帐号是可写的. 2.在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT).出于习惯你一定很想使用 Django 提供的 get__url 函数.举例来说,如果你的 ImageField叫作 mug_shot, 你就可以在模板中以 {{object.#get_mug_shot_url }} 这样的方式得到图像的绝对路径.

URLField:用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且没有返回404响应)。

NullBooleanField: 类似 BooleanField, 不过允许 NULL 作为其中一个选项.。推荐使用这个字段而不要用 BooleanField 加 null=True 选项。

更多约束参数

null:指定字段是否可为空,默认是False

blank:如果为True,该字段允许不填。默认为False。注意与null的区别,两者的范畴不一致,null是数据库范畴的,而blank是orm中数据验证范畴的。

default:字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值。

primary_key:设置主键,如果为True,那么这个字段就是模型的主键。

如果你没有指定任何一个字段的primary_key=True,Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,否则没必要设置任何一个字段的primary_key=True。

unique:如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的

choices:由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。

如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。

db_index:如果db_index=True 则代表着为此字段设置数据库索引。

choices字段约束补充

当我们使用choices字段时,是通过元组的形式来指定选择框的提交值和展示值。

如models中定义一个类

class Student(models.Model): name = models.CharField(max_length=12,null=True) sex_choice = ((1,'男'),(2,'女'),) # 1,2是提交后台的值,男,女是展示给用户看的值 sex = models.IntegerField( choices=sex_choice, ) 在视图函数中使用 def test(request): stu_obj = models.Student.objects.get(name='selfge') # 获取一个学生对象 print(stu_obj .sex) # 1,直接查看数据库中学生的性别是1 print(stu_obj .get_sex_display()) # 男,通过这个方法可以查到对应的显示文本,方便前端展示数据 print(stu_obj .name) # selfge return render(request,'testchoice.html',{'stu_obj ':stu_obj })

模板文件中显示

性别:{{ new_obj.get_sex_display }}

时间字段的独有约束

对于DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性

auto_now_add:配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。auto_now:配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。 3.同步指令

写完models中的代码后,需要通过两条数据库迁移指令在指定的数据库中创建表。

makemigrations命令 python manage.py makemigrations # 生成记录,每次修改了models里面的内容或者添加了新的app,新的app里面写了models里面的内容,都要执行这两条

makemigrations命令用来将models中的创建代码语句提交到migrations文件中生成对应的脚本文件,第一次提交会生成一个名为0001_initial.py的初始化文件,之后的每一次更新执行makemigrations命令都会将更新的操作更新到一个新的文件中,如0002_book_author.py。

migrate命令 python manage.py migrate #执行上面这个语句的记录来创建表,生成的表名字前面会自带应用的名字,例如:你的book表在mysql里面叫做app01_book表

migrate命令会翻译migrations中文件的语句,然后连接到数据库,执行对应的sql语句创建表;

注意:每一次执行migrations中的文件时,都会在django_migrations表中记录,当我们再次执行migrate命令,Django会查看这个记录表,如果发现执行的命令在记录中存在,说明该命令已经被执行过了,不会再被执行。

所以当我们需要重新初始化数据库中的表结构,不仅仅需要删除表名,还需要删除django_migrations中对应的执行记录。 在这里插入图片描述 另外我们可以通过pycharm的功能来执行manage.py相关指令。 在这里插入图片描述 执行这里,我们会调到Django环境的命令行,在里面直接运行makemigrations和migrate命令就行。

几个注意事项 注意点1:配置应用名称

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

INSTALLED_APPS = [ 'django.contrib.admin', # 这是django提供的一些特殊功能的配置(应用,只是咱们看不到),执行同步指令,django会到这里来找到应用的models创建各个应用的表 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', "book" # 直接写app的名字也行,写'app01.apps.App01Config'也行 ] 注意点2:mysql版本兼容问题

如果报如下错误

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

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

通过查找路径C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql,这个路径里的文件把下面代码注释掉就OK了。

if version < (1, 3, 3): raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__) 注意点3:设置显示对应sql语句

如果想打印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', }, } } 

另一种查看orm代码对应的sql语句方式

from app01 import models def add_book(request): ''' 添加表记录 :param request: http请求信息 :return: ''' book_obj = models.Book(title='python',price=123,pub_date='2012-12-12',publish='人民出版社') book_obj.save() from django.db import connection # 通过这种方式也能查看执行的sql语句 print(connection.queries) return HttpResponse('ok') 注意点4:mysql严格模式设置

mysql严格模式可以严格控制输入数据库数据的安全性,我们知道在mysql客户端设置严格模式只需要在mysql环境中执行

select @@sql_mode; --查看当前mysql的模式 set sql_mode="STRICT_TRANS_TABLES" --设置mysql为严格模式,暂时性的。

那么在Django中如何设置mysql的严格模式呢,其实也很简单,只需要在settings配置数据库为mysql的配置中加上一条选项数据就可以了。

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': "book_manage1", "HOST":"127.0.0.1", "PORT":3306, "USER":"root", "PASSWORD":"123", "OPTIONS":{ "init_command":"set sql_mode='STRICT_TRANS_TABLES'", } # 设置mysql为严格模式 } } 三、单表操作值添加表记录

在python中orm的对应关系有三种:

类 ---------->表类对象 ---------->行(记录)类属性 ---------->表的字段(重点)

首先想操作表的增删改查,你需要导入这个表,定义对应函数,通过url访问执行这个函数,如这个例子中我们定义operate函数

# urls.py文件 urlpatterns = [ url(r'^operate/', views.operate), ] # views.py文件 def operate(request): """这里写相关对表的操作""" # 如添加、删除、修改、查询代码 1.添加单条表记录

方式一:用的少

book_boj = models.Book(title="红楼梦",pub_date="2012-12-12",price=500,author="曹雪芹") # pub_date=datetime.date.today()这种方式设置时间数据也可以。 # 实例化一个对象表示一行记录 book_boj.save() # 这个命令就是执行pymysql中的commit命令

方式二:用的多

book_obj = models.Book.objects.create(title="蝶恋花",pub_date="1856-12-6",price=200,author="晏殊") # create方法的返回值book_obj就是插入book表中的蝶恋花这本书籍纪录对象 # Book.objects就像book表的管理器,提供了增删改查的所有方法 2.批量添加表记录

使用bulk_create方法批量添加列表中的对象

import random # 方式3:批量插入 book_list = [] for i in range(10): bk_obj = models.Book( title='枉凝眉%s'%i, pub_date='1846-7-2%s'%(random.randint(0,9)), price=random.randint(80,200), author="一只熊%s"%i ) book_list.append(bk_obj) models.Book.objects.bulk_create(book_list) # 使用bulk_create方法批量添加数据,效率比for循环添加高 3.update_or_create方法

update_or_create方法,有就更新,没有就创建

obj, created = models.Book.objects.update_or_create( id=3, # 查找筛选条件 defaults={ # 更新或添加的数据,有就更新,没有就添加 "title": "红楼梦(下)", "pub_date": "1826-10-8", "price": 250, } )

执行所有插入语句后的表结构如下

mysql> select * from app01_book; +----+--------------------+------------+--------+------------+ | id | title | pub_date | price | author | +----+--------------------+------------+--------+------------+ | 1 | 红楼梦 | 2012-12-12 | 500.00 | 曹雪芹 | | 2 | 蝶恋花 | 1856-12-06 | 200.00 | 晏殊 | | 3 | 红楼梦(下) | 1826-10-08 | 250.00 | 曹雪芹 | | 4 | 蝶恋花 | 1856-12-06 | 200.00 | 晏殊 | | 5 | 枉凝眉0 | 1846-07-29 | 124.00 | 一只熊0 | | 6 | 枉凝眉1 | 1846-07-21 | 147.00 | 一只熊1 | | 7 | 枉凝眉2 | 1846-07-23 | 127.00 | 一只熊2 | | 8 | 枉凝眉3 | 1846-07-29 | 92.00 | 一只熊3 | | 9 | 枉凝眉4 | 1846-07-22 | 187.00 | 一只熊4 | | 10 | 枉凝眉5 | 1846-07-29 | 142.00 | 一只熊5 | | 11 | 枉凝眉6 | 1846-07-21 | 132.00 | 一只熊6 | | 12 | 枉凝眉7 | 1846-07-25 | 191.00 | 一只熊7 | | 13 | 枉凝眉8 | 1846-07-28 | 188.00 | 一只熊8 | | 14 | 枉凝眉9 | 1846-07-22 | 141.00 | 一只熊9 | +----+--------------------+------------+--------+------------+ 14 rows in set (0.00 sec) 四、单表操作之查询表(重点)

model中的表类有一个objects属性,这个objects就好像一个管理器,提供了所有的增删改查方法,下面是常用的查询方法。

注意:model对象和queryset对象区别

model对象就是表中某一行数据对应的数据模型queryset类型是django中的数据类型,里面存放了model对象,可以为空 1.常用查询方法 all()

查询所有的对象结果,返回一个queryset类型,包含所有的model对象

bk_obj = models.Book.objects.all() # 查询所有结果 print(bk_obj) #

注意:如果想显示时,显示特定的特征字段,便于查看,可以在book类中定义__str__方法。

def __str__(self): return str(self.id) +"." + self.title filter(**kwargs)

查询满足筛选条件的model对象结果,返回一个queryset类型。

有多个条件使用逗号分隔,条件之间是and的关系,这里无法实现or的关系

bk_obj = models.Book.objects.filter(title="红楼梦",price=500) print(bk_obj) # get(**keargs)

查询满足筛选条件的model对象,返回model对像,也就是行记录

注意:

get的查询结果有且只有一个。多余一个报错:get() returned more than one Book – it returned 3!没有也报错:Book matching query does not exist. bk_obj = models.Book.objects.get(title="枉凝眉1") print(bk_obj) # 6.枉凝眉1,这里返回的是model对象,行记录 exclude(**kwargs)

查询排除了满足筛选条件的所有结果对象,返回一个queryset类型

参数可以为多个,排除满足任意筛选条件的对象

bk_obj = models.Book.objects.exclude(title="蝶恋花",price=127) print(bk_obj) # order_by(*field)

由queryset类型的对象来调用,对查询的结果排序,默认按照id的升序,返回仍是queryset类型。

排序默认是升序,如果需要降序,在字段前面添加"-"即可。

bk_obj = models.Book.objects.filter(author="曹雪芹").order_by("-id","price") # 查询作者为“曹雪芹”的书籍,按照id的降序,价格的升序排列 print(bk_obj) # reverse()

只能有order_by查询返回的queryset类型对象调用,对查询的结果反转。

bk_obj = models.Book.objects.filter(author="曹雪芹").order_by("-id","price").reverse() print(bk_obj) # count()

由queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量。

bk_obj = models.Book.objects.filter(author="曹雪芹").count() print(bk_obj) # 2 first()

由queryset类型的数据来调用,返回第一个model对象,也就是第一条记录。等效于索引取值[0]

bk_obj = models.Book.objects.filter(author="曹雪芹").first() print(bk_obj) # 1.红楼梦 等效于取索引[0] last()

由queryset类型的数据来调用,返回最后一条记录

exists()

由queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False

bk_obj = models.Book.objects.filter(author="曹雪芹").exists() print(bk_obj) # True

注意exists与直接对结果判断的区别

exists是取查询结果的第一条来判断是否有值,效率高直接判断结果,会遍历所有查询结果,效率低 values(*field) ***

较常用,由queryset类型的对象来调用,返回一个ValueQuerySet对象,一个特殊的queryset,结果是一个可迭代的列表嵌套字典序列

只要是返回queryset类型的对象,就可以继续链式调用queryset对象的其他查找方法。

bk_obj = models.Book.objects.filter(author="曹雪芹").values("id","title") print(bk_obj) # values_list(*field)

与values()非常相似,只不过values_list返回的是一个列表嵌套元组序列,values返回列表嵌套字典序列

bk_obj = models.Book.objects.filter(author="曹雪芹").values_list("id", "title") print(bk_obj) # distinct()

对values()和values_list()方法得到的queryset类型数据去重。

注意:

如果表中没有主键,django也会默认生成id为主键,所以我们直接对所有查询结果去重是毫无意义的。

all_books = models.Book.objects.all().distinct() # 不会有重复项

使用方法

bk_obj = models.Book.objects.all().values_list("title","price").distinct() # title和price两个同时重复才算一条重复的记录 print(bk_obj) # 结果中蝶恋花被去重了 小结

只有get、first、last方法拿到的是一个model对象,其他方法拿到的都是queryset类型。

2.基于双下滑线的模糊查询 filter条件中的模糊查询

使用双下划线对条件中的字段进行条件约束

语法:字段名__模糊条件

models.Book.objects.filter(price__in=[250,500]) # price值等于这三个里面的任意一个的对象 models.Book.objects.filter(price__gt=100) # bigthan,大于,大于等于是price__gte=100,别写price>100,这种参数不支持 models.Book.objects.filter(price__lt=100) # smallthan,小于 models.Book.objects.filter(price__range=[100,200]) # sql的between and,大于等于100,小于等于200 models.Book.objects.filter(title__contains="红楼") # title值中包含“红楼”字样 models.Book.objects.filter(title__icontains="python") # 不区分大小写 models.Book.objects.filter(title__startswith="红") # 以什么开头;istartswith,不区分大小写 models.Book.objects.filter(pub_date__year=2012) # 日期数据年份是2012的 关于日期查询 models.Book.objects.filter(pub_date__year=2012) # 年份为2012 models.Book.objects.filter(pub_date__year__gt=2012) # 年份大于2012 models.Book.objects.filter(pub_date__year=2019, pub_date__month=2) # 年份为2019,月份为2月 五、单表操作之删除记录

python中删除一条记录的方法是delete(),而删除记录的调用者可以是一个model对象,也可以是一个queryset集合。

delete运行时会立即删除对象,没有任何返回值。

1.两种对象调用delete

1.model对象调用删除方法,会删除单个model对象

bk_obj = models.Book.objects.get(id=14) # 找到model对象 bk_obj.delete() print(bk_obj) #

2.queryset类型调用删除方法,可以一次删除queryset中的所有对象

bk_obj = models.Book.objects.filter(id__in=[13,14]) bk_obj.delete() print(bk_obj) # 2.关于联级删除

如果涉及到外键,django中的外键会默认设置联级关系,on delete cascade;也就是说删除一个对象会删除与它相关的外键对象。

obj = models.Book.objects.get(pk=1) # This will delete the Blog and all of its Entry objects. obj.delete()

delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Entry.objects.delete() 方法导致 所有的 记录被误删除。

如果你确认要删除所有的对象,那么你必须显式地调用;

Entry.objects.all().delete()

如果不想联级删除,可以在settings中设置

pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True) 六、单表操作之修改记录 update()

models中修改表记录使用update方法;

update方法只能由queryset类型的对象调用,意思是你可以同时更新多条记录,update方法会返回一个整型数值,表示收到影响的记录条数。

bk_obj= models.Book.objects.filter(title__startswith="蝶恋").update(price=150) # 更新所有名字以“蝶恋”开头的数据价格为150 print(bk_obj) # 2 注意

type = “date” 的input标签,value的值必须是’Y-m-d’的格式,这个标签才能认识并被赋值,所以,要通过date过滤给它改变格式。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有