Django本地登录系统:含注册页、登录页、登出功能与SQLite数据存储

发布时间:2026/6/20 15:39:07

Django本地登录系统:含注册页、登录页、登出功能与SQLite数据存储 本文还有配套的精品资源点击获取简介直接运行就能用的Django用户认证小项目带完整的HTML登录/注册页面、密码加密存储、CSRF防护和会话保持功能。前端是纯HTMLCSS写的简洁表单不依赖JavaScript框架后端用Django内置auth模块处理用户创建、登录验证和登出逻辑所有用户数据存进自带的db.sqlite3文件里。项目结构清晰包含login和user两个应用已配置好URL路由、视图函数、模板路径和基础样式只要安装requirements.txt里的依赖主要是Django执行python manage.py runserver就能启动服务浏览器打开localhost:8000/login即可测试账号登录流程。支持普通用户名密码注册与登录登出后自动清除会话适合想动手理解Django认证机制的新手练习也适合嵌入到小型内部工具或原型系统中快速加个登录门槛。没有接入微信、手机号或第三方OAuth完全离线可用代码风格贴近Django官方教程写法。1. 项目概述为什么一个“能直接跑起来”的Django登录系统如此珍贵你有没有过这样的经历想给自己的小工具、内部报表页面或者原型系统加个登录门槛就为了防止同事误点、或者让家人别乱改配置结果一搜“Django 登录”出来的全是零散的教程片段——有的只讲UserCreationForm怎么用有的只说login()函数参数还有的直接跳到JWT或OAuth2中间缺了最关键的一环从新建项目到浏览器里输入账号密码、点登录、看到“欢迎回来”这整个闭环到底每一步该敲什么命令、改哪几行代码、模板文件放哪儿、URL怎么连起来更糟的是照着拼凑出来的代码跑起来要么404找不到页面要么登录成功但刷新一下又回到登录页要么注册时密码明文存进数据库……最后卡在某个报错信息里反复查文档却找不到对应场景的解释。这个项目就是为解决这个问题而生的。它不是一个教学PPT也不是一个需要你填空的骨架代码而是一个开箱即用、结构完整、逻辑自洽的最小可行认证系统。核心关键词“Django登录”、“用户注册”、“SQLite认证”不是标签而是它每一处设计的落脚点所有视图都基于Django内置的django.contrib.auth模块构建不绕弯子注册和登录表单是原生HTMLCSS写的没有JavaScript框架干扰你能一眼看清form里methodpost和{% csrf_token %}的位置与作用用户密码永远以哈希形式bcrypt存进db.sqlite3这个单一文件里删掉它整个用户库就清空干净利落登出操作不是简单跳转而是调用auth.logout(request)并重定向到首页确保session被彻底销毁。它面向两类人Python初学者可以把它当“活体教材”一行行读代码理解settings.py里AUTH_PASSWORD_VALIDATORS如何强制密码强度MIDDLEWARE里的SessionMiddleware和CsrfViewMiddleware怎样协同工作小型项目开发者则可以直接把login/和user/两个应用目录复制过去改几行INSTALLED_APPS和urlpatterns5分钟内就拥有了一个安全、合规、无需维护的本地登录入口。它不追求炫酷只解决一个最朴素的问题让“有登录功能”这件事变得像启动一个Python脚本一样确定、可预期、无歧义。2. 整体架构与设计思路为什么选择“原生Django SQLite”这条路径2.1 拒绝过度工程化从需求倒推技术选型很多新手一上来就想搞“高可用”“微服务”结果连一个登录按钮都点不动。这个项目的起点非常务实“我只需要一个页面输入用户名密码点登录如果对就进后台不对就提示错误注册同理登出后必须彻底退出。”围绕这个核心诉求所有技术决策都服务于“最小阻力路径”。为什么不用第三方包如django-allauthdjango-allauth确实强大支持邮箱验证、社交登录、多因素认证。但它引入了至少15个新配置项、一套独立的模板继承体系、以及大量你暂时用不到的模型SocialAccount,EmailAddress。对于只想验证“用户名密码”这一种方式的场景它就像用航空母舰去送快递——功能过剩学习成本陡增调试路径变长。而Django内置的auth模块仅需from django.contrib import auth一行导入就能完成90%的基础认证工作API清晰稳定文档详尽是官方明确推荐的“起步首选”。为什么坚持SQLite而非PostgreSQL或MySQL这是最关键的设计取舍。PostgreSQL当然更“生产级”但它需要你额外安装服务端、配置用户权限、管理数据库进程。而SQLite呢它就是一个文件。db.sqlite3在项目根目录下python manage.py migrate命令会自动创建它python manage.py createsuperuser会往里面写数据rm db.sqlite3就能一键重置。这种“零外部依赖”的特性完美匹配“开箱即用”的定位。更重要的是SQLite的ACID事务、行级锁机制在单机、低并发的小型应用中完全够用。我试过用它支撑一个20人团队的内部工单系统连续运行半年没出过一次锁表问题。它的局限性如不支持ALTER COLUMN在这个项目里根本不会触发——我们只做CRUD不做表结构动态变更。为什么前端拒绝JavaScript框架看似“落后”实则精准。一个登录表单的核心交互只有三步用户输入、提交表单、服务器返回结果成功跳转或失败渲染错误。这三步原生HTML表单Django模板的{{ form.username.errors }}和{{ form.non_field_errors }}就能完美承载。引入Vue或React你需要配置Webpack、处理跨域虽然本地开发不涉及、学习响应式数据绑定……这些复杂度与登录功能本身毫无关系。而纯HTML/CSS意味着你可以用任何文本编辑器打开templates/login/login.html修改input typepassword的placeholder文字保存刷新浏览器立刻生效。这种“所见即所得”的调试体验对学习者而言价值远超炫技。2.2 应用职责划分login与user的边界在哪里项目目录里有两个核心应用login和user。这不是随意命名而是遵循Django“关注点分离”的最佳实践。login应用专注“认证流程”的胶水层它不负责创建用户也不负责存储用户信息它的唯一使命是协调认证动作。具体表现为views.py里定义login_view处理GET显示表单、POST验证登录、logout_view执行登出逻辑、register_view处理注册请求urls.py里将/login/、/logout/、/register/这些URL路径映射到上述视图templates/login/下存放所有与登录流程强相关的HTML文件如login.html登录表单、register.html注册表单、logged_out.html登出成功页。user应用承载“用户实体”的数据层它才是Django用户模型的真正管理者。虽然我们使用内置的User模型但user应用的存在意义在于将所有与用户数据相关的逻辑如密码重置邮件模板、用户资料编辑预留扩展接口models.py虽为空因为复用内置模型但其存在明确了“用户数据归属此处”的契约admin.py中注册UserAdmin方便后续通过Django Admin后台管理用户比如禁用某个账号。这种划分让代码具备极强的可维护性。假如未来要增加“忘记密码”功能你只需在login应用里新增password_reset_view和对应URL完全不影响user应用的结构。反之若要为用户添加头像字段你只需修改user/models.py扩展User模型通过OneToOneField关联自定义Profile模型login应用的视图和模板几乎无需改动。2.3 安全基石CSRF、密码哈希与会话管理的三位一体一个“能用”的登录系统和一个“安全”的登录系统差距就在三个细节上而这三个细节Django都已为你封装好只需正确启用。CSRF防护不只是模板里的{% csrf_token %}很多人以为只要在表单里加上{% csrf_token %}就万事大吉。其实这只是冰山一角。真正的防护由三层构成1.客户端{% csrf_token %}生成一个随机token作为隐藏字段嵌入表单2.中间件CsrfViewMiddleware在每次POST请求到达视图前自动检查请求头中的X-CSRFToken或表单字段中的csrfmiddlewaretoken并与服务端session中存储的token比对3.服务端CSRF_COOKIE_SECURE和CSRF_COOKIE_HTTPONLY设置本项目默认开启确保cookie只能通过HTTPS传输且无法被JS读取。提示如果你在本地开发时遇到CSRF错误请检查settings.py中MIDDLEWARE是否包含django.middleware.csrf.CsrfViewMiddleware且位置在django.contrib.sessions.middleware.SessionMiddleware之后——顺序错了CSRF token就无法绑定到session上。密码哈希bcrypt为何是默认选择Django默认使用PBKDF2算法但本项目在settings.py中显式配置了PASSWORD_HASHERS [django.contrib.auth.hashers.BCryptPasswordHasher]。原因很简单bcrypt是专为密码哈希设计的算法它内置“盐值salt”和“计算轮数rounds”能有效抵御彩虹表攻击和暴力破解。当你调用User.objects.create_user(username, email, password)时Django不会存储明文密码而是生成类似bcrypt_sha256$32$...的哈希字符串存入数据库。即使黑客拿到了db.sqlite3文件也无法反向推导出原始密码。实测下来一个8位随机密码用现代GPU暴力破解bcrypt需要数百年。会话管理session_key背后的生命周期登录成功后Django会在django_session表SQLite中创建一条记录其中session_key是随机字符串session_data是加密后的用户ID等信息expire_date是过期时间。关键点在于auth.login(request, user)这行代码不仅把用户对象挂载到request.user更会调用request.session.create()生成新的session并将user.id写入session_data。登出时auth.logout(request)会调用request.session.flush()删除该session记录并清空request.session字典。这意味着登出后即使你手动在浏览器地址栏输入/admin/也会被重定向到登录页——因为request.user.is_authenticated此时为False。3. 核心细节解析与实操要点从代码到浏览器的每一处关键3.1 settings.py安全配置的“总开关”settings.py是整个项目的神经中枢这里集中了所有影响认证行为的关键配置。新手常犯的错误就是忽略其中几项看似不起眼的设置。INSTALLED_APPS的顺序与必要项除了基础的django.contrib.admin等以下三项是认证的刚需python INSTALLED_APPS [ # ... 其他应用 django.contrib.auth, # 提供User模型、认证后端 django.contrib.contenttypes, # auth模块依赖此用于权限系统 django.contrib.sessions, # 会话管理login/logout依赖它 login, # 我们的登录应用 user, # 用户数据应用 ]注意django.contrib.auth必须在django.contrib.sessions之前。因为auth模块的login()函数内部会调用session.save()如果sessions未加载就会报AttributeError: WSGIRequest object has no attribute session。MIDDLEWARE安全中间件的黄金组合认证流程依赖于特定中间件的协作python MIDDLEWARE [ django.middleware.security.SecurityMiddleware, django.contrib.sessions.middleware.SessionMiddleware, # 必须在CsrfViewMiddleware之前 django.middleware.common.CommonMiddleware, django.middleware.csrf.CsrfViewMiddleware, # 必须在AuthenticationMiddleware之前 django.contrib.auth.middleware.AuthenticationMiddleware, # 必须在SessionMiddleware之后 django.middleware.clickjacking.XFrameOptionsMiddleware, ]这个顺序不是随意的。SessionMiddleware必须在CsrfViewMiddleware之前否则CSRF token无法绑定到sessionAuthenticationMiddleware必须在SessionMiddleware之后因为它需要从session中读取user_id来实例化request.user。一旦顺序错乱轻则登录状态丢失重则整个认证流程崩溃。AUTH_PASSWORD_VALIDATORS强制密码强度的“守门员”默认配置启用了4条校验规则python AUTH_PASSWORD_VALIDATORS [ {NAME: django.contrib.auth.password_validation.UserAttributeSimilarityValidator}, {NAME: django.contrib.auth.password_validation.MinimumLengthValidator, OPTIONS: {min_length: 8}}, {NAME: django.contrib.auth.password_validation.CommonPasswordValidator}, {NAME: django.contrib.auth.password_validation.NumericPasswordValidator}, ]这意味着注册时如果密码是12345678纯数字、password常见弱口令、或与用户名相同表单提交会直接失败并在{{ form.password1.errors }}中显示具体错误原因。这是防止用户设置弱密码的第一道防线无需你写一行校验逻辑。3.2 login/views.py认证逻辑的“心脏地带”login/views.py是整个系统最核心的文件它把Django的认证能力翻译成具体的业务动作。register_view如何安全地创建用户关键代码如下pythonfrom django.contrib.auth import get_user_modelfrom django.contrib.auth.forms import UserCreationFormfrom django.contrib import messagesdef register_view(request):if request.method ‘POST’:form UserCreationForm(request.POST)if form.is_valid():user form.save() # 这里自动调用set_password()进行哈希messages.success(request, f’账户 {user.username} 创建成功’)return redirect(‘login’) # 重定向到登录页避免重复提交else:form UserCreationForm()return render(request, ‘login/register.html’, {‘form’: form}) 注意点 -form.save()是安全创建用户的唯一正确方式。它内部会调用user.set_password()确保密码被哈希而不是直接赋值user.password raw_password这会导致明文存储 - 使用messages.success()而非HttpResponse是为了在重定向后仍能显示提示信息提升用户体验 -redirect(‘login’)而非redirect(‘/login/’)是因为我们使用了命名URLpath(‘login/’, views.login_view, name’login’)这样即使将来修改URL路径视图代码也无需改动。login_view登录成功的“临门一脚”pythonfrom django.contrib import authfrom django.contrib.auth import authenticatedef login_view(request):if request.method ‘POST’:username request.POST.get(‘username’)password request.POST.get(‘password’)user authenticate(request, usernameusername, passwordpassword)if user is not None:auth.login(request, user) # 关键建立会话return redirect(‘home’) # 假设home是登录后的首页else:messages.error(request, ‘用户名或密码错误’)return render(request, ‘login/login.html’)authenticate()和auth.login()是两个不可分割的动作前者只做“验证”返回User对象或None后者才做“登录”将用户ID写入session。漏掉auth.login()用户永远处于未登录状态。另外authenticate()函数会自动检查用户是否is_activeTrue如果管理员在Admin后台禁用了该用户此处会直接返回None。logout_view登出的“彻底清除”python def logout_view(request): auth.logout(request) # 一行代码干掉session和request.user return redirect(login) # 重定向到登录页这行auth.logout(request)会执行三件事1调用request.session.flush()删除session记录2将request.user设为AnonymousUser3发送user_logged_out信号可用于扩展如记录登出日志。它比手动del request.session[user_id]可靠得多因为session可能包含多个相关键值。3.3 模板与表单HTML里的安全细节templates/login/login.html和templates/login/register.html是用户直接面对的界面它们的安全性往往被忽视。login.html中的CSRF与错误渲染html{% csrf_token %}用户名{{ form.username }} {{ form.username.errors }}密码{{ form.password }} {{ form.password.errors }}{{ form.non_field_errors }}登录{{ form.non_field_errors }}是关键。当authenticate()返回None时form.add_error(None, ‘用户名或密码错误’)会被调用这个错误就属于“非字段错误”必须用non_field_errors来渲染否则用户看不到任何提示。register.html中的密码确认UserCreationForm默认包含password1和password2两个字段后者用于确认密码。它的clean_password2()方法会自动比对两者是否一致。如果用户输入123456和1234567错误会出现在{{ form.password2.errors }}中。这是防止用户手滑输错密码的最简单有效手段。3.4 URL路由连接视图与浏览器的“高速公路”login/urls.py定义了用户访问的入口from django.urls import path from . import views urlpatterns [ path(login/, views.login_view, namelogin), path(logout/, views.logout_view, namelogout), path(register/, views.register_view, nameregister), ]而根urls.py需要包含它from django.contrib import admin from django.urls import path, include urlpatterns [ path(admin/, admin.site.urls), path(, include(login.urls)), # 将/login/等路径委托给login应用 ]注意include(login.urls)必须放在admin之后否则/admin/会被login应用的path(, ...)捕获导致Admin后台无法访问。这是新手最常见的404错误来源之一。4. 实操过程与核心环节实现从零开始搭建与验证4.1 环境准备与项目启动5分钟走通全流程假设你已安装Python 3.8以下是完整的、可复制粘贴的操作步骤解压资源包进入项目根目录bash unzip 55QKuo1TEt4sANhn6dHz-master-63429a7501349ad1d90dc457763d6442f6ad04b2.zip cd 55QKuo1TEt4sANhn6dHz-master-63429a7501349ad1d90dc457763d6442f6ad04b2创建并激活虚拟环境强烈推荐bash python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows安装依赖bash pip install -r requirements.txt # requirements.txt 内容应为Django4.2.7初始化数据库bash python manage.py migrate # 此命令会创建db.sqlite3文件并在其中生成auth_user、django_session等表创建超级用户用于Admin后台bash python manage.py createsuperuser # 按提示输入用户名、邮箱可选、密码启动开发服务器bash python manage.py runserver # 输出Starting development server at http://127.0.0.1:8000/浏览器验证- 访问http://127.0.0.1:8000/login/→ 应看到简洁的登录表单- 访问http://127.0.0.1:8000/register/→ 应看到注册表单- 访问http://127.0.0.1:8000/admin/→ 输入刚才创建的superuser账号应能进入Django Admin后台看到Users列表。4.2 注册与登录的完整链路验证让我们亲手走一遍用户生命周期注册新用户- 打开http://127.0.0.1:8000/register/- 输入用户名testuser密码SecurePass123!再次输入相同密码- 点击“注册”。页面应跳转到/login/并显示绿色提示“账户 testuser 创建成功”-验证打开db.sqlite3文件可用DB Browser for SQLite工具查询auth_user表找到testuser记录查看password字段内容应为bcrypt_sha256$...开头的长字符串而非明文。登录验证- 在/login/页面输入testuser和SecurePass123!- 点击“登录”。页面应跳转到/即home视图本项目中可设为重定向到/admin/或自定义首页-验证在浏览器开发者工具F12的Application → Storage → Cookies中查找127.0.0.1域名下的sessionidcookie其值应与数据库django_session表中的session_key匹配。登出与状态清除- 访问/logout/或点击页面上的登出链接- 页面跳转回/login/-验证再次打开Cookies面板sessionidcookie已被删除尝试直接访问/admin/应被重定向到/login/?next/admin/。4.3 数据库操作SQLite文件的日常管理db.sqlite3是你的用户数据库掌握它的基本操作能让你游刃有余查看用户列表命令行bash sqlite3 db.sqlite3 sqlite .tables # 输出auth_group auth_group_permissions auth_permission auth_user ... sqlite SELECT id, username, email, is_active FROM auth_user; # 查看所有用户及其状态禁用/启用用户通过Admin后台登录http://127.0.0.1:8000/admin/→ 点击Users→ 找到目标用户 → 取消勾选Active复选框 →Save。此后该用户无法再登录authenticate()会返回None。重置密码命令行如果忘记了superuser密码无需修改数据库bash python manage.py changepassword admin # 按提示输入新密码重置整个数据库开发时常用bash rm db.sqlite3 python manage.py migrate python manage.py createsuperuser # 一切回到初始状态5. 常见问题与排查技巧实录那些踩过的坑与独家心得5.1 经典404错误URL路径与视图不匹配现象浏览器访问http://127.0.0.1:8000/login/返回Page not found (404)。排查步骤1. 检查根urls.py是否包含path(, include(login.urls))2. 检查login/urls.py中path(login/, ...)的路径是否与浏览器地址栏完全一致注意末尾斜杠3. 运行python manage.py show_urls需先安装django-extensions查看所有已注册URL确认/login/是否存在。实操心得Django的URL匹配是严格区分大小写和斜杠的。path(Login/, ...)和path(login/, ...)是两个不同路径。建议全部使用小写并在path()中统一加末尾斜杠Django默认APPEND_SLASHTrue会自动重定向。5.2 登录后无法保持状态“登录成功但刷新就掉线”现象输入账号密码点击登录页面跳转到首页但几秒后又自动跳回登录页或在Admin后台能看到用户在线但自己写的视图中request.user.is_authenticated为False。根本原因SessionMiddleware未启用或MIDDLEWARE顺序错误。验证方法- 在任意视图中添加print(request.session.session_key)如果输出None说明session未创建- 检查settings.py中MIDDLEWARE是否包含django.contrib.sessions.middleware.SessionMiddleware且位置在django.middleware.csrf.CsrfViewMiddleware之前。实操心得我曾在一个项目中因复制粘贴错误把SessionMiddleware写成了django.contrib.sessions.middleware.SessionMiddleWareW大写了导致session始终为None。调试时打印request.META发现HTTP_COOKIE中根本没有sessionid这才定位到中间件加载失败。5.3 密码哈希失效数据库里存的是明文现象在db.sqlite3中查询auth_user表password字段内容是mypassword123这样的明文。原因在创建用户时没有使用form.save()或User.objects.create_user()而是直接user.password raw_password。修复方案- 对于已存在的明文密码可通过Django shell重置bashpython manage.py shellfrom django.contrib.auth import get_user_modelUser get_user_model()u User.objects.get(username’testuser’)u.set_password(‘NewSecurePass123!’)u.save() - 今后务必使用create_user()或form.save()。5.4 CSRF验证失败表单提交报403 Forbidden现象注册或登录时浏览器控制台报403 ForbiddenDjango日志显示Forbidden (CSRF token missing or incorrect)。排查清单- 检查模板中是否有{% csrf_token %}- 检查settings.py中MIDDLEWARE是否包含CsrfViewMiddleware- 检查CSRF_COOKIE_SECURE设置如果DEBUGFalse且未使用HTTPS需设为False否则浏览器不会发送CSRF cookie- 检查CSRF_TRUSTED_ORIGINS如果通过Nginx反向代理访问需将代理域名加入此列表。实操心得在本地开发时DEBUGTrueCSRF_COOKIE_SECURE默认为False所以通常没问题。但一旦部署到生产环境DEBUGFalse必须配置CSRF_COOKIE_SECURE True并启用HTTPS否则CSRF保护会失效。这是从开发到上线最容易翻车的点。5.5 表单错误不显示用户不知道哪里填错了现象输入错误密码点击登录页面刷新但没有任何错误提示。原因视图中未将form对象传递给模板或模板中未正确渲染{{ form.non_field_errors }}。验证方法- 在login_view中确保return render(request, login/login.html, {form: form})- 在login.html中确保有{{ form.non_field_errors }}且位于form标签内。实操心得Django表单的错误渲染是“懒加载”的。只有当form.is_valid()为False时form.errors才会被填充。所以如果视图中if form.is_valid():分支后直接return redirect()而else分支忘了return render(..., {form: form})那么form.errors永远不会被传递到模板用户就永远看不到错误。6. 后续扩展与定制化建议让这个小系统真正为你所用这个项目不是终点而是一个坚实可靠的起点。根据你的实际需求可以沿着几个方向平滑扩展添加邮箱验证增强安全性不需要引入django-allauth。只需在register_view中创建用户后不立即登录而是生成一个带签名的验证链接用django.core.signing发送到用户邮箱。用户点击链接后调用user.is_active True并保存。这能有效防止机器人批量注册。集成简单权限控制区分用户角色Django内置的Group和Permission模型足够应付多数场景。例如创建一个Editor组赋予change_article权限在视图中用permission_required(myapp.change_article)装饰器或在模板中用{% if perms.myapp.change_article %}控制按钮显示。美化前端不破坏现有逻辑static/css/style.css是样式入口。你可以用Bootstrap替换原生CSS只需确保表单input的name属性不变如nameusernameDjango视图就能继续正常接收数据。所有样式变更都在static/目录下与后端逻辑完全隔离。迁移到生产数据库无缝升级当项目需要更高并发时只需修改settings.py中的DATABASES配置python DATABASES { default: { ENGINE: django.db.backends.postgresql, NAME: myproject, USER: myuser, PASSWORD: mypass, HOST: localhost, PORT: 5432, } }然后运行python manage.py migrateDjango会自动在PostgreSQL中重建所有表结构数据零丢失。我个人在实际使用中发现这个项目的最大价值不在于它实现了什么而在于它剔除了所有模糊地带。每一行代码的目的清晰可见每一个配置项的影响范围明确可控。当你把db.sqlite3拖进DB Browser亲眼看到密码被哈希、session被创建、用户被禁用那种“掌控感”是任何教程都无法替代的。它教会你的不是某个API的用法而是如何像一个真正的工程师那样从需求出发权衡利弊选择最简单有效的工具并亲手验证每一个环节的正确性。这才是动手实践的终极意义。本文还有配套的精品资源点击获取简介直接运行就能用的Django用户认证小项目带完整的HTML登录/注册页面、密码加密存储、CSRF防护和会话保持功能。前端是纯HTMLCSS写的简洁表单不依赖JavaScript框架后端用Django内置auth模块处理用户创建、登录验证和登出逻辑所有用户数据存进自带的db.sqlite3文件里。项目结构清晰包含login和user两个应用已配置好URL路由、视图函数、模板路径和基础样式只要安装requirements.txt里的依赖主要是Django执行python manage.py runserver就能启动服务浏览器打开localhost:8000/login即可测试账号登录流程。支持普通用户名密码注册与登录登出后自动清除会话适合想动手理解Django认证机制的新手练习也适合嵌入到小型内部工具或原型系统中快速加个登录门槛。没有接入微信、手机号或第三方OAuth完全离线可用代码风格贴近Django官方教程写法。本文还有配套的精品资源点击获取

相关新闻