分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > IT知识

Django开发:(1)django基础 & url控制器

发布时间:2023-09-06 01:58责任编辑:胡小海关键词:urldjango

HTTP请求协议

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文本的传送协议。http协议是基于TCP/IP协议之上的应用层协议

请求协议:

  

请求方式: get与post请求
  1. GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中.
  2. GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
  3. GET与POST请求在服务端获取请求数据方式不同

响应协议:

  

响应状态码

  状态码的值 是当客户端向服务器端发送请求时, 返回的请求 结果。借助状态码,用户可以知道服务器端是正常 理了请求,还是出 现了 。

wsgiref模块

WSGI:Web Server Gateway Interface。wsgiref模块是python基于wsgi协议开发的服务模块。

from wsgiref.simple_server import make_serverdef application(environ,start_response): ???# wsgi做的两件事情: ???# 1. 按照HTTP请求协议解析数据:environ参数是个字典; ???# 2. 按照HTTP响应协议封装数据:start_response ???# 当前请求路径 ???path = environ.get(‘PATH_INFO‘) ???start_response(‘200 OK‘,[(‘Content-Type‘,‘text/html‘)]) ?# 第一个参数是响应头(字符串);第二个是响应首行(列表) ???print(path) ???# 动态匹配 ???if path == "/login": ???????with open("login.html","rb") as f: ???????????data = f.read() ???????????return [data] ?# return的格式是列表 ???elif path == "/index": ???????with open("index.html","r") as f: ???????????data = f.read() ???????????return [data.encode("utf-8")] ?# return的格式是列表 ???# start_response中的元素和return的内容共同组成了响应体 ???ret = "<h2>hello world!</h2>".encode(‘utf-8‘) ???return [ret]# 封装好的sockethttped = make_server("127.0.0.1",9001,application)# 等待用户连接;相当于 accept()方法;开始监听HTTP请求httped.serve_forever() ?# 一旦有用户连接,便会执行 application函数

DIY一个Web框架

from wsgiref.simple_server import make_serverdef login(environ): ?# environ 需要传入函数中,因为好多时候需要读取请求方式 ???with open("login.html","rb") as f: ???????data = f.read() ???return datadef fav(environ): ???with open("favicon.ico", "rb") as f: ???????data = f.read() ???return datadef application(environ, start_response): ???start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)]) ???path = environ.get("PATH_INFO") ???# 方案一 ???# # favicon.ico是网页的图标(浏览器默认会多发一次请求获取favicon.ico) ???# if path == "/favicon.ico": ???# ????with open("favicon.ico","rb") as f: ???# ????????data = f.read() ???# ????return [data] ???# ???# elif path == "/login": ???# ????with open("login.html","rb") as f: ???# ????????data = f.read() ???# ????return [data] ???# ret = "<h2>hello web!</h2>".encode(‘utf-8‘) ???# return [ret] ???# 方案二:路由分发 ???url_pattern = [ ???????("/favicon.ico",fav), ???????("/login",login) ???] # 列表中放元组,元组中放路径和视图函数 ???func = None ???for item in url_pattern: ???????if path == item[0]: ???????????func = item[1] ???????????break ???if func: ???????return [func(environ)] ???else: ???????return [b‘404!‘]httpd = make_server(‘‘, 8080, application)print(‘Serving HTTP on port 8080...‘)# 开始监听HTTP请求:httpd.serve_forever()

上述代码可以从耦合性上优化如下

main.py

from wsgiref.simple_server import make_serverdef application(environ, start_response): ???start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)]) ???path = environ.get("PATH_INFO") ???from urls import url_pattern ???func = None ???for item in url_pattern: ???????if path == item[0]: ???????????func = item[1] ???????????break ???if func: ???????return [func(environ)] ???else: ???????return [b‘404!‘]httpd = make_server(‘‘, 8080, application)print(‘Serving HTTP on port 8080...‘)# 开始监听HTTP请求:httpd.serve_forever()

urls.py

from views import *# 专门处理路径url_pattern = [ ???????("/favicon.ico",fav), ???????("/login",login) ???]

views.py

# 专门处理视图分发功能def login(environ): ?# environ 需要传入函数中,因为好多时候需要读取请求方式 ???with open("templates/login.html","rb") as f: ???????data = f.read() ???return datadef fav(environ): ???with open("templates/favicon.ico", "rb") as f: ???????data = f.read() ???return data

一个简单Web框架组成:

main.py :启动文件,封装了socket

1. urls.py :路径与视图函数映射关系 ------ url控制器

2. views.py :视图函数,固定有一个形式参数:environ -----视图函数

3. templates文件夹:html文件 -----模板

4. models.py:在项目启动前,在数据库中创建表结构 ----与数据库相关

Django

知识预览

MTV:Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:

  • M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM);
  • T 代表模板 (Template):负责如何把页面展示给用户(html);
  • V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。

除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示:

一般是用户通过浏览器向我们的服务器发起一个请求(request),这个请求回去访问视图函数,(如果不涉及到数据调用,那么这个时候视图函数返回一个模板也就是一个网页给用户),视图函数调用模型,模型去数据库查找数据,然后逐级返回,视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。

Django的基本命令

1. 创建一个django project:django-admin.py startproject mysites

当前目录下会生成mysite的工程,目录结构如下:

  • manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等;
  • settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量;
  • urls.py ----- 负责把URL模式映射到应用程序。

2.在mysite目录下创建应用:python manage.py startapp blog

3.启动django项目:python manage.py runserver 8080

Django简单示例:静态文件配置

目录结构如下:

urls.py

"""demo 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 adminfrom django.urls import pathfrom app01 import viewsurlpatterns = [ ???path(‘admin/‘, admin.site.urls), ???path(‘timer/‘, views.timer),]

views.py

from django.shortcuts import render# Create your views here.def timer(request): ???import time ???ctime = time.time() ???return render(request,"timer.html",{"ctime":ctime}) ???# render方法用于发送响应页面;第三个参数字典会嵌入到第二个参数的页面中

settings.py

"""Django settings for demo project.Generated by ‘django-admin startproject‘ using Django 2.0.1.For more information on this file, seehttps://docs.djangoproject.com/en/2.0/topics/settings/For the full list of settings and their values, seehttps://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 = ‘(+nwjycw@5azl7tpxu^)6uf_z@e#(zg6fzwce@%1qxqu-1_(mb‘# 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 = ‘demo.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 = ‘demo.wsgi.application‘# 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‘), ???}}# 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路径是Django提供给我们的用于代指下面拼出的绝对路径;即使urls.py中没有静态文件夹(static),client浏览器也能通过static来获取static文件夹中的文件;(静态文件夹都放在static中)# STATICFILES_DIRS 一个字都不能错STATICFILES_DIRS = [ ???os.path.join(BASE_DIR,"static"),]# 上面两个要配合使用

timer.html

<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<title>Title</title> ???<link rel="stylesheet" href="/static/app01/timer.css"></head><body><h4>当前时间:{{ ctime }}</h4>{#嵌入页面的内容需要用两个大括号包裹,参数对应的是字典中的key#}</body>{#通过/static/来引入静态文件夹中的静态文件#}<script src="/static/jquery-3.3.1.js"></script><script src="/static/app01/timer.js"></script></html>

timer.css

h4{ ???color:red; ???????}

timer.js

$(function () { ???$("h4").click(function () { ???????$(this).css("color","green") ???})})

路由控制

Django1.0:re_path(url)

1.无名分组、有名分组&分发

目录结构:

路由分发.urls.py

"""路由控制 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 adminfrom django.urls import path,re_path,includefrom app01 import viewsurlpatterns = [ ???path(‘admin/‘, admin.site.urls), ???# 分发:全局分发器;用于把路径分发到不同的应用(application)里面 ???re_path(r"app01/",include("app01.urls")) ???# include()是分发函数;如果用户访问的是 app01, include里面的 app01.urls这个py文件就能分发下去;此时用户输入的url中需要有 /app01/ ,e.g. http://127.0.0.1:8000/app01/articles/2003/10/123/ ???# 如果想把路径中的 /app01/ 去掉,如下: ???# re_path(r"^",include("app01.urls"))]

app01/urls.py

from django.contrib import adminfrom django.urls import path,re_pathfrom app01 import viewsurlpatterns = [ ???path(‘admin/‘, admin.site.urls), ???# 路由配置:路径 ----> 视图函数 ???re_path(r‘^articles/2003/$‘, views.special_case_2003), ???# ^articles/2003/$ :正则匹配;匹配以articles/2003/开头、以articles/2003/结尾的路径;唯一匹配 ???re_path(r‘^articles/([0-9]{4})/$‘, views.year_archive), ???# ([0-9]{4}) 是一个分组匹配(加了括号);匹配到路径后,request会传入 year_archive 函数的第一个参数,分组匹配结果会以位置参数传入到year_archive函数的第二个参数, e.g. year_archive(request,1999);so year_archive函数需要有两个参数 ???# 从上到下执行,所以如果匹配到了2003,会走第一个路径,下面的不再执行 ???# 匹配分组之后,视图函数一定要传入相应的位置参数 ???re_path(r‘^articles/([0-9]{4})/([0-9]{2})/$‘, views.month_archive), ???# 同理, month_archive需要有三个参数 ???re_path(r‘^articles/(?P<y>[0-9]{4})/(?P<m>[0-9]{2})/(?P<c>[0-9]+)/$‘, views.article_detail), ???# (?P<名字>):这是有名分组(就是给每个组取了个名字,用的比较多),有名分组利用的是关键字传参; ???# 有名分组取的名字一定要和后面函数的形参相同;有名分组传参不依赖于位置顺序]

views.py

from django.shortcuts import render,HttpResponse# Create your views here.def special_case_2003(request): ?# request指请求对象 ???return HttpResponse("special_case_2003") ???# HttpResponse 指响应对象;参数是字符串,响应体的内容def year_archive(request,year): ???return HttpResponse("year_archive_%s"%year)def month_archive(request,year,month): ???return HttpResponse("month_archive_%s_%s"%(year,month))def article_detail(request,c,m,y): ?# 形参位置顺序无所谓 # y,m,c都是字符串;通过re_path(url)传过来的参数都是字符串格式 ???return HttpResponse(y+"-"+m+"-"+c)

2.反向解析:

反向解析一:在模板中

urls.py

"""路由控制之反向解析 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 adminfrom django.urls import pathfrom app01 import viewsurlpatterns = [ ???path(‘admin/‘, admin.site.urls), ???path("login/",views.login,name="log") ?# name=log 是这个url的别名(反向解析); path()的第一个参数是接口,用户需要知道]

app01/views.py

from django.shortcuts import render,HttpResponse# Create your views here.def login(request): ???# request 是请求对象;所有的请求信息都在request里面 ???print(request.method) ???# request.method 表示获取 请求方式(GET/POST) ???if request.method == "GET": ???????print(request.GET) ???????# request.GET 表示获取所有的 GET请求数据;字典的形式 ???????# <QueryDict: {}> ???????return render(request, "login.html") ???????# 反向解析的过程:用户通过 /login/ 这个接口 到达urls.py,然后通过 path("login/",views.login,name="log") 到达 views.py; ???????# render(request, "login.html") 方法在渲染 login.html 这个页面的时候,会在 action="{% url ‘log‘ %}" 这一步 找到别名为 "log" 的url,并将 {% url ‘log‘ %} 替换为该url ???else: ???????print(request.POST) ???????# request.PSOT 表示获取所有的 POST请求数据;字典的形式 ???????# < QueryDict: {‘username‘: [‘123‘], ‘psw‘: [‘123‘]} > ???????# 获取请求体(POST) ???????user = request.POST.get("username") ???????psw = request.POST.get("psw") ???????if user == "abc123" and psw == "abc123": ???????????# 每次请求一定要有 返回值 ???????????return HttpResponse("成功") ???????else: ???????????return HttpResponse("用户名密码错误")

login.html

<!DOCTYPE html><html lang="en"><head> ???<meta charset="UTF-8"> ???<title>Title</title></head><body>{#action如果不写IP和Port,默认是发送到当前的IP端口#}{#<form action="http://127.0.0.1:8000/login/" method="post">#}{#{% url "log" %} 反向解析的语法#}{#通过这种反向解析方法,不管 urls.py 中的路径接口如何改变,action中的路径能始终自动与其保持一致#}<form action="{% url ‘log‘ %}" method="post"> ???用户名:<input type="text" name="username"> ???密码:<input type="password" name="psw"> ???<input type="submit"></form></body></html>

反向解析二:在python脚本的视图函数中(views.py)

以“1.无名分组、有名分组&分发”中的代码为例:

app01/urls.py

from django.contrib import adminfrom django.urls import path,re_pathfrom app01 import viewsurlpatterns = [ ???path(‘admin/‘, admin.site.urls), ???# 路由配置:路径 ----> 视图函数 ???re_path(r‘^articles/2003/$‘, views.special_case_2003,name="s_c_2003"), ???# ^articles/2003/$ :正则匹配;匹配以articles/2003/开头、以articles/2003/结尾的路径;唯一匹配 ???re_path(r‘^articles/([0-9]{4})/$‘, views.year_archive,name="y_a"), # 通过 name="y_a" 给这个url起一个别名]

app01/views.py

from django.shortcuts import render,HttpResponse# Create your views here.from django.urls import reversedef special_case_2003(request): ?# request指请求对象 ???# 在视图函数中也可以反向解析获取其他的url ???url1 = reverse("s_c_2003") ?# 反向解析 ???url2 = reverse("y_a",args=("1999",)) ???# 由于别名为 "y_a" 的那个url含有正则匹配的分组,而我们指向解析出来的应该是一个具体的路径,而不是一个匹配规则,所以在reverse("y_a",args=("1999",)) 中需要手动添加一个元组如args=("1999",),元组中的元素得满足 该url的正则匹配规则 ???print(url1) ???print(url2) ???return HttpResponse("special_case_2003") ???# HttpResponse 指响应对象;参数是字符串,响应体的内容

3.反向解析之名称空间

命名空间(英语:Namespace)是表示标识符(如:路径的别名name属性)的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其它命名空间中。

我们在开发项目时,会经常使用name属性反解出URL,当不小心在不同的app的urls中定义相同的name时,可能会导致URL反解错误,为了避免这种事情发生,引入了命名空间。

project的urls.py:

from django.contrib import adminfrom django.urls import path,re_path,includeurlpatterns = [ ???path(‘admin/‘, admin.site.urls), ???# 分发:全局分发器;用于把路径分发到不同的应用(application)里面 ???re_path(r"^app01/",include(("app01.urls","app01"))), ???re_path(r"^app02/",include(("app02.urls","app02"))) # 元组的第二个参数表示该url的名称空间]

app01.urls:

from django.contrib import adminfrom django.urls import path,re_pathfrom app01 import viewsurlpatterns = [ ???path(‘admin/‘, admin.site.urls), ???re_path(r"index/",views.index,name="index") # name为路径的别名]

app01.views

from django.shortcuts import render,HttpResponse# Create your views here.from django.urls import reversedef index(request): ???return HttpResponse(reverse("app01:index")) # 反向解析的时候,reverse("app01:index")中 app01:index 表示 app01这个名称空间中的别名 index

app02.urls:

from django.contrib import adminfrom django.urls import path,re_pathfrom app02 import viewsurlpatterns = [ ???path(‘admin/‘, admin.site.urls), ???re_path(r"index/",views.index,name="index")]

app02.views

from django.shortcuts import render,HttpResponse# Create your views here.from django.urls import reversedef index(request): ???return HttpResponse(reverse("app02:index"))

django2.0版的path

一个简单的基本事例:

from django.urls import path ?from . import views ?urlpatterns = [ ?????path(‘articles/2003/‘, views.special_case_2003), ?????path(‘articles/<int:year>/‘, views.year_archive), ?????path(‘articles/<int:year>/<int:month>/‘, views.month_archive), ?????path(‘articles/<int:year>/<int:month>/<slug>/‘, views.article_detail), ?]
  • 使用尖括号(<>)从url中捕获值;<>就代表一个分组;

  • 捕获值中可以包含一个转化器类型(converter type),比如使用 <int:name> 捕获一个整数变量。若果没有转化器,将匹配任何字符串,当然也包括了 / 字符;

  • 无需添加前导斜杠。

path转化器:Django默认支持以下5个转化器:

  • str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
  • int,匹配正整数,包含0。
  • slug,匹配字母、数字以及横杠、下划线组成的字符串
  • path,匹配任何非空字符串,包含了路径分隔符
  • uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。

path的自定义转化器:

对于一些复杂或者复用的需要,可以定义自己的转化器。

(1)需要自定义一个path转化器,如下:

app01/urlconvert.py

# 自定义path转化器class FourDigitYearConvert: ???regex = "[0-9]{4}" # 匹配规则 ???def to_python(self,value): ???????return int(value) ???def to_url(self,value): ???????return "%04d" %value

(2)使用register_converter在全局urls.py中注册该path转化器

urls.py

from django.contrib import adminfrom django.urls import path,re_path,include,register_converterfrom app01.urlconvert import FourDigitYearConvertregister_converter(FourDigitYearConvert,"yyyy") ?# 把自定义的path转化器注册一下;第二个参数是为该转化器起的一个别名,后面用的时候就用这个别名from app01 import viewsurlpatterns = [ ???path(‘admin/‘, admin.site.urls), ???path("articles/<yyyy:year>",views.path_year), # 调用该自定义的path转化器]

转化器是一个类或接口,它的要求有三点:

  • regex 类属性,字符串类型
  • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中
  • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。

Django开发:(1)django基础 & url控制器

原文地址:https://www.cnblogs.com/neozheng/p/9125700.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved