开始

建立django文件。

python -m venv venv #创建venv文件

venv/Scripts/activate # 进入activate文件

deactivate 退出虚拟环境

pip install django==4.1 -i Simple Index 下载django资源

建立django项目(django-admin 命令 像pip类似)

运行django demo文件:django-admin startproject demo

cd demo

运行py文件:python manage.py runserver

models

model子类模版

from django.db import models

from utils.basemodel import BaseModel

# Create your models here.

class User(BaseModel):

# primary_key 主键 max_length 字符串长度 null 数据是否可以设置为空值 blank 是否显示为空 unique 是否是唯一值

id = models.IntegerField(primary_key=True)

username = models.CharField('用户名',max_length=30,null=True,blank=True,unique=True)

password = models.CharField('密码',max_length=30)

email = models.EmailField('邮箱',unique=True,null=True,blank=True)

class Meta:

# 给表命名

db_table = 'User'

verbose_name = '用户信息'

basemodel父类模版

from django.db import models

class BaseModel(models.Model):

# auto_now 默认时间 editable 可以填可以不填

Created_at = models.DateTimeField('创建时间', auto_now_add=True,editable=True)

updated_at = models.DateTimeField('更新时间', auto_now=True,editable=True)

# 设置为抽象类

class Meta:

abstract = True

增删改查

python manage.py shell --启动shell

from account.models import User 导入数据表

(1)

user_obj = User(id = '1',username='wjy',passwor='123',email='123@qq.com')

user_obj.save()

(2)

user = User.objects.create(id = '1',username='wjy',passwor='123',email='123@qq.com')

user1 = User(id = '1',username='wjy1',password='123',email='1123@qq.com')

user2 = User(id = '2',username='wjy2',password='123',email='2123@qq.com')

user3 = User(id = '3',username='wjy3',password='123',email='323@qq.com')

list = [user1,user2,user3]

user = User.objects.bulk_create(list)

(1)

user.objects.all()

(2)

User.objects.get(username='wjy')

(3)

User.objects.first()

User.objects.first()

(4)查询多条记录(所有id大于1的记录)

User.objects.filter(id__gt=1)

查询条件利用上面返回1条或者多条数据做条件返回。

多条件查询

User.objects.filter(created_at__minute=44).filter(created_at__minute=11)

修改数据

user = User.objacts.filter(username='wjy').update(password='222',email='123.qq.com')

数据删除

user = User.objacts.filter(username='wjy')

user.delete()

django管理后台

注册管理员:python manage.py createsuperuser

配置后台中文和时间 settings.py

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

展示字段在admin.py中设置

from django.contrib import admin

from account.models import User

# 自定义函数(本表)

# def get_author(obj):

# return obj.user.username

# 自定义函数(关联表中)

# get_author.short_descriton = '作者'

# Register your models here.

class UserAdmin(admin.ModelAdmin):

# 配置展示列表

list_display = ('id','username','email')

# 展示的时候添加关联表的字段 上面还需要做自定义函数

# list_display = ('id',get_author,'username','email')

# 属性可以点击修改

list_display_links = ('id','username','email')

# 配置过滤字段

list_filter = ('username','email')

# 配置搜索信息

search_fields = ('username','email')

# 只读属性

readonly_fields = ('id',)

admin.site.register(User, UserAdmin)

app.py

from django.apps import AppConfig

class AccountConfig(AppConfig):

default_auto_field = 'django.db.models.BigAutoField'

name = 'account'

verbose_name = '用户管理'

企业官网制作

企业管理项目:

建立项目

根据后台建立文件-----配置文件 setting: INSTALLED_APPS、DATABASES、LANGUAGE_CODE、TIME_ZONE、STATICFILES_DIRS

调试url-------创建index.html

连通数据库------数据迁移

创建管理后台账号密码 createsuperuser

复制静态界面-----更改静态界面配置 {% load static %}

根据静态界面创建后台数据表

创建表:models.py 创建后台上传数据表:admin.py

显示动态照片:

from django.conf import settings

from django.conf.urls.static import static

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

补充

创建数据表

class News(models.Model):

id = models.AutoField(primary_key=True)

title = models.CharField('标题',max_length=100)

content = RichTextUploadingField()

cover = models.ImageField('封面',upload_to='news')

created_at = models.DateTimeField('创建时间',auto_now_add=True,)

uped_at = models.DateTimeField('创建时间',auto_now=True,editable=True)

# categrory = models.ForeignKey(Categrory, on_delete=models.CASCADE)

categrory = models.ForeignKey(Categrory, on_delete=models.CASCADE)

class Meta:

db_table = 'news'

verbose_name= '新闻管理'

verbose_name_plural = '新闻管理'

def __str__(self):

return self.title

寻找到图片的位置

from django.conf import settings

from django.conf.urls.static import static

# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

STATIC_URL = '/static/'

STATICFILES_DIRS = [

os.path.join(BASE_DIR, 'static')

]

# MEDIA_ROOT = os.path.join(BASE_DIR,'static')

# 配置media

MEDIA_URL = 'media/'

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# 配置ckeditor

CKEDITOR_UPLOAD_PATH = 'upload/'

CKEDITOR_IMAGE_BACKEND = 'pillow'

学生系统

班级管理

grades关键点

实现功能:建立model中的数据表;urls开启三个链接,一个是默认显示列表数据,使用继承django的ListView视图类,一个是新增功能url,继承使用CreateView类,一个是修改类型url,继承UpdateView类。引入form表单,重写继承modelform,可以少些一些参数。

model

from django.db import models

# Create your models here.
class Grade(models.Model):
    grade_name = models.CharField(max_length=50, unique=True,verbose_name='班级名称')
    grade_number = models.CharField(max_length=10, unique=True, verbose_name='班级编号')

    def __str__(self):
        return self.grade_name
    
    class Meta:
        db_table = 'grade'
        verbose_name = '班级'
        verbose_name_plural = "班级"
    

urls

from django.urls import path,include
# 引入类视图
from .views import GradeListView,GradeCreateView,GradeUpdateView,GradeDeleteView

urlpatterns = [
    # 引用视图类,并给它命名
    path('', GradeListView.as_view(), name='grades_list'),
    path('create/',GradeCreateView.as_view(), name='grade_create'),
    path('<int:pk>/update/',GradeUpdateView.as_view(), name='grade_update'),
    path('<int:pk>/delete/',GradeDeleteView.as_view(), name='grade_delete'),
]

view

from django.shortcuts import render
# 引入列表视图
from django.views.generic import ListView,CreateView,UpdateView,DeleteView
# 引入表
from .models import Grade
from django.db.models import Q
from .forms import GradeForm
# 引入跳转函数
from django.urls import reverse_lazy

# Create your views here.
class GradeListView(ListView):
    # 展示表
    model = Grade
    # 展示html的路径
    template_name = 'grades/grades_list.html'
    # 展示的表中字段
    fields = ['grade_name', 'grade_number']
    # 相当于render传递数据
    context_object_name = 'grades'
    # 设置页面展示数目
    paginate_by = 5

# 文件创建:设定总的grades路由,因为有多个访问路径(增/改等链接),所以在grades下面做urls视图,
# 视图继承现有ListView列表类,设置列表类就可以访问template_name下的路径了

    def get_queryset(self):
        # 继承写queryset方法
        queryset = super().get_queryset()
        # 判断url获取search参数
        search = self.request.GET.get('search')
        if search:
            # 引入Q函数,判断是否包含search中的内容。
            queryset = queryset.filter(
                Q(grade_name__icontains=search) |
                Q(grade_number__icontains=search)
                )
        return queryset

class GradeCreateView(CreateView):
    model = Grade
    template_name = 'grades/grade-form.html'
    # 引用表单类
    form_class = GradeForm
    # 成功后跳转  要求表单提交成功后必须设置
    success_url = reverse_lazy('grades_list')

class GradeUpdateView(UpdateView):
    model = Grade
    template_name = 'grades/grade-form.html'
    # 引用表单类
    form_class = GradeForm
    # 成功后跳转  要求表单提交成功后必须设置
    success_url = reverse_lazy('grades_list')

class GradeDeleteView(DeleteView):
    model = Grade
    template_name = 'grades/grade_delete.html'
    success_url = reverse_lazy('grades_list')

form

from django import forms
from .models import Grade

# 重写modelform可以少些一些属性
class GradeForm(forms.ModelForm):

    class Meta:
        # 两个必要参数
        # 表
        model = Grade
        # 字段
        fields = ['grade_name','grade_number'] 

记录关键部分

搜索框和下拉菜单  79

前端html  form表单:

        <div class="class-info">
            <form method="get" action="/students/">
                <span>班级: </span>
                <select name="grade">
                    <option value="" selected="">请选择班级</option>
                        {% for grade in grades %}
                            <option value="{{ grade.pk }}" {% if grade.pk|stringformat:"s" == current_grade %} selected {% endif %} >
                                {{ grade.grade_name }}
                            </option>
                        {% endfor %}
                </select>
                <span>姓名/学号:</span>
                <input type="text" name="search">
                <input type="submit" value="搜索">
            </form>
        </div>
{% extends 'base.html' %}

{% load url_utils %}

{% block content %}
<div class="right">
  <div class="top">
    <div class="tool">
        <div class="class-info">
            <form method="get" action="/students/">
                <span>班级: </span>
                <select name="grade">
                    <option value="" selected="">请选择班级</option>
                        {% for grade in grades %}
                            <option value="{{ grade.pk }}" {% if grade.pk|stringformat:"s" == current_grade %} selected {% endif %} >
                                {{ grade.grade_name }}
                            </option>
                        {% endfor %}
                </select>
                <span>姓名/学号:</span>
                <input type="text" name="search">
                <input type="submit" value="搜索">
            </form>
        </div>
        <div class="actions">
            <button type="button" class="add" id="add">新增</button>
            <button type="button" class="del" id="del-all">批量删除</button>
            <button type="button" class="import" id="import">导入</button>
            <button type="button" class="export" id="export">导出</button>
        </div>
    </div>
  </div>
  <div class="bottom">
    <table>
        <thead>
            <tr>
                <th><input type="checkbox" id="select-all"></th>
                <th>姓名</th>
                <th>班级</th>
                <th>学号</th>
                <th>性别</th>
                <th>生日</th>
                <th>联系电话</th>
                <!-- <th>地址</th> -->
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
           {% for student in students %} 
            <tr>
                <td><input type="checkbox" name="student_ids" value="{{ student.pk }}"></td>
                <td>{{ student.student_name }}</td>
                <td>{{ student.grade }}</td>
                <td>{{ student.student_number }}</td>
                <td>{{ student.gender }}</td>
                <td>{{ student.birthday }}</td>
                <td>{{ student.contact_number }}</td>
                <td>
                    <a href="{% url 'student_update' student.pk %}" class="btn btn-primary btn-sm edit">编辑</a>
                    <a href="{% url 'student_delete' student.pk %}" class="btn btn-danger btn-sm del">删除</a>
                </td>
            </tr>
            {% endfor %}
        
        </tbody>
    </table>

  <!-- 分页导航 -->
  <div class="pagination">
      <span class="step-links">
          <span class="step-links">
            {% if page_obj.has_previous %}
                <a href="?{% search_url request page=1 %}">&laquo; 首页</a>
                <a href="?{% search_url request page=page_obj.previous_page_number %} ">上一页</a>
            {% endif %}

            <span class="current">
                 {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
            </span>

            {% if page_obj.has_next %}
                <a href="?{% search_url request page=page_obj.next_page_number %}">下一页</a>
                <a href="?{% search_url request page=page_obj.paginator.num_pages %}">尾页 &raquo;</a>
            {% endif %}
        </span>


      </span>
  </div>
  </div>

</div>

<script>
    // 全选
    document.addEventListener('DOMContentLoaded', function () {
        // 获取全选复选框元素
        const selectAllCheckbox = document.getElementById('select-all');
        
        // 监听全选复选框的点击事件
        selectAllCheckbox.addEventListener('change', function () {
            // 获取所有 name 为 'student_ids' 的复选框
            const studentCheckboxes = document.querySelectorAll('input[name="student_ids"]');
            
            // 根据全选复选框的选中状态设置每个学生复选框的状态
            studentCheckboxes.forEach(function(checkbox) {
                checkbox.checked = selectAllCheckbox.checked;
            });
        });
    });


    // 点击新增
    document.getElementById('add').addEventListener('click', function() {
        Swal.fire({
		  position: "top-end",
		  html: `<iframe src="{% url 'student_create' %}" width="100%", height="800px" frameborder="0" > <iframe>`,
		  width: 600,
          showConfirmButton: false
		});
    });

    // 点击编辑
    document.querySelectorAll('.edit').forEach(button => {
        button.addEventListener('click', function(e) {
            e.preventDefault(); // 阻止跳转
            url = this.getAttribute('href')
            Swal.fire({
                position: "top-end",
                html: `<iframe src="${url}" width="100%", height="800px" frameborder="0" > <iframe>`,
                width: 600,
                showConfirmButton: false
            });
        })
    })

    // 点击删除
    document.querySelectorAll('.btn-danger').forEach(button => {
        button.addEventListener('click', function(e) {
            e.preventDefault(); // 阻止跳转
            url = this.getAttribute('href')
            Swal.fire({
                title: "确认删除?",
                icon: "warning",
                showCancelButton: true,
                confirmButtonText: "删除",
                confirmButtonColor: "#d33",                
            }).then((result) => {
                if (result.isConfirmed) {
                    // 向后台发送数据
                    fetch(url, {
                        method: 'DELETE',
                        headers: {
                            'X-Requested-With': 'XMLHttpRequest',
                            'X-CSRFToken': '{{ csrf_token }}', 
                        },
                    })
                    .then(response => response.json())
                    .then(data => {
                        if (data.status === 'success') {
                            Swal.fire("Deleted!", data.messages, "success");
                            window.location.reload();
                        } else {
                            Swal.fire("Error!", data.messages, "error")
                        }
                        
                    })
                }
            })
        })
    })

    // 批量删除
    document.getElementById('del-all').addEventListener('click', function(){
        // 是否有学生被选择
        const checkboxes = document.querySelectorAll('input[name="student_ids"]:checked');
        if (checkboxes.length === 0) {
            Swal.fire({
                title: "错误",
                text: "请先选择要删除的学生信息",
                icon: "error",
                confirmButtonText: "好的"
            });
            return ;
        }
        // 如果有被选中,则fetch发送请求
        Swal.fire({
            title: "确认删除选中的数据?",
            icon: "warning",
            showCancelButton: true,
            confirmButtonText: "删除",
            confirmButtonColor: "#d33",
        })
        .then((result) => {
            if (result.isConfirmed) {
                // 创建一个表单对象
                const formData = new FormData()
                // 遍历所有选择的数据
                checkboxes.forEach((checkbox) => {
                    formData.append('student_ids', checkbox.value)
                })
                fetch("{% url 'student_bulk_delete' %}", {
                    method: 'POST',
                    headers: {
                            'X-Requested-With': 'XMLHttpRequest',
                            'X-CSRFToken': '{{ csrf_token }}', 
                        },
                    body: formData                
                })
                .then(response => response.json())
                .then(data => {
                    if (data.status === 'success') {
                            Swal.fire("Deleted!", data.messages, "success");
                            window.location.reload();
                    } else {
                        Swal.fire("Error!", data.messages, "error")
                    }
                })
            }
        })
    })
    // 导入excel
    document.getElementById('import').addEventListener('click', function() {
        Swal.fire({
            title: '上传学生信息Excel',
            input: 'file',
            inputAttributes: {
                'accept': '.xlsx',
                'aria-label': 'Upload your Excel file'
            },
            showCancelButton: true,
            confirmButtonText: 'Upload',
            showLoaderOnConfirm: true,
            preConfirm: (file) => {
                // 处理文件上传的逻辑,例如使用 FormData 和 fetch API 上传文件
                const formData = new FormData();
                formData.append('excel_file', file);
                     
                return fetch('{% url "upload_student" %}', {
                    method: 'POST',
                    headers: {
                        'X-CSRFToken':  "{{ csrf_token }}" // 确保添加 CSRF 令牌
                    },
                    body: formData,
                })
                .then(response => response.json())
                .then(data => {
                    if (data.status === 'error') {
                        throw new Error(data.messages); // 直接抛出错误,让 catch 块处理
                    }
                })
                .catch(error => {
                    console.log(error);
                    Swal.showValidationMessage(`${error.messages || error }`);
                });
            },
            allowOutsideClick: () => !Swal.isLoading()
        }).then((result) => {
            if (result.isConfirmed) {
                Swal.fire({
                    title: 'Uploaded!',
                    text: '上传成功.'
                })
                window.location.reload();
            }
        });        
    })

    // 导出excel 
    document.getElementById('export').addEventListener('click', function() {
        // 检查是否选择了班级
        var select = document.querySelector('select[name="grade"]');
        var value = select.value;
        var gradeText = select.options[select.selectedIndex].text;
        if (!value) {
            Swal.fire({
                title: '错误!',
                text: '请选择一个班级!',
                icon: 'error',
                confirmButtonText: '确定'
            });
            return ;            
        }
        // 提交请求
        Swal.fire({
            title: '确认',
            text: '导出【' + gradeText + '】学生信息',
            icon: 'warning',
            showCancelButton: true,
            confirmButtonText: '确认',
            cancelButtonText: '取消'
        }).then((result) => {
            if (result.isConfirmed) {
                // 发送请求
                fetch('{% url "export_excel" %}', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-CSRFToken': '{{ csrf_token }}'
                    }, 
                    body: JSON.stringify({grade: value})
                })
                .then(response => {
                    if (!response.ok) {
                        response.json().then(result => {
                            Swal.fire({
                                title: '下载失败',
                                text: '服务器错误: ' + result.messages,
                                icon: 'error',
                                confirmButtonText: '关闭'
                            });
                        });
                        throw new Error('网络或服务器错误');
                    }
                    return response.blob();
                })
                .then(blob => {
                    const url = window.URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.style.display = 'none';
                    a.href = url;
                    a.download =  gradeText + '.xlsx';
                    document.body.appendChild(a);
                    a.click();
                    // 清理并移除元素
                    document.body.removeChild(a);
                    window.URL.revokeObjectURL(url);
                })

            }
        })


    })


</script>

{% endblock %}

view方法:

    def get_queryset(self):
        queryset = super().get_queryset()
        grade_id = self.request.GET.get('grade') # 获取班级
        keyword = self.request.GET.get('search') # 获取搜索的内容
        if grade_id:
            queryset = queryset.filter(grade__pk=grade_id)
        if keyword:
            queryset = queryset.filter(
                Q(phone_number=keyword) |
                Q(teacher_name=keyword)
            )
        return queryset

    def get_context_data(self):
        context = super().get_context_data()
        # 获取所有班级并添加到上下文
        context['grades'] = Grade.objects.all().order_by('grade_number')
        context['current_grade'] = self.request.GET.get('grade', '')
        return context

上下页码搜索保存 80

添加一个文件包,url_utils.py


from django import template
from django.http import QueryDict

from urllib.parse import urlencode

register = template.Library()

@register.simple_tag
def search_url(request, **kwargs):
    query_params = QueryDict(request.META['QUERY_STRING'], mutable=True)
    for key, value in kwargs.items():
        if value is None:
            query_params.pop(key, None)
        else:
            query_params.setlist(key, [value])
    return urlencode(query_params, doseq=True)

html


  <!-- 分页导航 -->
  <div class="pagination">
      <span class="step-links">
          <span class="step-links">
            {% if page_obj.has_previous %}
                <a href="?{% search_url request page=1 %}">&laquo; 首页</a>
                <a href="?{% search_url request page=page_obj.previous_page_number %} ">上一页</a>
            {% endif %}

            <span class="current">
                 {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
            </span>

            {% if page_obj.has_next %}
                <a href="?{% search_url request page=page_obj.next_page_number %}">下一页</a>
                <a href="?{% search_url request page=page_obj.paginator.num_pages %}">尾页 &raquo;</a>
            {% endif %}
        </span>

登录界面81-88

from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.http import JsonResponse
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth import update_session_auth_hash


from .forms import LoginForm
from teachers.models import Teacher
from students.models import Student

# Create your views here.
def user_login(request):
    # 判断是否是post请求
    if request.method == 'POST':
        # form表单验证
        form = LoginForm(request.POST)
        # 验证失败
        if not form.is_valid():
            return JsonResponse({'status':'error', 'messages': '提交信息有误!'}, status=400, safe=False)
        # 验证成功
        # 获取账号和密码的输入
        username = form.cleaned_data.get('username')
        password = form.cleaned_data.get('password')
        # 确定角色
        role = form.cleaned_data.get('role')
        # 判断角色
        if role == 'teacher':
            try:
                # 搜索获得老师的phone_number
                teacher = Teacher.objects.get(phone_number=username)
                username = teacher.teacher_name + '_' + teacher.phone_number
                # 两者进行比较得到结果user
                user = authenticate(username=username, password=password)
                # teacher.user.username 
            except Teacher.DoesNotExist:
                return JsonResponse({'status':'error', 'messages':'老师信息不存在'}, status=404)
        elif role == 'student':
            try:
                student = Student.objects.get(student_number=username)
                username = student.student_name + "_" + student.student_number
                user = authenticate(username=username, password=password)
            except:
                return JsonResponse({'status':'error', 'messages':'学生信息不存在'}, status=404)
        else:
            try:
                user = authenticate(username=username, password=password)
            except:
                return JsonResponse({'status':'error', 'messages': '管理员信息不存在'}, status=404)
        # 验证成功,返回json数据
        if user is not None:
            # django自带的禁用和激活权限is_active,可以设置保存
            if user.is_active:
                # django自带的登录模版
                login(request, user)
                # 保存session信息
                request.session['username'] = username.split('_')[0]
                request.session['user_role'] = role
                return JsonResponse({'status':'success','messages': '登录成功', 'role': role})
            else:
                return JsonResponse({'status':'error', 'messages': '账户已被禁用'}, status=403)
        else:
             # 处理登录失败的情况
            return JsonResponse({'status':'error', 'messages': '用户名或密码错误'}, status=401)           

    return render(request, 'accounts/login.html') 

def user_logout(request):
    """退出登录"""
    # if 'user_role' in request.session:
    #     del request.session['user_role']
    logout(request) # 执行退出
    #  删除所有
    request.session.flush()
    return redirect('user_login')


def error_404_view(request, exception):
    return render(request, '404.html', {}, status=404)











def change_password(request):
    """修改密码"""
    if request.method == 'POST':
        form = PasswordChangeForm(request.user, request.POST)
        if form.is_valid():
            user = form.save()
            update_session_auth_hash(request, user)
            return JsonResponse({
                'status': 'success',
                'messages': '您的密码已成功更新!'
            })
        else:
            errors = form.errors.as_json()
            return JsonResponse({
                'status': 'error',
                'messages': errors
            })

    return render(request, 'accounts/change_password.html')

form

from django import forms
from django.core.exceptions import ValidationError


ROLE_CHOICES = [
    ('admin', '管理员'),
    ('teacher', '老师'),
    ('student', '学生'),
]


class LoginForm(forms.Form):
    username = forms.CharField(
        max_length=50,
        widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入手机号或学号'}),
    )
    password = forms.CharField(
        max_length=50,
        widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入密码'}),
    )

    role = forms.ChoiceField(label='角色', choices=ROLE_CHOICES)

    def clean_username(self):
        username = self.cleaned_data['username']
        if len(username) == 0:
            raise ValidationError('用户名长度不能为0。')
        return username

    def clean_password(self):
        password = self.cleaned_data['password']
        if len(password) == 0:
            raise ValidationError('密码长度不能为0。')
        return password
    

    def clean_role(self):
        role = self.cleaned_data['role']
        if role not in ['admin','student', 'teacher']:
            raise ValidationError('请选择一个用户身份')
        return role
    

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐