Django打造大型企业官网-项目实战(二)
Django打造大型企业官网-项目实战
一、项目环境搭建
1、新建虚拟环境:mkvirtualenv project_name
2、pycharm 新建 Django 项目:xfz
3、 删除 xfz 项目中的 templates 目录,新建 front 目录。项目 xfz 初始目录如下:
4、front 前端环境初始化
1)进入front目录,打开终端,执行命名:npm init ,进行npm 初始化,执行完成会生成一个 package.json 跟一个 package_lock.json 文件
2)当前项目下安装 gulp :npm install gulp --save-dev , 安装完成后会生成一个 node_modules 包
3)在front 目录下,新建js文件:gulpfile.js
4)我们将之前安装过的 gulp 插件命令,拿到本项目中用:
{"name": "xfz_front","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","devDependencies": {"browser-sync": "^2.26.3","gulp": "^4.0.0","gulp-cache": "^1.1.1","gulp-concat": "^2.6.1","gulp-cssnano": "^2.1.3","gulp-imagemin": "^5.0.3","gulp-rename": "^1.4.0","gulp-uglify": "^3.0.2"} }
package.json
5)终端下执行命令:npm install , 对 package.json 中 devDependencies 下的所有插件进行安装。
至此便完成了前端环境的初始化,接下来需要在 gulpfile.js 文件中,对 一些源文件进行相应的预处理(压缩)
5、编写 gulpfile.js 文件
创建处理 css 的任务
创建处理 js 的任务
创建处理 images 的任务
创建处理 html 的任务
创建监听任务
实现指定文件内容修改时,自动刷新浏览器内容
var gulp = require("gulp"); // gulp 插件 var cssnano = require("gulp-cssnano"); // css 压缩 var rename = require("gulp-rename"); // 重命名,加后缀等 var uglify = require("gulp-uglify"); // js 压缩 var concat = require("gulp-concat"); // 文件拼接 合并 var cache = require("gulp-cache"); // 缓存 var imagemin = require("gulp-imagemin"); // 图片压缩 var bs = require("browser-sync").create(); // 浏览器自动刷新 var sass = require("gulp-sass"); // sass 压缩 var util = require("gulp-util"); // 这个插件中有一个 log 方法,可以用来打印当前 js 错误信息。需安装// 定义全局路径 var path = {'html':'./templates/**/','css':'./src/css/**/','js':'./src/js/**/','images':'./src/images','css_dist':'./dist/css/','js_dist':'./dist/js/','images_dist':'./dist/images' };// 初始化 browser-sync 的任务 gulp.task("bs",function(){bs.init({'server':{'baseDir':'./'}}); });// 定义处理 HTML 文件的任务 gulp.task("html",function () {gulp.src(path.html + '*.html').pipe(bs.stream()) // html文件更改时,浏览器会自动刷新 });// 定义处理 css 的任务 gulp.task("css",function(){gulp.src(path.css + '*.scss').pipe(sass().on("error",sass.logError)) // 将 scss 文件转换成 css 文件,如果出错则打印错误信息.pipe(cssnano()) // 压缩 css 文件.pipe(rename({"suffix":".min"})).pipe(gulp.dest(path.css_dist)).pipe(bs.stream()) });// 定义处理 js 的任务 gulp.task("js",function () {gulp.src(path.js + '*.js').pipe(uglify().on("error",util.log)) // js 文件压缩错误时打印错误信息.pipe(rename({"suffix":".min"})).pipe(gulp.dest(path.js_dist)).pipe(bs.stream()) });// 定义处理图片文件的任务 gulp.task("images",function () {gulp.src(path.images + '*.*').pipe(cache(imagemin())).pipe(gulp.dest(path.images_dist)).pipe(bs.stream()) });// 定义监听文件修改的任务 gulp.task("watch",function () {gulp.watch(path.html + '*.html',['html']);gulp.watch(path.css + '*.scss',['css']);gulp.watch(path.js + '*.js',['js']);gulp.watch(path.images + '*.*',['images']); });// 创建一个默认的任务,在终端执行时只需要执行命令:gulp gulp.task("default",["bs",'watch']);
gulpfile.js
注:gulpfile.js文件内容只适合在 gulp 3的版本中使用,因 gulp 3 和 gulp 4 版本有些不同,如直接运行上述 gulpfile.js 会报错。 当前项目安装 gulp 时,默认会安装 gulp 最新版本,即 gulp 4版本,我们可以通过下述命令让 gulp 4版本回退到 gulp 3 版本(3.9.1):
npm install gulp@3 --save-dev
此时,我们可以在终端执行命令:gulp 来启动我们的前端程序,启动成功终端显示如下图,同时会跳出一个本地ip:3000端口的网页,即我们的前端显示页面。我们可以在浏览器输入网址:http://localhost:3000/templates/news/index.html ,来访问我们的新闻首页。
注:如果报错:Cannon find module "lodash.assign" 错误,直接安装lodash即可,目前package.json依赖如下:
{"name": "xfz_front","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","devDependencies": {"browser-sync": "^2.26.3","gulp": "^3.9.1","gulp-cache": "^1.1.1","gulp-concat": "^2.6.1","gulp-cssnano": "^2.1.3","gulp-imagemin": "^5.0.3","gulp-rename": "^1.4.0","gulp-sass-china": "^3.1.0","gulp-uglify": "^3.0.2","gulp-util": "^3.0.8","lodash": "^4.17.11"},"dependencies": {} }
package.json
二、后端开发
前端开发不累述。
在我们借助 gulp 完成前端页面的开发后,正式进入到我们的后端开发。开始后端开发前,需要在 settings.py 中做好以下几步配置工作:
- 配置好数据库
- 配置好模板文件的路径
- 配置好静态文件的路径
- 配置好时区
- 配置好模板的static标签
1)配置好数据库,如下:
Navicat:
数据库配置:
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': "xfz",'HOST': "127.0.0.1",'POST': "3306",'USER': "root",'PASSWORD': '***'} }
配置好数据库好,我们还需要进入虚拟环境(workon xfz)中安装 mysqlclient ,这是数据库的依赖包,安装后才能正常连接数据库。
2)配置好模板文件的路径:
templates原配置:'DIRS': [os.path.join(BASE_DIR, 'templates')]新:'DIRS': [os.path.join(BASE_DIR, 'front', 'templates')] # 在front目录下的templates目录
3)配置好静态文件的路径:
STATIC_URL = '/static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, 'front', 'dist') ]
4)配置好时区:
LANGUAGE_CODE = 'en-us'TIME_ZONE = 'Asia/Shanghai'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = True
5)配置好模板的静态 static 标签 ,这样在模板中使用static语法时就不需要在每个相应的模板页面中 {% load static %} 了 :
TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [os.path.join(BASE_DIR, 'front', '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',],'builtins':[ # static 标签配置'django.templatetags.static']},}, ]
settings.py 相关配置配置完成后,我们新建个apps目录,用来存放所有app ,同时将 apps 目录设为 Sources Root、templates目录设为 Template Folder
接着新建app:news ,并将该app添加进settings.py 中的 INSTALLED_APPS 中:
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','apps.news', # 新增 ]
然后,将template模板中设计的的文件/图片路径均改成动态路径,即{% static '...' %}格式。
完成上述步骤后,我们就可以正式进入到后端开发中。
1、使用 xadmin 后台管理系统,而不使用django自带的admin后台系统。xadmin 相关介绍请参考:https://www.cnblogs.com/Eric15/articles/9527556.html
1)依赖包安装:
pip install django-crispy-forms django-reversion django-formtools future httplib2 six # 多个依赖包一起安装 pip install django-import-exportpip install xlwt xlsxwriter
2)将xadmin 及 DjangoUeditor 包复制,放到extra_apps(新建)中,在apps中的每个app(创建的app)都添加一个adminx文件
3)在settings.py 中将 apps 、extre_apps 路径配置进去:
# settings.py import sys sys.path.insert(0,BASE_DIR) sys.path.insert(0,os.path.join(BASE_DIR, 'apps')) sys.path.insert(0,os.path.join(BASE_DIR, 'extra_apps'))
3)将xadmin、crispy_forms、DjangoUeditor(富文本编辑器),在setting中进行注册:
INSTALLED_APPS = ['django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','apps.news.apps.NewsConfig' # 创建的app 用这种形式注册 'xadmin', 'crispy_forms', 'DjangoUeditor', ]
关于 DjangoUeditor → ueditor 富文本使用,可查看博文:https://www.cnblogs.com/Eric15/articles/9589396.html
4)settings.py 中语言改为中文:
# 语言改为中文 LANGUAGE_CODE = 'zh-hans'
5)静态文件/上传文件设置:
# 设置静态文件路径,上面已经设置过 STATIC_URL = '/static/'STATICFILES_DIRS = [os.path.join(BASE_DIR, "front", "dist"), ] # 用户上传文件存放路径 MEDIA_URL = "/media/"MEDIA_ROOT = os.path.join(BASE_DIR, "media")
6)app 名称中文化:
# news/apps.py class GoodsConfig(AppConfig):name = 'news'verbose_name = "新闻"
7)配置 xadmin 、ueditor 路由:
# xfz/urls.pyimport xadminfrom django.urls import path,includeurlpatterns = [path('xadmin/', xadmin.site.urls),path('ueditor/', include('DjangoUeditor.urls')), ]
8)makemigrations / migrate :生成数据库表
9)创建超级管理员:
python manager.py createsuperuser ,创建超级用户,登录(127.0.0.1:8000/xadmin)xadmin后台管理查看详情
2、系统登录/注册功能,使用django自带的登录功能,实现自定义用户名(邮箱、手机等)登录/注册
2.1、注册功能
主要用到手机号码、用户名、随机验证码、密码及短信验证码等字段,随机验证码我们可以借用第三方库 Captcha 来实现,短信验证码需要另外生成一个表来处理,下面先单独设计用户表,继承于Django 自带的 AbstractUser :
注意:在 AbstractUser 中:REQUIRED_FIELDS = ['mobile'] , 指定创建用户时需输入某个字段,如这里就是要求创建用户时必须输入mobile,另外username、password 是默认必须要输入的字段。
class UserProfile(AbstractUser):"""用户"""gender_choices = (('male', '男'),('female', '女'))nick_name = models.CharField('昵称', max_length=32, default='', null=True, blank=True)birthday = models.DateField('生日', null=True, blank=True)gender = models.CharField('性别', max_length=8, choices=gender_choices, default='female')mobile = models.CharField('手机号', max_length=11, null=True, unique=True)image = models.ImageField(upload_to='image/%Y/%m', default='image/default.png', max_length=100)email = models.EmailField('邮箱', blank=True, unique=True, null=True) # 重写email字段,加上'唯一'标识is_active = models.BooleanField(default=False) # AbstractUser 类中该字段默认为Trueis_staff = models.BooleanField(default=True) # AbstractUser 类中该字段默认为False ,为了让前端注册成功后能正常登录后台管理系统,需设为True,同时后续还需要处理权限问题REQUIRED_FIELDS = ['mobile', 'email'] # 通过manage.py...创建用户/管理员时,会提醒输入此字段(必输入)class Meta:verbose_name = '用户信息'verbose_name_plural = verbose_namedef __str__(self):return self.username
要想登录xadmin后台系统,需要同时满足:is_active为True、is_staff为True 两个条件。在AbstractUser 类中默认,is_active=True,is_staff=False ,在下面我们处理的注册相关操作中我们选择通过使用is_active来控制注册用户是否有效可用,因此在这里我们需要重载该字段并设置为 is_active=False、is_staff=True,如上述代码所示 , 这样,通过前端注册并成功激活的用户便能登录前端界面及后端管理后台,这类用户是没有后台任何权限的,我们后续需要配置权限相关操作。接着,我们也需要对 auth/models.py(django内置)中的 UserManager 类下create_superuser 方法进行如下代码优化,目的还是实现:在后台创建超级用户时,超级用户拥有is_active=True、is_staff=True 两个条件,可以登录后台并拥有后台所有权限。
如果要创建能登录后台的普通用户,方法一:可以通过超级用户登录到后台,再创建新普通用户并赋予指定权限就可以了;也可以前端直接注册,成功后照样能登录后台。 当然,如果用户注册时不需要用到is_active条件,在models.py中我们就只需要重载is_staff字段就可以,后续的UserManager 类中我们就不需要更改任何代码了。
# auth/models.py class UserManager(BaseUserManager):use_in_migrations = Truedef _create_user(self, username, email, password, **extra_fields):"""Create and save a user with the given username, email, and password."""if not username:raise ValueError('The given username must be set')email = self.normalize_email(email)username = self.model.normalize_username(username)user = self.model(username=username, email=email, **extra_fields)user.set_password(password)user.save(using=self._db)return userdef create_user(self, username, email=None, password=None, **extra_fields):extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False)return self._create_user(username, email, password, **extra_fields)def create_superuser(self, username, email, password, **extra_fields):extra_fields.setdefault('is_staff', True)extra_fields.setdefault('is_active', True) # 新增extra_fields.setdefault('is_superuser', True)if extra_fields.get('is_staff') is not True:raise ValueError('Superuser must have is_staff=True.')if extra_fields.get('is_superuser') is not True:raise ValueError('Superuser must have is_superuser=True.')return self._create_user(username, email, password, **extra_fields)
用户表中用到 ImageField,因此我们需要安装下第三方库:pillow
pip install pillow
用户注册时,需要用到图形验证码及短信验证码,我们先来处理这两个问题
1)图形验证码
1. 使用第三方库:django-simple-captcha
虚拟环境下安装:
注意,使用django-simple-captcha第三方库时会涉及到image,此时如果未安装pillow,则需同时安装pillow:pip install pillow
pip install django-simple-captcha
2. 在项目的setting.py中的INSTALLED_APPS需要注册captcha。同时注册后,需要经过makemigrations、migrate生成对应的表数据(反经过这种注册的app都需要进行 makemigrations 、migrate 操作)
INSTALLED_APPS = [……'captcha',…… ]
3. 在urls.py中配置:
from django.urls import path,include,re_pathurlpatterns = [# 验证码re_path(r'^captcha', include('captcha.urls')), ]
4. 后台中,需要用到captcha 的地方中使用:
from captcha.fields import CaptchaFieldclass RegisterForm(forms.Form):"""注册表单验证"""captcha = CaptchaField(error_messages={'invalid':'验证码错误'})
5. 前端展示 captcha 图形验证码:
<label>验 证 码</label> {{ register_form.captcha }}
前端中生成的captcha 代码:
6. 在 js 文件中实现点击图形验证码即时更新:
//刷新验证码 function refresh_captcha(event){$.get("/captcha/refresh/?"+Math.random(), function(result){$('#'+event.data.form_id+' .captcha').attr("src",result.image_url);$('#id_captcha_0').attr("value",result.key);});return false; }// 绑定点击事件,点击刷新验证码 $(function() {$('#email_register_form .captcha-refresh').click({'form_id':'email_register_form'},refresh_captcha); // email_register_form 是表单的id$('#email_register_form .captcha').click({'form_id':'email_register_form'},refresh_captcha); })
在 form.py 中进行表单验证时,添加的 captcha 字段是由第三方库 Captcha 内部定义的,内部已经完成验证码验证,即不需要我们再额外判断用户输入图形验证码是否正确
django-simple-captcha 使用时,在settings.py 中的部分可选择配置:
# 格式 CAPTCHA_OUTPUT_FORMAT = u'%(text_field)s %(hidden_field)s %(image)s' # 噪点样式 CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_null', # 没有样式# 'captcha.helpers.noise_arcs', # 线'captcha.helpers.noise_dots', # 点 ) # 图片大小 CAPTCHA_IMAGE_SIZE = (100, 30) # 字符个数 CAPTCHA_LENGTH = 4 # 超时(minutes) CAPTCHA_TIMEOUT = 1 # 文字倾斜 CAPTCHA_LETTER_ROTATION = (-10,10) # 背景颜色 CAPTCHA_BACKGROUND_COLOR = '#FFFFFF' # 文字颜色 CAPTCHA_FOREGROUND_COLOR = '#0A12E5' # 验证码类型 # 图片中的文字为随机英文字母,如 mdsh ,默认就是这种 CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge' # 图片中的文字为数字表达式,如1+2= # CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge'
2)短信验证码
1. 前端注册页面代码:
{% extends 'front_base.html' %}{% block title %}小饭桌注册{% endblock %}{% block front-css %}<link rel="stylesheet" type="text/css" href="{% static 'css/news_auth/reset.min.css' %}"><link rel="stylesheet" type="text/css" href="{% static 'css/news_auth/login.min.css' %}">{% endblock %}{% block front-js %}<script src="{% static 'js/unslider.min.js' %}" type="text/javascript"></script><script src="{% static 'js/login.min.js' %}" type="text/javascript"></script><script src="{% static 'js/validateDialog.min.js' %}" type="text/javascript"></script>{% endblock %}{% block body %} <section><div class="c-box bg-box"><div class="login-box clearfix"><div class="hd-login clearfix"><a class="index-logo" href="/index/"></a><h1>小饭桌用户注册</h1><a class="index-font" href="/index/">回到首页</a></div><div class="fl slide"><div class="imgslide"><ul class="imgs"><li><a href=""><img width="483" height="472" src="{% static 'images/auth/57aa86a0000145c512000460.jpg' %}" /></a></li><li><a href=""><img width="483" height="472" src="{% static 'images/auth/57a801860001c34b12000460.jpg' %}" /></a></li><li><a href=""><img width="483" height="472" src="{% static 'images/auth/57a801860001c34b12000460.jpg' %}" /></a></li></ul></div><div class="unslider-arrow prev"></div><div class="unslider-arrow next"></div></div><div class="fl form-box"><div class="tab"><!--<h2 class="active">手机注册</h2>--><h2>手机号注册</h2></div><div class="tab-form"><form id="email_register_form" method="post" autocomplete="off">{% csrf_token %}<div class="form-group marb8 {% if register_form.errors.mobile.0 %} errorput {% endif %}"><label>手 机 号</label><input type="text" id="id_mobile" name="mobile" {% if register_form.errors.mobile.0 %} placeholder="{{ register_form.errors.mobile.0 }}" {% elif register_form.mobile.value %} value="{{ register_form.mobile.value }}" {% else %} placeholder="请输入您的手机号" {% endif %}/></div><div class="form-group marb8 {% if register_form.errors.username.0 %} errorput {% endif %}"><label>用 户 名</label><input type="text" class="form-control" name="username" {% if register_form.errors.username.0 %} placeholder="{{ register_form.errors.username.0 }}" {% elif register_form.username.value %} value="{{ register_form.username.value }}" {% else %} placeholder="请输入您的用户名" {% endif %}></div><div class="form-group marb8 captcha1 {% if register_form.errors.captcha.0 %} errorput {% endif %}"><label>验 证 码</label>{{ register_form.captcha }}</div><div class="error btns" id="jsEmailTips">{{ register_form.errors.captcha.0 }}</div><div class="form-group marb8 {% if register_form.errors.password1.0 %} errorput {% endif %}"><label>密 码</label><input type="text" class="form-control" name="password1" {% if register_form.errors.password1.0 %} placeholder="{{ register_form.errors.password1.0 }}" {% else %} placeholder="请输入密码" {% endif %}></div><div class="form-group marb8 {% if register_form.errors.password2.0 %} errorput {% endif %}"><label>确 认 密 码</label><input type="text" class="form-control" name="password2" {% if register_form.errors.password2.0 %} placeholder="{{ register_form.errors.password2.0 }}" {% else %} placeholder="请再次输入密码" {% endif %}></div><div class="form-group marb8 sms_captcha {% if register_form.errors.captcha.0 %} errorput {% endif %}"><label>短信验证码</label><div class="short-input-group"><input type="text" class="form-control" name="sms_captcha"></div><div class="input-group-addon"><span class="sms-captcha-btn">发送验证码</span></div></div><div class="error btns" id="jsEmailTips">{{ register_form.errors.sms_captcha.0 }}</div><input class="auth-btn btn-green" id="jsEmailRegBtn" type="submit" value="注册" /></form></div><p class="form-p">已有账号?<a href="{% url 'auth_login' %}">[立即登录]</a></p></div></div></div> </section>{% endblock %}
register.html
2. js 代码,给'发送验证码' 控件绑定点击事件(发送验证码事件),同时实现发送验证码时,该控件显示验证码发送倒计时:
function Auth(){var self = this;self.smsCaptcha = $('.sms-captcha-btn'); }// 短信验证码 发送短信 绑定点击事件 Auth.prototype.listenSmsCaptchaEvent = function (){var self = this;var smsCaptcha = $(".sms-captcha-btn");smsCaptcha.click(function () { // 给 '发送验证码'按钮 绑定点击事件var token = $('input[name=csrfmiddlewaretoken]').val();var mobileVal = $("#id_mobile").val();if(mobileVal.length == 0){ // 判断手机号位数是否为0alert("请输入手机号码!");}else if(mobileVal.length == 11) {$.ajax({cache: false,type: 'post',async: true,// dataType: 'json',url: "/sms_captcha/",data:{'mobile': mobileVal,csrfmiddlewaretoken: token},'success': function (result) {if(result['code'] == 200){ // 表示发送成功// result:{code: 200, message: ""} self.smsSuccessEvent();}},'fail': function (error) {console.log(error);}})}else{alert("请输入正确的手机号!")}}) };// 短信验证码倒计时 Auth.prototype.smsSuccessEvent = function(){var self = this;alert("短信验证码发送成功");self.smsCaptcha.addClass('disabled'); // 短信发送成功,令该按钮不能再点响应击事件var count = 60;self.smsCaptcha.unbind('click'); // 解绑点击事件var timer = setInterval(function () {self.smsCaptcha.text("已发送"+count+"s"); // 验证码倒计时count -= 1;if(count <= 0){clearInterval(timer); // 倒计时满1分钟,清除倒计时self.smsCaptcha.removeClass('disabled');self.smsCaptcha.text("发送验证码"); // 清除倒计时后,文本改回'发送验证码'self.listenSmsCaptchaEvent(); // 重新绑定点击事件 }},1000) };// 页面加载完成时执行下述内容 $(function () {// 发送短信验证码var auth = new Auth();auth.listenSmsCaptchaEvent(); });
3)使用缓存存储短信验证码:完成短信验证码相关功能之前,我们先完成缓存相关的一些配置
1. 下载 memcached
网上下载 memcached (win版),下载完成打开是这样的:
安装 memcached,进入终端,执行以下命名:
C:/memcached-1.2.1-win32/memcached.exe -d install # 前面是 下载后的memcached 存放的目录
安装完成后,通过以下命名启动 memcached:
C:/memcached-1.2.1-win32/memcached.exe -d start
在任务管理器中可以看到 memcached 服务已经启动:
此时的 memcached 便是安装完成,以后只要开启,就能正常用于各个项目中。
2. 在python - django 项目中使用
memcached 安装完成后,我们要在python - django 项目中使用,需要在项目中安装 python-memcached ,安装完成即可正常使用
pip install python-memcached # 虚拟环境
3. memcached 在项目settings.py 中配置:
# 缓存配置 CACHES = {'default': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache','LOCATION': '127.0.0.1:11211'} }
测试:
# pycharm Django console 环境下>>> from django.core.cache import cache >>> cache.set("haha",1234,60) >>> cache.get("haha") 1234 # 获取到的结果
4)完善短信验证码后端相关功能
views.py:
从前端点击 '发送验证码' 后,后端需要起一个函数来处理前端提交过来的信息,及返回数据(发送短信)
from django.http import JsonResponse from django.core.cache import cachefrom utils.random_code import generate_code from utils.aliyunsdk import aliyun_send_sms# @csrf_exempt # 短信验证码,可以不需要csrf_token验证 def sms_captcha(request):"""注册-发送短信验证码"""# /sms_captcha/?mobile=136***if request.method == "POST":mobile = request.POST.get('mobile')code = generate_code() # 四位数验证码cache.set(mobile, code, 5*60) # 以mobile为key,code为value,保存到缓存中,过期时间为5分钟print(cache.get(mobile)) # 查看是否已保存到缓存中send_sms_status = aliyun_send_sms(mobile=mobile, code=code) # 发送短信验证码if send_sms_status == 'OK':# {"code":400,"message":""} 返回json格式return JsonResponse({"code": 200, "message": ""})else:return JsonResponse({"code": 400, "message": "短信发送不成功!"})
urls.py配置:
from django.urls import path, include, re_path from xfz.views import sms_captchaurlpatterns = [path("sms_captcha/", sms_captcha, name='sms_captcha'),]
aliyun_send_sms.py:
阿里云短信验证码使用参考教程:https://www.cnblogs.com/Eric15/p/10925460.html
import json from aliyunsdkcore.client import AcsClient from aliyunsdkcore.request import CommonRequestfrom utils.random_code import generate_codedef aliyun_send_sms(mobile=None, code=None):"""阿里云发送短信"""client = AcsClient('******', '********', 'default') # <accessKeyId> 、<accessSecret> request = CommonRequest()request.set_accept_format('json')request.set_domain('dysmsapi.aliyuncs.com')request.set_method('POST')request.set_protocol_type('https') # https | httprequest.set_version('2017-05-25')request.set_action_name('SendSms')request.add_query_param('PhoneNumbers', mobile)request.add_query_param('SignName', "小饭桌网站")request.add_query_param('TemplateCode', "SMS_******")request.add_query_param('TemplateParam', json.dumps({'code': code})) # 以json 格式传递code参数 response = client.do_action(request) # 发送短信 response = str(response, encoding='utf-8') # 返回的response 为 <class 'bytes'> 类型,将其转换成str字符串类型response = json.loads(response) # 再通过json.loads,将str类型的 response 转换成dict 格式# print(response) # response:{'Message': 'OK', 'RequestId': 'F07A40C3-539C-453B-BE52-4B60FF8DF58E', 'BizId': '431121158876223912^0', 'Code': 'OK'}# print(response['Code']) # 获取 Code 值return response['Code']
aliyun_send_sms.py
random_code.py:
import randomdef generate_code():"""生成四位数的验证码"""seeds = "1234567890"random_str = []for i in range(4):random_str.append(random.choice(seeds))return "".join(random_str)
random_code.py
4)处理好图形验证码、短信验证码之后,接下来可以来完善注册相关的功能了。
1. forms.py:
import re from django import forms from django.core.exceptions import ValidationError from django.core.cache import cachefrom captcha.fields import CaptchaFieldclass RegisterFrom(forms.Form):"""用户注册form表单"""mobile = forms.CharField(required=True,max_length=11,min_length=11,error_messages={'min_length': '手机号只能是11位数!','max_length': '手机号只能是11位数!'})username = forms.CharField(required=True,max_length=20)# 生成验证码图片及输入框captcha = CaptchaField(required=True,error_messages={'invalid': '验证码错误','required': '验证码不能为空'})password1 = forms.CharField(required=True,min_length=8,error_messages={'required': '密码不能为空','min_length': '密码不能低于8个字符'})password2 = forms.CharField(required=True,min_length=8,error_messages={'required': '密码不能为空'})sms_captcha = forms.CharField(min_length=4, max_length=4)def clean_password1(self):if self.cleaned_data.get('password1').isdigit() or self.cleaned_data.get('password1').isalpha():raise ValidationError('密码必须包含数字和字母')else:return self.cleaned_data['password1']def clean_mobile(self):mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')mobile_value = self.cleaned_data.get('mobile')if not mobile_re.match(mobile_value):raise ValidationError('手机号码格式错误')else:return self.cleaned_data['mobile']def clean_sms_captcha(self):"""短信验证码存于cache中, 验证短信验证码是否正确"""mobile = self.cleaned_data.get('mobile')sms_captcha = self.cleaned_data.get('sms_captcha')cached_sms_captcha = cache.get(mobile)if not cached_sms_captcha or cached_sms_captcha != sms_captcha:raise ValidationError('短信验证码错误!')else:return self.cleaned_data['sms_captcha']def clean(self):cleaned_data = super(RegisterFrom, self).clean()if cleaned_data.get('password1') != cleaned_data.get('password2'):raise ValidationError('密码不一致')else:return self.cleaned_data
注册:forms.py
2. views.py:
from django.shortcuts import render, redirect, HttpResponse from django.contrib.auth import authenticate, login, logoutfrom .forms import RegisterFrom from users.models import UserProfiledef xfz_register(request):"""注册"""if request.method == "POST":register_form = RegisterFrom(request.POST)if register_form.is_valid():mobile = request.POST.get('mobile', None)if UserProfile.objects.filter(mobile=mobile):# 用户已经存在,不需要再注册return render(request, 'register.html', {'msg': '用户已经存在', 'register_form': register_form})pwd = request.POST.get('password1', None)username = request.POST.get('username', None)# 将密码加密后再保存pwd = make_password(pwd)user = UserProfile.objects.create(mobile=mobile,username=username,is_active=True,password=pwd)login(request, user)return redirect('/index/')else:return render(request, 'register.html', {'msg': '注册输入的数据有误', 'register_form': register_form})else:register_form = RegisterFrom()return render(request, "register.html", locals())
注册:views.py
3. urls.py:
from django.urls import path, include, re_path from xfz.views import xfz_registerurlpatterns = [path("register/", xfz_register, name='auth_register'),]
注册:urls.py
4. html:相关代码上面已经给出。
至此,注册的功能便完成了!
2.2、登录功能
1)users/models.py:
from django.db import models from django.contrib.auth.models import AbstractUser from datetime import datetimeclass UserProfile(AbstractUser):"""用户"""gender_choices = (('male','男'),('female','女'))nick_name = models.CharField('昵称', max_length=32, default='', null=True, blank=True)birthday = models.DateField('生日', null=True, blank=True)gender = models.CharField('性别', max_length=8, choices=gender_choices, default='female')mobile = models.CharField('手机号', max_length=11, null=True, unique=True)image = models.ImageField(upload_to='image/%Y/%m', default='image/default.png', max_length=100)email = models.EmailField('邮箱', blank=True, unique=True, null=True) # 重写email字段,加上'唯一'标识 REQUIRED_FIELDS = ['mobile', 'email'] # 通过manage.py...创建用户/管理员时,会提醒输入此字段(必输入)class Meta:verbose_name = '用户信息'verbose_name_plural = verbose_namedef __str__(self):return self.username
models.py
2)form.py:
import re from django import forms from django.core.exceptions import ValidationErrorclass LoginForm(forms.Form,common_forms.FormMixin):"""登录验证"""mobile = forms.CharField(required=True,max_length=11,min_length=11,error_messages={'min_length': '手机号只能是11位数!','max_length': '手机号只能是11位数!'})password = forms.CharField(required=True,min_length=8,error_messages={'min_length': '密码最少不能少于8个字符!'})remember = forms.IntegerField(required=False)
form.py
common_forms.py: 也可以像注册那样,直接在forms中写,不需要另外处理error
class FormMixin(object):"""用于获取表单验证失败时的错误信息,存进字典里"""def get_error(self):if hasattr(self, 'errors'):# 如果有errors这个属性errors = self.errors.get_json_data()new_errors = {}for key, message_dicts in errors.items():messages = []for message in message_dicts:messages.append(message['message'])new_errors[key] = messagesreturn new_errorselse:return {}
common_forms.py
3)views.py:
from django.db.models import Q from django.shortcuts import render, redirect from django.contrib.auth.backends import ModelBackend from django.contrib.auth import authenticate, login, logout from django.contrib.auth.hashers import make_passwordfrom .forms import LoginForm from users.models import UserProfileclass CustomBackend(ModelBackend):"""用于login用户登录验证需在settings中配置好authenticate验证方式(即在此进行authenticate的相关验证)"""def authenticate(self, request, username=None, password=None, **kwargs):# 重写authenticate方法try:user = UserProfile.objects.get(Q(username=username)|Q(mobile=username))if user.check_password(password):"""1.在注册时,我们对密码使用了加密处理(django下的make_password),因此在登录验证密码时需要先将明文密码加密后才能跟数据库中已加密后的密码进行比较2.check_password()是AbstractUser类中的方法,UserProfile继承于AbstractUser,check_password会加密明文密码后,与数据库密码做对比,再进行判断两密码是否一致3.验证如果为True,表示密码一致;为False,表示密码不一致"""return userelse:return Noneexcept Exception as e:return Nonedef xfz_login(request):"""登录"""login_form = LoginForm()if request.method == 'POST':login_form = LoginForm(request.POST)if login_form.is_valid():mobile = login_form.cleaned_data.get('mobile')password = login_form.cleaned_data.get("password")remember = login_form.cleaned_data.get('remember')user = authenticate(request, username=mobile, password=password)if user:if user.is_active:login(request, user)if remember:request.session.set_expiry(None)else:request.session.set_expiry(0)return redirect('/news/')else:return render(request, 'login.html', {'msg': '用户未激活', 'login_form': login_form})else:return render(request, 'login.html', {'msg': '用户名或密码错误', 'login_form': login_form})else:return render(request, 'login.html', {'msg': '用户名或密码格式错误,请重新输入!', 'login_form': login_form})return render(request, 'login.html')
views.py
4)login.html:
{% extends 'front_base.html' %}{% block title %}小饭桌登录{% endblock %}{% block front-css %}<link rel="stylesheet" type="text/css" href="{% static 'css/news_auth/reset.min.css' %}"><link rel="stylesheet" type="text/css" href="{% static 'css/news_auth/login.min.css' %}">{% endblock %}{% block front-js %}<script src="{% static 'js/unslider.min.js' %}" type="text/javascript"></script><script src="{% static 'js/login.min.js' %}" type="text/javascript"></script>{% endblock %}{% block body %} <section><div class="c-box bg-box"><div class="login-box clearfix"><div class="hd-login clearfix"><a class="index-logo" href="{% url 'index' %}"></a><h1>小饭桌用户登录</h1><a class="index-font" href="{% url 'index' %}">回到首页</a></div><div class="fl slide"><div class="imgslide"><ul class="imgs"><li><a href=""><img width="483" height="472" src="{% static 'images/auth/02_mid.jpg' %}"/></a></li><li><a href=""><img width="483" height="472" src="{% static 'images/auth/57a801860001c34b12000460.jpg' %}"/></a></li><li><a href=""><img width="483" height="472" src="{% static 'images/auth/course.jpg' %}"/></a></li></ul></div><div class="unslider-arrow prev"></div><div class="unslider-arrow next"></div></div><div class="fl form-box"><h2>帐号登录</h2><form action="{% url 'auth_login' %}" method="post" autocomplete="off">{% csrf_token %}<div class="form-group marb8 {% if login_form.errors.mobile.0 %} errorput {% endif %}"><label>手 机 号</label><input name="mobile" id="account_l" type="text" placeholder="手机号" {% if login_form.mobile.value %}value="{{ login_form.mobile.value }}"{% endif %}/></div><div class="error btns login-form-tips" id="jsLoginTips">{{ login_form.errors.mobile.0 }}</div><div class="form-group marb8 {% if login_form.errors.password.0 %} errorput {% endif %}"><label>密 码</label><input name="password" id="password_l" type="password" {% if login_form.password.value %}value="{{ login_form.password.value }}"{% endif %} placeholder="请输入您的密码"/></div><div class="error btns login-form-tips" id="jsLoginTips">{{ login_form.errors.password.0 }}</div><div class="error btns login-form-tips" id="jsLoginTips">{{ msg }}</div><div class="auto-box marb8"><label class="fr"><input type="checkbox" name="remember" value="1" class="remember-me">记住我</label> {# <a class="fr" href="{% url 'forgetpwd' %}">忘记密码?</a>#}</div><input class="auth-btn btn-green" id="jsLoginBtn" type="submit" value="立即登录 > "/> {# <input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy'/>#}</form><p class="form-p">没有小饭桌帐号?<a href="{% url 'auth_register' %}">[立即注册]</a></p></div></div></div> </section> {% endblock %}
login.html
5)继承:front_base.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>{% block title %}小饭桌{% endblock %}</title><link rel="stylesheet" href="{% static 'css/news/index.min.css' %}">{% block front-css %}{% endblock %}<script src="{% static 'js/jquery-3.3.1.min.js' %}"></script><script src="{% static 'js/index.min.js' %}"></script>{% block front-js %}{% endblock %} </head> <body> <!-- 导航栏 --> <header class="header"><div class="container"><!-- logo --><div class="logo-box"><a href=""></a></div><!-- 主体中间部分 标题 --><div class="nav"><ul class="daohangtiao"><li {% if request.path|slice:'5' == '/news' %}class="active"{% endif %} ><a href="{% url 'index' %}">资讯</a></li><li {% if request.path|slice:'8' == '/courses' %}class="active"{% endif %} ><a class="chuangyeketang" href="{% url 'course' %}">创业课堂</a></li><li><a class="qiyefuwu" href="#">企业服务</a></li><li {% if request.path|slice:'8' == '/payinfo' %}class="active"{% endif %} ><a href="{% url 'payinfo' %}">付费资讯</a></li><li {% if request.path|slice:'7' == '/search' %}class="active"{% endif %} ><a href="{% url 'search' %}">搜索</a></li></ul><div class="nav-float"><ul class="ketang"><li><a href="#">在线课堂</a></li><li><a href="#">线下课堂</a></li></ul><ul class="qiye"><li><a href="#">创业礼包</a></li><li><a href="#">企业资讯</a></li></ul></div></div><!-- 登录注册 --><div class="auth-box">{% if request.user.is_authenticated%}<div class="auth-login"><div class="personal"><div class="user"><p class="current-user">{{ request.user }}</p><span class="top-down"><img src="{% static 'images/auth/top_down.png' %}"/></span><span class="touxiang"><img width="45" height="45" src="{{ MEDIA_URL }}{{ request.user.image }}"/></span></div></div></div><div class="userdetail"><div class="personal-info"><span><img width="60" height="60" src="{{ MEDIA_URL }}{{ request.user.image }}"/></span><div class="user-info"><h2>{{ request.user }}</h2><p>{{ request.user.nick_name }}111</p></div></div><div class="personal-center"><a class="personcenter" href="#">进入个人中心</a><a class="fr" href="{% url 'auth_logout' %}">退出</a></div></div>{% else %}<p class="personal-p"></p><div class="auth-login"><a href="{% url 'auth_login' %}">登录 |</a><a href="{% url 'auth_register' %}"> 注册</a></div>{% endif %}</div></div> </header> {% block body %} <!-- body主体[中间部分] --> <div class="main"><div class="wrapper">{% block left-content %}<!-- 内容左边[新闻部分] --><div class="main-content-wrapper"><!-- 轮播图 --><div class="banner-group" id="banner-group"><ul class="banner-ul" id="banner-ul"><li><a href="#"><img src="{% static 'images/banners/lunbo_2.jpeg' %}" alt=""></a></li><li><a href="#"><img src="{% static 'images/banners/lunbo_3.jpg' %}" alt=""></a></li><li><a href="#"><img src="{% static 'images/banners/lunbo_4.jpg' %}" alt=""></a></li><li><a href="#"><img src="{% static 'images/banners/lunbo_5.png' %}" alt=""></a></li></ul><ul class="num"><li class="current"><a href="#">1</a></li><li><a href="#">2</a></li><li><a href="#">3</a></li><li><a href="#">4</a></li></ul><span class="left-btn btn">‹</span><span class="right-btn btn">›</span></div><!-- 新闻主体 --><div class="news-list-group"><div class="news-inner"><ul class="list-tab"><li class="active"><a href="#">最新资讯</a></li><li><a href="#">深度报道</a></li><li><a href="#">金融科技</a></li><li><a href="#">人工智能</a></li><li><a href="#">干货分享</a></li></ul><!-- 新闻list --><ul class="news-list"><li><div class="thumbnail-group"><a href="#"><img src="http://static-image.xfz.cn/1516169692_914.jpg-website.news.list"alt=""></a></div><div class="news-group"><p class="title"><a href="#">王健林卖掉进军海外首个项目:17亿售伦敦ONE六成股权</a></p><p class="desc">外界关于万达要出售此前在海外投资项目的消息一直不断。</p><p class="more"><span class="category">深度报道</span><span class="pub-time">1小时前</span><span class="author">知了课堂</span></p></div></li><li><div class="thumbnail-group"><a href="#"><img src="http://static-image.xfz.cn/1516169692_914.jpg-website.news.list"alt=""></a></div><div class="news-group"><p class="title"><a href="#">王健林卖掉进军海外首个项目:17亿售伦敦ONE六成股权</a></p><p class="desc">外界关于万达要出售此前在海外投资项目的消息一直不断。</p><p class="more"><span class="category">深度报道</span><span class="pub-time">1小时前</span><span class="author">知了课堂</span></p></div></li></ul><div class="load-more-group"><button class="load-more">查看更多</button></div></div></div></div>{% endblock %}{% block right-wrapper %}<!-- 内容右边[侧边栏] --><div class="sidebar-wrapper"><div class="online-class"><span class="class-title">在线课堂</span><span class="more"><a href="#">更多</a></span></div><div class="hot-advertist"><a href="#"><img src="{% static 'images/build/hot-advertist.png' %}" alt=""></a></div><div class="platform-group"><div class="online-class"><span class="class-title">关注小饭桌</span></div><div class="focus-group"><ul class="left-group"><li class="zhihu"><a href="#" target="_blank">小饭桌创业课堂</a></li><li class="weibo"><a href="#" target="_blank">小饭桌创业课堂</a></li><li class="toutiao"><a href="#" target="_blank">小饭桌</a></li></ul><div class="right-group"><p class="desc">扫码关注小饭桌微信公众平台xfz008</p></div></div></div><div class="hot-news-group"><div class="online-class"><span class="class-title">热门推荐</span></div><ul class="hot-list-group"><li><div class="left-group"><p class="title"><a href="#">王健林卖掉进军海外首个项目:17亿售伦敦ON...</a></p><p class="more"><span class="category"><a href="#">深度报道</a></span><span class="pub-time">1小时前</span></p></div><div class="right-group"><a href="#"><img src="{% static 'images/build/hot-news_01.png' %}" alt=""></a></div></li><li><div class="left-group"><p class="title"><a href="#">王健林卖掉进军海外首个项目:17亿售伦敦ON...</a></p><p class="more"><span class="category"><a href="#">深度报道</a></span><span class="pub-time">1小时前</span></p></div><div class="right-group"><a href="#"><img src="{% static 'images/build/hot-news_01.png' %}" alt=""></a></div></li></ul></div></div>{% endblock %}</div> </div> {% endblock %}<!-- footer --> <footer class="footer"><div class="top-group"><div class="top-inner-group"><div class="logo-box"></div><div class="detail-group"><div class="line1"><ul class="links"><li><a href="#">关于小饭桌</a></li><li><a href="#">创业课堂</a></li><li><a href="#">寻求报道</a></li><li><a href="#">创业礼包</a></li></ul><div class="about-us"><span class="title">关于我们:</span><ul class="social-group"><li class="weixin"><div class="wx-qrcode"></div><span class="text">xfz2019</span></li><li class="weibo"><a href="#" class="text">小饭桌创业课堂</a></li></ul></div></div><div class="line2"><p class="address">地址:北京市朝阳区东三环北路38号院1号楼17层2001内1、16室</p><p class="contact">联系方式:400-810-1090(工作日10点-18点)</p></div></div></div></div><div class="bottom-group">©2017 北京子木投资顾问有限公司 京ICP备15051289号-1</div> </footer></body></html>
front_base.html
6)urls.py:
from django.urls import path,include, re_path from xfz.views import xfz_logouturlpatterns = [path("login/", xfz_login, name='auth_login'),]
urls.py
2.3、退出登录功能
退出登录功能比较简单,只需要views.py 及 urls.py相关代码处理即可,如下
1. views.py:
from django.contrib.auth import logoutdef xfz_logout(request):logout(request)return render(request, 'news/index.html')
2. urls.py:
from django.urls import path, include, re_path from xfz.views import xfz_logouturlpatterns = [path("logout/", xfz_logout, name='auth_logout'),]
至此,小饭桌登录及注册相关的功能实现到这就算完成了!
转载于:https://www.cnblogs.com/Eric15/articles/10681450.html
Django打造大型企业官网-项目实战(二)相关推荐
- Django打造大型企业官网-项目实战(四)
Django打造大型企业官网-项目实战(四) 一.新闻相关功能 在项目实战三中,我们完成了新闻分类相关的一些功能,现在我们来完成新闻列表.发布新闻.编辑新闻.删除新闻的功能 1.发布新闻/编辑新闻 功 ...
- Django打造大型企业官网-项目实战(三)
Django打造大型企业官网-项目实战(三) 一.CRM 后台管理系统 前面我们使用的是 xadmin 后台管理系统,在使用中发现,在权限限制中,我们能实现不同等级的用户/管理(超级管理员/管理员/用 ...
- Django打造大型企业官网-项目部署
Django打造大型企业官网-项目部署 一.准备工作 1.在开发机上的准备工作 1)确认项目没有bug. 2)打开终端,进入虚拟环境,再 cd 到项目根目录下,执行命令:pip freeze > ...
- Django打造大型企业官网(五)
4.6.切换轮播图的箭头样式以及显示和隐藏 templates/news/index.html <span class="arrow left-arrow">‹< ...
- Taro多端开发实现原理与项目实战(二)
Taro多端开发实现原理与项目实战(二) 多端电商平台项目概述及开发准备 学习了前面的基础知识和进阶后是否跃跃欲试?我们准备了一个电商平台的项目来和大家一起实践使用 Taro 开发电商平台. 项目概述 ...
- 鲜花网项目实战(一)
鲜花网项目实战(一) 1.开发环境和技术栈 2.安装NodeJS环境 一.环境的作用 二.下载地址 三.查看安装 3.安装Vue-CLI 一.脚手架工具介绍 二.安装命令 三.查看安装 4.创建前端项 ...
- 学院官网项目三级页面总结
学院官网项目三级页面总结 (撰写时间:2019年7月6作者:李梦熙) 这个六月我来到了这个项目班,在这一个多月的时间里,虽然有时候还是不知道去做些什么,但是我在这里也学习到了很多项目管理和分配工作之类 ...
- 视频教程-Ruby on Rails打造企业级RESTful API项目实战我的云音乐-Ruby/Rails
Ruby on Rails打造企业级RESTful API项目实战我的云音乐 任苹蜻,爱学啊创始人 & CEO,曾就职于某二车手公司担任Android工程师后离职创办爱学啊,我们的宗旨是:人生 ...
- 仿照小米官网项目具体操作与细节
本项目已上传github 有需要的可以下载 点这里前往下载 小米官网项目具体操作 1.gulp的搭建 一 , 打开node控制台 命令行输入 cd 加文件夹路径 进入当前文件夹 命令行输入 cnpm ...
- 牛客网SQL实战二刷 | Day10
「牛客网SQL实战二刷」是个系列学习笔记博文,今天解析7道SQL题目- 第55 - 61题. 每篇笔记的格式大致为,三大板块: 大纲 题目(题目描述.思路.代码.相关参考资料/答疑) 回顾 ❤️「往期 ...
最新文章
- org 07c00h的原因
- BZOJ3163 [Heoi2013]Eden的新背包问题
- HDFS重复上传文件将会覆盖原文件
- 第二章java编程基础测试题_Java编程基础测试题分享
- 【水滴石穿】react-native-book
- Android——实现欢迎界面的自动跳转(转)
- leetcode944. 删列造序
- 小米、QQ等8款手机浏览器发布自查整改公告;AMD宣布收购赛灵思;​TypeScript 4.0.5发布|极客头条
- MySQL 服务的启动与停止
- asp.net下ajax.ajaxMethod使用方法
- kali社工密码字典生成
- cocos creator2.2.2休闲游戏(单机捕鱼)源码H5+安卓+IOS三端源码
- 基于深度学习的农作物病虫害识别
- 图像处理--边缘检测
- Charles ios无法下载证书- chls.pro/ssl一直加载治标办法
- 联想S41-70拆机换内存条
- java计算器sqrt_比较完整的Java计算器
- cnn是深度神经网络吗,cnn神经网络算法
- 出自ios深入浅出专栏(内购)
- springboot windows下WORD文档转PDF
热门文章
- 为什么不推荐使用BeanUtils属性转换工具,老程序员都不使用!
- BAT等大厂十年研发经历,总结了12开发条经验(墙裂推荐)
- LVS负载均衡的几种模式和算法
- Android 高效开发调试神器 JRebel
- Android Studio 2.0来啦
- 两个形状不同的长方形周长_三年级上册数学《周长》同步练习,附答案
- python3.7安装pyltp出错_安装pyltp遇到的问题及解决办法
- 根据录入的计算公式计算_预制桩极限承载力标准值快速计算方法研究
- 神奇的数学:牛津教授给青少年的讲座
- 分布式系统理论(二):一致性协议Paxos