当前位置: 首页 > 编程日记 > 正文

为 Django admin 登录页添加验证码

为什么80%的码农都做不了架构师?>>>   hot3.png

历史原因,使用上古版本 django 1.6.5,但新版本应该大同小异

首先添加自定义后台模块app, 如adm,并添加到 INSTALLED_APPS 下。

假设处理自定义登录的view是 apps/adm/views/custom_admin.py 中的 custom_login 函数

配置url:


from django.contrib import admin
from adm.views.custom_admin import custom_loginadmin.autodiscover()
admin.site.login = custom_loginurlpatterns += (url(r'^custom-admin/', include(admin.site.urls)),url(r'^custom-admin/', include('adm.urls')),
)

登录页中有两种形式,一种是本地和测试环境, 输入用户名加密码加captch三项,然后登录,此处的captcha都是修改的django的表单,不少代码是搬运的django-simple-captcha,因为这个库使用的话还需要添加它的model进行migrate,所以没直接使用;另一种是线上环境,需要输入用户名(手机号)加密码加短信验证码三项,并且短信验证码的需要点击获取,正确输入弹出的captcha并且用户名在setting中的白名单才可以获取,此处的captcha是后台的api获取,而非表单。

view如下(需要自己实现生成captcha图片和发送短信的api):


from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login
from django.contrib.sites.models import get_current_site
from django.http import HttpResponseRedirect, HttpResponse
from django.template.response import TemplateResponse
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters
from django.utils.encoding import force_textfrom adm.forms import SmscodeAdminAuthenticationForm, MultiCaptchaAdminAuthenticationForm
from settings import  ADMIN_ENABLE_SMS_LOGIN, ADMIN_PHONES, ADMIN_SHOW_DELETE_SELECTEDif ADMIN_ENABLE_SMS_LOGIN:admin.AdminSite.login_form = SmscodeAdminAuthenticationFormAuthForm = SmscodeAdminAuthenticationForm
else:admin.AdminSite.login_form = MultiCaptchaAdminAuthenticationFormAuthForm = MultiCaptchaAdminAuthenticationForm# 根据设置禁用批量删除
if not ADMIN_SHOW_DELETE_SELECTED:admin.site.disable_action('delete_selected')@sensitive_post_parameters()
@csrf_protect
@never_cache
def custom_login(request, template_name='custom_login.html',redirect_field_name=REDIRECT_FIELD_NAME,authentication_form=AuthForm,current_app=None, extra_context=None):redirect_to = request.REQUEST.get(redirect_field_name, '')if request.method == "POST":ua = request.META['HTTP_USER_AGENT'].strip()ip = request.META['REMOTE_ADDR']username = request.POST.get('username')logtrade.info('try login. username:%s' % username)if username not in ADMIN_PHONES:password = request.POST.get('password')logging.error(u'登录失败! 用户名不在白名单:%s, input password:%s, IP:%s, UserAgent:%s' % (username, password, ip, ua))form = authentication_form(request, data=request.POST)if form.is_valid():auth_login(request, form.get_user())return HttpResponseRedirect(redirect_to)else:form = authentication_form(request)current_site = get_current_site(request)context = {'form': form,redirect_field_name: redirect_to,'site': current_site,'site_name': current_site.name,'sms_login': ADMIN_ENABLE_SMS_LOGIN,}if extra_context is not None:context.update(extra_context)return TemplateResponse(request, template_name, context,current_app=current_app)

其中两个表单的实现如下:
大部分功能使用了django-simple-captcha的代码,主要的改动是把captcha文本存在缓存里,使用session_key关联,取消了数据库的使用

# encoding=utf-8from django.contrib.admin.forms import AdminAuthenticationFormfrom django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse, NoReverseMatch
from django import forms
from django.forms import ValidationError
from django.forms.fields import CharField, MultiValueField
from django.forms.widgets import TextInput, MultiWidget, HiddenInput
from django.utils.translation import ugettext, ugettext_lazy
from six import u
import base64
from django.core.cache import cache
from settings import ADMIN_PHONES# utils为自己实现的库,包括生成captcha,发送短信等
from utils.captcha_util import get_captcha_by_text, get_code_text, compare_code_ignore_case
from utils.sms_util import verify_sms_codeCAPTCHA_OUTPUT_FORMAT = u'%(image)s %(hidden_field)s %(text_field)s'def get_captcha(key):return cache.get('captcha_' + key)def set_captcha(key):captcha_text = get_code_text()  # 获取captcha文本cache.set('captcha_' + key, captcha_text, 30 * 60)def delete_captcha(key):cache.delete('captcha_' + key)class BaseCaptchaTextInput(MultiWidget):"""Base class for Captcha widgets"""def __init__(self, attrs=None):self.session_key = attrs.get('session_key')widgets = (HiddenInput(attrs),TextInput(attrs),)super(BaseCaptchaTextInput, self).__init__(widgets, attrs)def decompress(self, value):if value:return value.split(',')return [None, None]def fetch_captcha_store(self, name, value, attrs=None):"""Fetches a new CaptchaStoreThis has to be called inside render"""#key = CaptchaStore.generate_key()key = self.session_key  # 用session_key关联captchaset_captcha(key)# these can be used by format_output and renderself._value = [key, u('')]self._key = keyself.id_ = self.build_attrs(attrs).get('id', None)def id_for_label(self, id_):if id_:return id_ + '_1'return id_def image_url(self):# 返回的不是url而是图片的base64编码text = get_captcha(self.session_key)captcha_content = get_captcha_by_text(text)return base64.b64encode(captcha_content)def refresh_url(self):return reverse('captcha-refresh')class CaptchaTextInput(BaseCaptchaTextInput):def __init__(self, attrs=None, **kwargs):self._args = kwargsself._args['output_format'] = self._args.get('output_format') or CAPTCHA_OUTPUT_FORMATself._args['id_prefix'] = self._args.get('id_prefix')for key in ('image', 'hidden_field', 'text_field'):if '%%(%s)s' % key not in self._args['output_format']:raise ImproperlyConfigured('All of %s must be present in your CAPTCHA_OUTPUT_FORMAT setting. Could not find %s' % (', '.join(['%%(%s)s' % k for k in ('image', 'hidden_field', 'text_field')]),'%%(%s)s' % key))super(CaptchaTextInput, self).__init__(attrs)def build_attrs(self, extra_attrs=None, **kwargs):ret = super(CaptchaTextInput, self).build_attrs(extra_attrs, **kwargs)if self._args.get('id_prefix') and 'id' in ret:ret['id'] = '%s_%s' % (self._args.get('id_prefix'), ret['id'])return retdef id_for_label(self, id_):ret = super(CaptchaTextInput, self).id_for_label(id_)if self._args.get('id_prefix') and 'id' in ret:ret = '%s_%s' % (self._args.get('id_prefix'), ret)return retdef format_output(self, rendered_widgets):hidden_field, text_field = rendered_widgetstext_field = text_field.replace('<input', '<input autocomplete="off"')return self._args['output_format'] % {'image': self.image,'hidden_field': hidden_field,'text_field': text_field}def render(self, name, value, attrs=None):self.fetch_captcha_store(name, value, attrs)self.image = '<img src="https://img-blog.csdnimg.cn/2022010623403229180.png" alt="captcha" class="captcha" />' % self.image_url()return super(CaptchaTextInput, self).render(name, self._value, attrs=attrs)class CaptchaField(MultiValueField):def __init__(self, session_key=None, *args, **kwargs):self.session_key = session_keyfields = (CharField(show_hidden_initial=True),CharField(),)if 'error_messages' not in kwargs or 'invalid' not in kwargs.get('error_messages'):if 'error_messages' not in kwargs:kwargs['error_messages'] = {}kwargs['error_messages'].update({'invalid': ugettext_lazy('Invalid CAPTCHA')})attrs = {'session_key': session_key}kwargs['widget'] = kwargs.pop('widget', CaptchaTextInput(attrs=attrs,output_format=kwargs.pop('output_format', None),id_prefix=kwargs.pop('id_prefix', None)))super(CaptchaField, self).__init__(fields, *args, **kwargs)def compress(self, data_list):if data_list:return ','.join(data_list)return Nonedef clean(self, value):super(CaptchaField, self).clean(value)response, value[1] = (value[1] or '').strip().lower(), ''correct_captcha = cache.get('captcha_' + self.session_key)if not compare_code_ignore_case(response, correct_captcha):raise ValidationError(getattr(self, 'error_messages', {}).get('invalid', ugettext_lazy('Invalid CAPTCHA')))delete_captcha(self.session_key)return valueclass MultiCaptchaAdminAuthenticationForm(AdminAuthenticationForm):def __init__(self, request=None, *args, **kwargs):if not request.session.session_key:request.session.create()session_key = request.session.session_keysuper(MultiCaptchaAdminAuthenticationForm, self).__init__(*args, **kwargs)self.fields['captcha'] = CaptchaField(session_key=session_key)class SmscodeField(CharField):"""短信验证码字段"""def __init__(self, cellphone=None, *args, **kwargs):self.cellphone = cellphoneself.required = Truesuper(SmscodeField, self).__init__(*args, **kwargs)def clean(self, value):super(SmscodeField, self).clean(value)# 登录用户名必须是setting白名单中设置的手机号if self.cellphone not in ADMIN_PHONES:params = {'username': u"用户名"}raise forms.ValidationError(u'用户名或密码有误', code='invalid', params=params)if not verify_sms_code(cellphone=self.cellphone, sms_code=value):params = {'smscode': u'短信验证码'}raise forms.ValidationError(u'短信验证码有误', code='invalid', params=params)class SmscodeAdminAuthenticationForm(AdminAuthenticationForm):def __init__(self, request=None, *args, **kwargs):if not request.session.session_key:request.session.create()super(SmscodeAdminAuthenticationForm, self).__init__(*args, **kwargs)self.fields['smscode'] = SmscodeField(cellphone=self.data.get('username'))

登录页的模板 custom_login.html, 找前端同学写的js和样式:

{% extends "admin/base_site.html" %}
{% load i18n admin_static %}{% block extrastyle %}
{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/login.css" %}" />
<link rel="stylesheet" type="text/css" href="{% static "css/custom_form.css" %}" />
{{ block.super }}{% endblock %}{% block bodyclass %}login{% endblock %}{% block nav-global %}{% endblock %}{% block content_title %}{% endblock %}{% block breadcrumbs %}{% endblock %}{% block content %}
{% if form.errors and not form.non_field_errors and not form.this_is_the_login_form.errors %}
<p class="errornote">
{% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}
</p>
{% endif %}{% if form.non_field_errors or form.this_is_the_login_form.errors %}
{% for error in form.non_field_errors|add:form.this_is_the_login_form.errors %}
<p class="errornote">{{ error }}
</p>
{% endfor %}
{% endif %}<div id="content-main"><form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}<div class="form-row">{% if not form.this_is_the_login_form.errors %}{{ form.username.errors }}{% endif %}<label for="id_username" class="required">{{ form.username.label }}:</label> {{ form.username }}</div><div class="form-row">{% if not form.this_is_the_login_form.errors %}{{ form.password.errors }}{% endif %}<label for="id_password" class="required">{% trans 'Password:' %}</label> {{ form.password }}<input type="hidden" name="this_is_the_login_form" value="1" /><input type="hidden" name="next" value="{{ next }}" /></div>{% if sms_login %}<div class="form-row">{% if not form.this_is_the_login_form.errors %}{{ form.smscode.errors }}{% endif %}<label for="smscode" class="required">短信验证码: </label><a href="javascript:void(0);" class="getSmscode" id="getSmscode" data-click="0" onclick="getMsgCode()">点击获取短信验证码</a><div><th width="920">{{ form.smscode }}</th></div></div>{% else %}<div class="form-row">{% if not form.this_is_the_login_form.errors %}{{ form.captcha.errors }}{% endif %}{{ form.captcha }}</div>{% endif %}<div class="submit-row"><label>&nbsp;</label><input type="submit" value="{% trans 'Log in' %}" /></div>
</form><div class="captcha-cover"><div class="form-row"><div class="captchaImg"><img id="captchaImg" src="" alt="" onclick="getCaptcha()"><span onclick="getCaptcha()">点击更换图片</span></div><div class="text-box"><input placeholder="请输入上图中的验证码" id="captchaText" value=""></div><div class="control-btn"><a href="javascript:void 0;" onclick="hideModal()">取消</a><div class="line"></div><a href="javascript:void 0;" class="current" onclick="getSmscode()">确定</a></div></div>
</div><script type="text/javascript" src="{% static "admin/js/jquery.min.js" %}"></script><script type="text/javascript">
{% if sms_login %}// 获取图片验证码
function getCaptcha() {var captchaUrl = "/custom-admin/captcha";$.ajax({type: "GET",url: captchaUrl,dataType: "json",success: function (data) {if (data.ret) {document.getElementById("captchaImg").src = "data:image/png;base64," + data.captcha;} else {console.log(data.msg);}}})
}//弹窗的显示和隐藏
function getMsgCode(){$(".captcha-cover").css("display","block");getCaptcha();
}
function hideModal(){$(".captcha-cover").css("display","none");
}$("#captchaText").on("keypress",function(event){if(event.keyCode===13){getSmscode();}
})// 获取短信
function getSmscode(){var smscodeUrl = "/custom-admin/smscode";var captchaText = document.getElementById("captchaText").value;var cellphone = document.getElementById("id_username").value;var data = {'captcha': captchaText,'cellphone': cellphone,};$.ajax({type: "POST",url: smscodeUrl,dataType: "json",data: data,success: function (data) {if (data.ret) {hideModal();} else {alert(data.msg);getCaptcha();}}})
}
{% endif %}
</script>
</div>
{% endblock %}

样式:

.login .form-row #id_smscode {clear: both;padding: 6px;width: 100%;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;
}.captcha-cover{display: none;position:fixed;left:0;top:0;width:100%;height:1000px;background-color:rgba(0,0,0,0.5);text-align:center;
}.captcha-cover .form-row{padding-top:40px;display: inline-block;float: inherit!important;width:320px;margin:150px auto;background-color:#fff;padding-bottom:0;
}
.captchaImg{padding:0 30px;display: inline-block;margin-bottom:10px;
}
#captchaImg{float:left;display:inline-block;width: 100%;padding:6px 2px;border:1px solid #000;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;vertical-align:middle;
}
#captchaText{display:inline-block;vertical-align:middle;width:100%;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;
}
.captchaImg span{cursor:pointer;margin-left:20px;display: inline-block;line-height:40px;
}
#captchaImg{padding:2px 10px;width:100px;height:38px;
}
.text-box{padding:0 50px;
}
#captchaText{height:40px;box-sizing: border-box;
}
.control-btn{position:relative;margin-top:20px;border-top:1px solid #ededed;
}
.control-btn a{display: inline-block;width:49%;text-align:center;box-sizing: border-box;font-size:14px;line-height:44px;color:#666;
}
.control-btn a.current{color:dodgerblue;
}
.line{position:absolute;top:-1px;left:50%;width:1px;height:45px;background-color:#ededed;
}

最后效果如下

转载于:https://my.oschina.net/sukai/blog/1604584

相关文章:

手机芯片谁是AI之王?高通、联发科均超华为

整理 | apddd出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;尽管相当数量的人工智能服务&#xff0c;是由云计算网络提供&#xff0c;但在响应低延迟、保护隐私、应用场景等方面&#xff0c;手机AI芯片无可替代。例如人脸解锁&#xff0c;图像增强、识别&#xff0c…

【linux】error: stdio.h: No such file or directory

ubuntu 默认没有C和C编译环境 ubuntu 默认没有C和C编译环境&#xff0c;新装的ubuntu&#xff0c;使用gcc编译时&#xff0c;会报错&#xff0c;找不到某某头文件等。在编译一个demo&#xff0c;如hello world时&#xff0c;会报错&#xff1a; error: stdio.h: No such file …

SqlParameter的用法

1。一些面例子为例&#xff1a;List<SqlParameter> listp new List<SqlParameter>();listp.Add(new SqlParameter("userid", userid));string sql "select * from userbooks where useriduserid";在执行sql语句时 listp.Add(new SqlParamete…

程序员如何用“撞针“拯救35亿地球人?

春节假期即将结束&#xff0c;有多少程序员朋友已经离开家乡在返回北上广深等工作所在城市的路上&#xff1f;有多少程序员已经开工大吉开始了新一年的代码征程&#xff1f;回首这一个春节&#xff0c;8 部电影在大年初一齐上线&#xff0c;《流浪地球》在前期预售票房远不如《…

【linux】printf在终端打印彩色hello world

代码 #include <stdio.h>#define NONE "\033[m" #define RED "\033[0;32;31m" #define GREEN "\033[0;32;32m" #define BLUE "\033[0;32;34m" #define YELLOW "\033[1;33m&qu…

看腾讯运维应对“18岁照片全民怀旧”事件的方案,你一定不后悔!

作者丨魏旸&#xff1a;腾讯高级工程师&#xff0c;15年运维经验的老专家&#xff0c;负责QQ空间、微云、QQ空间相册的运维工作&#xff0c;亲历8亿军装照、QQ空间异地多活建设等重大架构升级事件。2017年12月30日&#xff0c;元旦假期的第一天&#xff0c;你的朋友圈被18岁照片…

基于svnserve的SVN服务器(windows下安装与配置)

基于svnserve的SVN服务器(windows下安装与配置)关键字: svn 安装SVNserve 从http://subversion.tigris.org/servlets/ProjectDocumentList?folderID91得到最新版本的Subversion。 如果你已经安装了Subversion&#xff0c;svnserve已经运行&#xff0c;你需要在继续之前把它停…

【c语言】C语言配置文件解析库——iniparser

转载自&#xff1a;http://blog.csdn.net/u011192270/article/details/49339071 C语言配置文件解析库——iniparser前言&#xff1a;在对项目的优化时&#xff0c;发现Linux下没有专门的供给C语言使用的配置文件函数&#xff0c;于是搜索到了iniparser库&#xff0c;可以像那些…

利用java虚拟机的工具jmap分析java内存情况

2019独角兽企业重金招聘Python工程师标准>>> 有时候碰到性能问题&#xff0c;比如一个java application出现out of memory,出现内存泄漏的情况&#xff0c;再去修改bug可能会变得异常复杂&#xff0c;利用工具去分析整个java application 内存占用情况&#xff0c;然…

代码测试意味着完全消灭了Bug?

日前&#xff0c;一位名为 Jens Neuse 的开发者在改进其 graphql 解析库的过程中&#xff0c;发现词法分析器和解析器中存在很多的低效率&#xff0c;因此不得不重构完整的代码库&#xff08;https://medium.com/jens.neuse/want-to-write-good-unit-tests-in-go-dont-panic-or…

Android系列讲座(2):为TextView组件加上边框

本文为原创&#xff0c;如需转载&#xff0c;请注明作者和出处&#xff0c;谢谢&#xff01; 源代码Android系统本身提供的TextView组件并不支持边框&#xff0c;但可以对TextView进行扩展来添加边框。我们可以使用如下两种方法为TextView组件添加边框。 1. 编写一个继承TextV…

如何用Python做三阶拼图?

作者 | Ahab来源 | Ahab杂货铺&#xff08;ID:AhabBin&#xff09;今天是年假的最后一天&#xff0c;明天大家就要回到各自的工作岗位上去&#xff0c;公众号从今天开始恢复更新。不知道大家假期过得怎么样&#xff0c;帅张提到假期多关注一些家里的变化&#xff0c;自己也记录…

【C语言】libiniparser库使用例子,

libiniparser是C语言实现的ini文件解析库 使用实例如下 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h>#include "iniparser.h"void create_example_ini_file(void); int parse_ini_file(char * ini_nam…

laravel5.4 关于数据填充的知识

需求&#xff1a;大量excel表格数据 集中整理到一个规定数据表中&#xff0c;并且增加新字段做标记步骤&#xff1a;把需要整理的excel表格提前存放到mysql数据库指定的表中 &#xff0c;可以用图形化工具来执行&#xff01; 核心&#xff1a;利用laravel5.4 框架自带的填充功能…

windows 域环境应用---VNC远程协助工具的使用

域环境中&#xff0c;少不了远程协助工具&#xff0c;这可以替管理员省掉许多跑腿的事。<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />今天我就介绍一下远程协助工具中的一款real VNC.此工具有三个软件&#xff0c;vnc server…

从技术角度分析“抢票软件的加速”有多快?

作者 | 上海小胖来源 | Python专栏&#xff08;ID&#xff1a;xpchuiit&#xff09;大部分人在昨天前天已经回程了&#xff0c;不知道大家什么状态。我有2个状态并存&#xff1a;1.快点上班。2.继续休息。为什么会有这2种状态呢&#xff0c;还得从娃说起。昨天月嫂回家一天&am…

【物联网】NB-IoT简介

一、“我”是谁&#xff1f; NB-IoT&#xff1a;基于蜂窝的窄带物联网&#xff08;Narrow Band Internet of Things, NB-IoT&#xff09;成为万物互联网络的一个重要分支。NB-IoT构建于蜂窝网络&#xff0c;只消耗大约180KHz的带宽&#xff0c;可直接部署于GSM网络、UMTS网络或…

CES上百度无人车队炫技,陆奇要用“China speed”改变世界

本文由 「AI前线」原创&#xff0c;原文链接&#xff1a;CES上百度无人车队炫技&#xff0c;陆奇要用“China speed”改变世界编辑&#xff5c;EmilyAI 前线导读&#xff1a;“美国当地时间 1 月 8 日&#xff0c;百度在拉斯维加斯举办了主题为“AI is Changing the World, Chi…

《Microsoft Sql server 2008 Internals》读书笔记--第九章Plan Caching and Recompilation(10)

《Microsoft Sql server 2008 Internals》读书笔记订阅地址&#xff1a; http://www.cnblogs.com/downmoon/category/230397.html/rss 《Microsoft Sql server 2008 Internals》索引目录&#xff1a; 《Microsoft Sql server 2008 Internal》读书笔记--目录索引 上文主要介…

小心!你的脸正在成为色情片主角……

编译丨福尔摩望本文经授权转载自 猎云网&#xff08;ID&#xff1a;ilieyun&#xff09;【导语】那的确是她的脸&#xff0c;但是在她不知情的情况下被无缝地移植到了别人的身体上。网络上曾出现过这样的视频&#xff1a;一名女子穿着粉色肩膊上衣&#xff0c;坐在床上&#xf…

【modbus】libmodbus库的移植与使用

一、源码下载 1、libmodbus官网 http://libmodbus.org/download/ 2、选择长期稳定版本libmodbus-3.0.6.tar.gz 下载链接&#xff1a;http://libmodbus.org/releases/libmodbus-3.0.6.tar.gz 3、MD5值 c80f88b6ca19cabc4ceffc195ca07771 libmodbus-3.0.6.tar.gz …

IntelliJ IDEA控制台输出中文乱码问题解决

如果还不行&#xff0c;那么再极端的设置&#xff0c;在IDEA启动的时候强制设置为UTF-8&#xff1a; 打开增加-Dfile.encodingUTF-8&#xff0c;重启Intellij IDEA 再或者直接在项目运行的时候加入UTF-8的设置 如果还是不行&#xff0c;那么你可能装了一个假的IDEA。

【数据库】mysql移植

一、源码下载 1、下载mysql源码 源码下载地址&#xff1a;选择版本&#xff1a;5.1.72&#xff08;这是个老版本&#xff0c;高版本需要使用cmake&#xff09; https://cdn.mysql.com/archives/mysql-5.1/mysql-5.1.72.tar.gz 历史版本下载地址 源码官网地址&#xff0c;在Oper…

HTML添加上传图片并进行预览

使用说明&#xff1a;新建文件&#xff0c;直接复制粘贴&#xff0c;保存文件为html 格式&#xff0c;在浏览器运行即可&#xff1b; 第一种&#xff1a; <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loos…

滴滴裁员2000多人,去年亏损超百亿

作者 | 非主流出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;据多家媒体的消息&#xff0c;在今天&#xff08;1 月 15 日&#xff09;上午的月度全员会上&#xff0c;滴滴 CEO 程维宣布公司将做好过冬准备&#xff0c;2019年会聚焦当前最重要的出行主业&#xff0c…

自己写的Treeview控件绑定数据源

首先在数据库中建表&#xff0c;表的格式如下id name parent_id代码如下&#xff1a;/// <summary>/// 添加根节点/// </summary>/// <param name"list">表的所有数据orm的List集合&#xff0c;object为表对应的实体对象</param>private…

【GDB】gdbserver的使用,远程调试开发板

0、将gdbserver拷贝到目标板子上 注:gdbserver在交叉编译工具目录/debug-root/usr/bin下 可用file命令查看 ------------------------------------------------------------ $ file gdbserver gdbserver: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linke…

关于javascript代码优化的8点建议

前面的话 本文将详细介绍JS编程风格的几个要点 松耦合 当修改一个组件而不需要更改其他组件时&#xff0c;就做到了松耦合 1、将JS从CSS中抽离&#xff1a;不要使用CSS表达式 //不好的做法 .box{width: expression(document.body.offsetWidth ’px)} 2、将CSS从JS中抽离&#…

英伟达Q4净利同比降49%,还能继续躺赚吗?

图片来自英伟达官网整理 | 非主流出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;北京时间 2 月 15 日凌晨消息&#xff0c;英伟达公布了 2019 财年第四季度及全年财报。报告显示&#xff0c;英伟达第四季度营收为 22.05 亿美元&#xff0c;与上年同期的 29.11 亿美元…