Django框架基本语法(二)
Django 框架(二)
六、 请求与响应
官方文档地址:https://docs.djangoproject.com/en/4.0/ref/request-response/
1、 HttpRequest
1.1 介绍
页面被请求,Django创建一个HttpRequest
对象,该对象里有请求的源数据。django
会加载适当的视图,将HttpRequest
作为第一个参数传递给视图函数,每个视图负责返回一个HttpResponse
对象。
服务器接收到http
协议的请求后,会根据报文创建HttpRequest
对象视图函数的第一个参数是HttpRequest
对象在django.http
模块中定义了HttpRequest
对象的API
1.2 接口
1.3 表单
这里默认会将文件写入相应路径,同时提交表单,推荐使用post方式提交,更加安全
index.html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>form表单</title> </head> <body> <form action="{% url 'student:index' %}" method="post"> {% csrf_token %} <!--如果action为空,则将数据提交给当前页面,使用csrf_token离开解决跨域问题--> <label for="username">用户名</label> <input type="text" name="username" id="username" placeholder="please input your name"/> <label for="pwd">密码</label> <input type="password" name="pwd" id="pwd" placeholder="please input your password"> <input type="submit" value="提交"> </form> </body> </html>
csrf
跨域问题:
django
的配置中设置了跨站请求的限制,默认禁止的状态form
表单发送post请求时,服务端要求客户端加上csrfmiddlewatetoken
字段,该字段的值为当前会话ID
加上一个密钥的散列值。- 防止跨域请求
解决方法:
在
form
表单后边加上{% csrf_token %}
,推荐使用这种方式修改配置文件
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', # 把这一行注释掉,不进行视图的csrf验证,防止其他网站伪造请求进行访问 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
views.py
from django.shortcuts import render from django.http import HttpResponse import json # Create your views here. # path("index/", views.index, name="index") 路由地址 def index(request): """这里的操作只是测试,在实际中会对提交的密码和账户进行校验,成功返回首页面,失败返回登录页面""" if request.method == "POST": # 返回数据 username = request.POST.getlist('username') # 返回列表 pwd = request.POST.getlist('pwd') # 也可以通过get方法来获取,推荐使用getlist() resp = {'code': 200, 'username': username[0], "pwd": pwd[0]} return HttpResponse(json.dumps(resp), content_type="application/json") return render(request, "student/index.html")
总结:
GET
:如其名,是从服务器获取数据,不会更改服务器的状态和数据,在URL中可携带参数POST
:将一定数据发送给服务器,一般都会更改服务器的数据- 相对来说,
POST
提交数据的方式比GET
安全一点
1.4 文件上传
将index.html
修改成
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>form表单</title> </head> <body> <form action="" method="post" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="file" multiple="multiple"> <!--可以进行多文件上传--> <input type="submit" value="提交"> </form> </body> </html>
将views.py
修改成
from django.shortcuts import render from django.http import JsonResponse from CRM.settings import MEDIA_ROOT # 导入配置文件,在配置文件中设置了文件存储路径 # MEDIA_ROOT = os.path.join(BASE_DIR, 'static/media') from datetime import datetime import os # Create your views here. # path("index/", views.index, name="index") 路由地址 def index(request): if request.method == "POST": file = request.FILES.getlist("file") # 建议使用getlist,当然,也可以使用get方法 day_dir_name = datetime.now().strftime("%Y%m%d") # 定义目录名 day_dir = os.path.join(MEDIA_ROOT, day_dir_name) # 设置路径 # 创建路径,存在则不创建 if not os.path.exists(day_dir): os.mkdir(day_dir) for file in files: with open(f"{day_dir}/{file.name}", "wb") as f: for line in file: f.write(line) # 防止文件过大,进行逐行逐行写入 return JsonResponse({"code": 200}, content_type="application/json") return render(request, "student/index.html")
2、 HttpResponse
2.1 常用方法
当进行前后端分离的时候,可以需要将我们的响应数据传递给前端页面,然后由前端页面进行渲染,可以使用类视图,我们暂时学的是前后端不分离,使用context
参数进行上下文管理
同时,响应的更多方法,请到官网进行查看!
2.2 类视图
from django.views import View from django.http import JsonResponse # path("test/", views.ViewTest.as_view(), name="test"), # 注册类视图的方法,这个主要用于前后端分离开发,作为后端的响应 class ViewTest(View): def get(self, request): return JsonResponse({"code": 200, "data": {"page": "1", "title": "test文档"}}) # 将我们后端的数据进行封装成JSON数据后,传递给前端进行渲染 def post(self, request): return JsonResponse({"code": 200, "data": {"name": "lihua", "age": 1}})
2.3 Cookies
HTTP(超文本传输协议)是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。
服务器无法区分请求是哪个客户端发送过来的,故这时候就可以使用Cookies来区分了。客户端向服务器发送请求,服务器在http协议加上请求头,通过响应,传送并保存在客户端,客户端再次访问时,就会自动带上这个cookie。
from django.http import JsonResponse, HttpResponse # 这里我们使用类视图来演示 class ViewTest(View): def get(self, request): if not request.COOKIES.get("name"): # 如果没有cookies,设置cookies, resp = HttpResponse() # 创建一个请求 resp.set_cookie("name", "lihua") # 设置cookies,可以通过 max_age 设置cookies的有效时间,单位是秒 resp["headers"] = "xxxxx" # 设置 Response Headers resp.content = json.dumps({"code": 200, "msg": "登录成功"}) # 设置返回的内容 resp.content_type="application/json" # 设置返回数据的类型 return resp # 返回响应 return JsonResponse({"code": 200})
但是这个cookies没有进行加密,容易篡改,如何解决呢?我们使用session
来进行会话的加密。
3、 Session
浏览器存储cookie的方式不太安全,那有没有更好些的来存储登入状态的方式呢?
使用sesison方式,所有数据存储在服务器端,在客户端cookie中存储session_id
session基于cookie
,request.session.get()
注意:
-
使用前记得查看是否注册
sessions
的app
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', # session的app 'django.contrib.messages', 'django.contrib.staticfiles', ] . 一般来说,
session
的app
的注册好的 -
使用前也需要查看是否添加
session
的中间件MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', # session的中间件 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -
同时,也需要对
session
进行数据库的迁移python manage.py makemigrations sessions # 对sessions数据库进行迁移 python manage.py migrate sessions # 使得迁移生效 数据库中主要存储
session_key
,用来设置cookies
比如,登录状态的保持
def login(request): if request.method == "POST": data = request.POST user = data.get("username") pwd = data.get("password") if user == "kun" and pwd == "1": # 把用户名设置到session中去 request.session.update({ "user": user, # 这个为敏感信息,故需要保存到session中 }) return redirect("student:index") return render(request, "student/login.html")
删除当前的状态:
request.session.flush()
设置会话过期时间:
request.session.set_expiry(1) # 设置过期时间为1秒
通过配置文件来配置会话过期时间:
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得session过期,默认是False SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存 SESSION_COOKIE_AGE = 12.96000 # Session的cookie失效日期,默认是2周
4、 分页器
Django中内置了分页的对象
分页功能位于django.core.paginator
模块。
向Paginator
提供包含一些对象的列表,以及你想每一页显示几条,比如每页5条、10条、20条、100条等等,它就会为你提供访问的一系列API方法,示例如下:
>>> from django.core.paginator import Paginator >>> objects = ['john', 'paul', 'george', 'ringo'] >>> p = Paginator(objects, 2) # 对objects进行分页,虽然objects只是个字符串列表,但没关系,一样用。每页显示2条。 >>> p.count # 对象个数 4 >>> p.num_pages # 总共几页 2 >>> type(p.page_range) # `<type 'rangeiterator'>` in Python 2. <class 'range_iterator'> >>> p.page_range # 分页范围 range(1, 3) >>> page1 = p.page(1) # 获取第一页 >>> page1 <Page 1 of 2> >>> page1.object_list # 获取第一页的对象 ['john', 'paul'] >>> page2 = p.page(2) >>> page2.object_list ['george', 'ringo'] >>> page2.has_next() # 判断是否有下一页 False >>> page2.has_previous()# 判断是否有上一页 True >>> page2.has_other_pages() # 判断是否有其它页 True >>> page2.next_page_number() # 获取下一页的页码 Traceback (most recent call last): ... EmptyPage: That page contains no results >>> page2.previous_page_number() # 获取上一页的页码 1 >>> page2.start_index() # 从1开始计数的当前页的第一个对象 3 >>> page2.end_index() # 从1开始计数的当前页最后1个对象 4 >>> p.page(0) # 访问不存在的页面 Traceback (most recent call last): ... EmptyPage: That page number is less than 1 >>> p.page(3) # 访问不存在的页面 Traceback (most recent call last): ... EmptyPage: That page contains no results
简单地说,使用Paginator分四步走:
- 使用任何方法,获取要展示的对象列表QuerySet
- 将对象列表和每页展示数量传递给Paginator,返回一个分页对象
- 调用该对象的各种方法,获取各种分页信息
- 在HTML模板中,使用上面的分页信息构建分页栏
七、 表单类
1、 普通表单
#!/usr/bin/python3 # -*- coding: UTF-8 -*- __author__ = "A.L.Kun" __file__ = "form.py" __time__ = "2022/8/29 18:44" from django.shortcuts import render from django import forms class RegisterForm(forms.Form): """创建一个普通表单""" username = forms.CharField(label="用户名", max_length=20) pwd = forms.CharField( label="密码", max_length=8, min_length=6, widget=forms.PasswordInput(attrs={ "placeholder": "请输入6~8位的密码" # 设置表单的一些属性,可以设置类名等 }), # 设置输入框类型,默认为文本输入框 error_messages={ "min_length": "密码长度小于六位", "max_length": "密码长度大于八位" }, # 设置报错信息 ) pwd_repeat = forms.CharField( label="密码", widget=forms.PasswordInput(attrs={ "placeholder": "请再次输入密码", }), error_messages={ "min_length": "密码长度小于六位", "max_length": "密码长度大于八位" } # 设置报错信息 ) email = forms.EmailField(required=False) # 邮箱字段 def clean(self): """"扩展校验功能,使得多个字段可以相互检验""" cleaned_data = super(RegisterForm, self).clean() # 继承父类的clean方法 # 扩展校验功能 pwd = cleaned_data.get("pwd") pwd_ = cleaned_data.get("pwd_repeat") if pwd_ != pwd: msg = "两次密码不一致" self.add_error("pwd_repeat", msg) # path("", form.register, name="register") # 注册路由 def register(request): if request.method == "GET": form = RegisterForm() if request.method == "POST": data = request.POST form = RegisterForm(data) # 构建一个填充好数据的对象 if form.is_valid(): # 判断校验是否成功,成功返回True username = form.cleaned_data.get("username", None) # 从校验后的数据中,获取用户名 print("用户名为", username) return render(request, "student/register.html", { "form": form, })
创建表单的参数:
max_length
:最大长度min_length
:最小长度widget
:负责渲染网页上HTML表单的输入元素和提取提交的原始数据attrs
:包含渲染后的Widget将要设置的HTML属性error_messages
:报错信息validators
:验证器可以参考我以前的博客中flask的表单验证,原理大概相同
2、 模型表单
官方文档地址:https://docs.djangoproject.com/zh-hans/4.0/topics/forms/modelforms/
使用示例:
from django.shortcuts import render from django import forms from django.db import models class Student(models.Model): """我们创建的模型类""" name = models.CharField("姓名", max_length=20, ) # verbose_name 用来设置字段前边的标签文字 age = models.SmallIntegerField() SEX_CHOICE = ( [0, "女"], [1, "男"] ) sex = models.SmallIntegerField(choices=SEX_CHOICE, default=1) # 其生成字段后,是一个下拉框 qq = models.CharField(max_length=20, unique=True, error_messages={ "unique": "qq号码重复" # 设置报错信息 }) phone = models.CharField(max_length=20, unique=True) c_time = models.DateTimeField("创建时间", auto_now_add=True) e_time = models.DateTimeField("修改时间", auto_now=True) is_delete = models.BooleanField(default=False) # 逻辑删除,False代表没删除 grade = models.ForeignKey("Grade", on_delete=models.SET_NULL, null=True) def __str__(self): return f"{self.name}-{self.age}" class StudentForm(forms.ModelForm): class Meta: """在这里指定要映射的模型类""" model = Student # 指定我们创建的学生模型 # fields = "__all__" # 所有字段都进行展示 exclude = ["is_delete"] # 指定不需要展示的字段 widgets = { "sex": forms.RadioSelect(attrs={}) # 设置字段类型 } def clean_qq(self): """对单个字段进行校验""" data = self.cleaned_data # 校验后的数据,再对该字段进行校验 if not data.isdigit(): self.add_error("qq", "你输入的qq号码有问题") return data # 要记得将数据返回 def register(request): if request.method == "GET": form = StudentForm() if request.method == "POST": data = request.POST student = None # 获取到学生对象 form = StudentForm(data, instance=student) # 构建一个填充好数据的对象,instance参数,覆盖原来的数据 if form.is_valid(): # 判断校验是否成功,成功返回True username = form.cleaned_data.get("username", None) # 从校验后的数据中,获取用户名 print("用户名为", username) return render(request, "student/register.html", { "form": form, })
表单属性:
from student.form import * forms = StudentForm() for form in forms: print(form.label) # 获取字段对应的标签 print(form.id_for_lable) # 获取标签对应的id form.as_widget(attrs={ "class": "class_str" }) # 使用这个方法,给该字段增加样式、属性 print(form.errors) # 获取报错信息 print(form.value) # 获取该字段的值 # 这些属性可以用来自定义我我们的表单,使其个性化
八、 中间件
django
中的中间件(middleware),在django
中,中间件其实就是一个类,在请求到来和结束后,django
会根据自己的规则在合适的时机执行中间件中相应的方法。
在django
项目的settings
模块中,有一个 MIDDLEWARE_CLASSES
变量,其中每一个元素就是一个中间件
官方网站地址:https://docs.djangoproject.com/zh-hans/4.1/topics/http/middleware/
1、 创建中间件
顾名思义:请求送入之前和响应送出之前对请求和响应进行处理的组件
中间件的命名和存储是可以任意的,只要是可以导入就行
#!/usr/bin/python3 # -*- coding: UTF-8 -*- __author__ = "A.L.Kun" __file__ = "middleware.py" __time__ = "2022/8/30 19:24" """使用装饰器作为中间件""" # 函数中间件 def simple_middleware(get_response): """get_response参数名不可以修改""" print("初始化") def middleware(request): print("视图函数调用之前,进行检验") response = get_response(request) # 视图函数执行 print("视图函数调用之后,进行检验") return response return middleware # 类中间件 class SimpleMiddleware: """也可以使用类来作为装饰器""" print("初始化") def __init__(self, get_response): self.get_response = get_response print("初始化") def __call__(self, request): print("请求前") if "chrome" not in request.META.get("HTTP_USER_AGENT").lower(): "如果不是使用Chrome浏览器,则拒绝访问" return HttpResponseForbidden() response = self.get_response(request) print(response) return response # 将响应返回
2、 注册中间件
我们需要在配置文件中,注册我们自定义的中间件
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', 'student.middleware.SimpleMiddleware', # 注册类中间件 'student.middleware.simple_middleware' # 注册函数中间件 ]
九、 上下文处理器
1、 创建
#!/usr/bin/python3 # -*- coding: UTF-8 -*- __author__ = "A.L.Kun" __file__ = "customer_context.py" __time__ = "2022/8/30 20:08" def my_name(request): return { "name": "fei" # 将name对应的值添加到全局变量中,这个变量可以通过request中的数据进行获取 }
2、 注册
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', 'student.customer_context.my_name', # 注册创建的上下文处理器 ], }, }, ]
所有页面共用的变量,如果同时存在,优先使用context
中的变量
十、 管理系统
1、 配置
要求
- app注册
- 中间件注册
- 路径配置
根据路径访问管理员站点:http://127.0.0.1:1236/admin/
创建管理员账号:在项目根目录下运行shell
python manage.py createsuperuser
根据要求来创建超级用户
2、 管理模型
在student
app中,来注册我们的模型,使得我们可以通过管理员用户来修改:
Django
提供了admin.ModelAdmin
类
通过定义ModelAdmin
的子类,来定义模型在Admin
界面的显示方式
-
列表页属性
list_display
:显示字段,可以点击列头进行排序list_filter
:过滤字段,过滤框会出现在右侧search_fields
:搜索字段,搜索框会出现在上侧list_per_page
:分页,分页框会出现在下侧 -
添加、修改页属性
fields
:属性的先后顺序fieldsets
:属性分组
注意:上面两个属性,二者选一
from django.contrib import admin from .models import Student, StudentDetail # Register your models here. # 自定义 class StudentAdmin(admin.ModelAdmin): list_display = ['id', 'name', 'sex', 'age', 'qq', 'phone'] # 指定显示字段 list_display_links = ['name', 'sex'] # 指定跳转详情字段 search_fields = ['name', 'qq', 'phone'] # 自定义搜索字段 list_filter = ['sex'] # 过滤 list_per_page = 5 # 每页显示数量 # fields = ['age'] # 只允许修改的字段,和字段分组设置二选一 # exclude = [] # 排除字段 # 分组设置 fieldsets = [ (None, {'fields': ['name', 'sex']}), ('详细信息', {'fields': ['age', 'qq', 'phone']}), ('设置', {'fields': ['is_delete']}), ] admin.site.register(Student, StudentAdmin) # 注册自定义模型 admin.site.register(StudentDetail) # 注册模型
3、 auth
3.1 数据表
auth系统中的数据表
User:User
是auth模块中维护用户信息的关系模式(继承了models.Model
), 数据库中该表被命名为auth_user
.
Group:User
对象中有一个名为groups
的多对多字段, 多对多关系由auth_user_groups
数据表维护。Group
对象可以通过user_set
反向查询用户组中的用户。
Permission:Django
的auth
系统提供了模型级的权限控制, 即可以检查用户是否对某个数据表拥有增(add
), 改(change
), 删(delete
)权限。
3.2 验证系统
配置文件讲解:
INSTALLED_APPS = [ 'django.contrib.admin', # 管理系统 'django.contrib.auth', # 权限系统 'django.contrib.contenttypes', # 把权限和模型关联起来 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'student' ] 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', ]
注意:使用权限管理前,需要对模型进行迁移
用户身份验证系统:
用户身份验证系统:
-
身份验证:验证用户是否是用户
-
授权:决定用户做什么
当前登录用户,是一个user对象,或者是一个AnonymousUser
匿名用户实例,它会被存储在模板变量{{ user }}
中
登录功能的实现:
from django.contrib.auth import authenticate # 对用户进行校验 from django.contrib.auth import login as login_ # 进行登录,反正重名 # path("", views.login, name="login") def login(request): # 判断是否登录 if request.user.is_authenticated: # 匿名用户返回false return redirect("student:index") if request.method == "POST": data = request.POST user = data.get("username") pwd = data.get("password") usr = authenticate(username=user, password=pwd) # 如果验证失败,返回空;验证正确,返回一个用户对象 if usr: # 如果验证正确 # 把用户名设置到session中去 login_(request, usr) # 将请求与用户关联起来 return redirect("student:index") return render(request, "student/login.html") # path("/", views.login, name="login") def login_out(request): logout(request) # 登出功能
登录验证:
from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator # 将函数装饰器,转换为方法装饰器 from django.views import View # Create your views here. @method_decorator(login_required) # 进行登录验证 class Register(View): def get(self, request): return "正在注册" @login_required # 对是否登录进行验证,如果没有登录,跳转到登录页面 def index(request): students = None # 声明学生为空,查看post请求是否得到数据 name = request.session.get("user", "游客") search = '' # 默认搜索内容为空 if request.method == "POST": search = request.POST.get("search", "").strip() # 获取到数据 if search: # 如果search有值,则筛选,否则返回全部数据 if search.isdigit(): students = Student.objects.filter(Q(qq=search) | Q(phone=search), is_delete=False) else: students = Student.objects.filter(name=search, is_delete=False) if not students: students = Student.objects.filter(is_delete=False) section = "学生列表" return render(request, "student/index.html", context={ "section": section, "students": students, "search_val": search, "name": name, })
登录页面的路径在配置文件中配置:
from django.urls import reverse_lazy LOGIN_URL = reverse_lazy("student:login") # 懒加载 登录常用接口
create_user 创建用户 authenticate 验证登录 login 记住用户的登录状态 logout 退出登录 is_authenticated 判断用户是否登录 login_required 判断用户是否登录的装饰器 permission_required 权限验证装饰器
3.3 User模型
User模型常用属性和方法:
User
对象有两个多对多字段:groups
和user_permissions
,User
对象可以通过他们访问管理对象
3.4 Group模型
# user 为登录用户 # 组操作 user.groups.add(g2) # 将用户添加到g2组 user.groups.clear() # 清空用户添加的组 user.groups.set([g1, g2]) # 将用户添加到g1, g2组 user.groups.remove(g1, g2) # 将用户从g1, g2组中移除 g1.user_set.add() # 通过反向代理来把用户添加到组中
3.5 Permission模型
# user 为登录用户 # 权限操作 user.user_permissions.set([25,26,27,28]) # 设置 user.user_permissions.remove(25,28) # 移除 user.user_permissions.add(25,28) # 添加 user.user_permissions.clear() # 清空,对应功能的id # 对组进行权限操作 gl.permissions.add(permission) # 添加某个权限 # 查看用户是否有某个app下某个模型的某个操作功能(增add删delete改change查view) user.has_perm('student.view_student') # app.add_模型
权限会被存储在模板变量{{ perms }}
本文来自博客园,作者:Kenny_LZK,转载请注明原文链接:https://www.cnblogs.com/liuzhongkun/p/16644255.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了