是时候让大家看看你用django写出来的博客了内含部署教程视频

作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库博客的基础功能已经开发的差不多了,虽然还有很多地方可以完善,但我们还是希望早点把博客部署到服务器上,让他人可以通过外网访问。至于有待完善的地方,可以等部署完后一点点地迭代和改进。现在就让我们来把博客部署到服务器上吧! ↓↓↓ 视频在这里 ↓↓↓ 作者亲自录制的真机环境演示部署全过程,再不成功你打我!B 站演示(阿里云 CentOS 7 系统)观看地址:https://www.bilibili.com/vide... 注意:本文的每一个步骤都在真实环境下验证无误。除非你知道自己在做什么,否则建议每一步均严格按照教程的指导来,这样能保证你顺利完成部署。 部署前准备我们将使用比较流行的 Nginx + Gunicorn 的方式将 django 开发的博客部署到自己的服务器,让别人能够通过域名访问你的博客。至于 Nginx、Gunicorn 是什么暂时放到一边,读完本教程后你就会知道它们的作用和使用方法了。 为了部署我们的博客,需要满足以下两个条件: 最好有一台可以通过外网访问的服务器(如果没有的话可以在自己的电脑上建一台虚拟机,通过虚拟 ip 访问)。最好有一个域名(如果没有的话,则只能使用 ip 访问,且不能开启 HTTPS)。配置服务器本教程使用的本地环境为 Windows 10,服务器环境为 CentOS 7(64 位)。如果你的环境和我的有所差异(比如 Ubuntu)导致一些命令无法执行,将这些命令转换为你所在环境的命令执行即可,重点在于理解每一步的作用,这样无论在何种环境你都能成功地完成部署,而不是机械地复制粘贴命令。 ### 远程登录到服务器 服务器通常位于云端,需要使用远程登录工具登录后才能对服务器进行操作。我使用的是 Xshell,Windows 下百度 Xshell 下载安装即可,软件对学校和个人用户是免费的。 如何远程登录到服务器这里就不赘述了,相信你参考网上的一些教程肯定能够顺利登录。假如你和我一样使用 Xshell 的话,这里有一篇很详细的教程可以参考:教你怎么使用 xshell 远程连接 linux 服务器。 创建一个超级用户顺利连接到远程服务器了,如果是一台全新服务器的话,通常我们是以 root 用户登录的。在 root 下部署代码不够安全,最好是建一个新用户(如果你已经以非 root 用户登录的话可以跳过这一步)。下面的一些列命令将创建一个拥有超级权限的新用户(把 yangxg 替换成你自己想要的用户名,我这里取我的名字拼音 yangxg): # 在 root 用户下运行这条命令创建一个新用户,yangxg 是用户名# 因为我叫杨学光,所以我取的用户名是 yangxg# 选择一个你喜欢的用户名,不一定非得和我的相同root@server:~# adduser yangxg# 为新用户设置密码# 注意在输密码的时候不会有字符显示,不要以为键盘坏了,正常输入即可root@server:~# passwd yangxg# 把新创建的用户加入超级权限组root@server:~# usermod -aG wheel yangxg# 切换到创建的新用户root@server:~# su - yangxg# 切换成功,@符号前面已经是新用户名而不是 root 了yangxg@server:$新用户创建并切换成功了。如果是新服务器的话,最好先更新一下系统,避免因为版本太旧而给后面安装软件带来麻烦。运行下面的两条命令: ...

September 20, 2019 · 5 min · jiezi

HelloDjango-第-14-篇交流的桥梁评论功能

作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 截止到目前为止我们的 django blog 文章展示部分,已经实现的“八九不离十”了。你以为本系列文章就要结束了吗?不能够!新的征程才刚刚开始,HelloDjango 系列文章刚刚过半,后面的文章你将接触更多博客系统的细节。向着一个小而全的博客系统前进、前进、前进,你定会收获颇多。 今天我们就来开启博客的评论功能,建起和读者的沟通桥梁。 创建评论应用相对来说,评论是另外一个比较独立的功能。Django 提倡,如果功能相对比较独立的话,最好是创建一个应用,把相应的功能代码组织到这个应用里。我们的第一个应用叫 blog,它里面放了展示博客文章列表和详情等相关功能的代码。而这里我们再创建一个应用,名为 comments 这里面将存放和评论功能相关的代码。首先进入到项目根目录,然后输入如下命令创建一个新的应用: > pipenv run python manage.py startapp comments可以看到生成的 comments 应用目录结构和 blog 应用的目录是类似的(关于创建应用以及应用的目录结构在 "空空如也"的博客应用 中已经有过详细介绍)。 创建新的应用后一定要记得在 settings.py 里注册这个应用,django 才知道这是一个应用。 blogproject/settings.py...INSTALLED_APPS = [ ... 'blog.apps.BlogConfig', # 注册 blog 应用 'comments.apps.CommentsConfig', # 注册 comments 应用]v...注意这里注册的是 CommentsConfig 类,在 博客从“裸奔”到“有皮肤” 中曾经讲过如何对应用做一些初始化配置,例如让 blog 应用在 django 的 admin 后台显示中文名字。这里也对评论应用做类似的配置: comments/app.pyfrom django.apps import AppConfigclass CommentsConfig(AppConfig): name = 'comments' verbose_name = '评论'设计评论的数据库模型用户评论的数据必须被存储到数据库里,以便其他用户访问时 django 能从数据库取回这些数据然后展示给访问的用户,因此我们需要为评论设计数据库模型,这和设计文章、分类、标签的数据库模型是一样的,如果你忘了怎么做,再回顾一下 创建 Django 博客的数据库模型 中的做法。我们的评论模型设计如下(评论模型的代码写在 commentsmodels.py 里): ...

September 10, 2019 · 5 min · jiezi

Django搭建个人博客基于-LocalStorage-的点赞功能

假设你的博客已经顺利部署到了线上。你写了很多好文章,和粉丝们互动并感受成就感。 现在你想更进一步,努力提高文章质量,使其更受读者欢迎,打造圈内一流博客。问题是该如何判断一篇文章是“受欢迎的”?靠浏览量是个方法,但是并不能区分出内容花拳绣腿的标题党。靠评论数也是个好方法,但个人博客通常读者不多,好文章零评论是很正常的。 这时候“点赞”功能就显得重要了。如果大部分读者都给了一个赞,那就表明文章确实还不错。 动手之前的思考点赞功能可不简单,实现途径非常的多。别急着动手,耐心思考:我们的博客到底需要什么样的点赞? 首先,点赞是否要求用户必须登录?要求登录的好处是可以精确的记录是哪些用户、对哪些文章点过赞(多对多关系),以便进行细致的数据分析。坏处是登录这个要求很笨重,会屏蔽掉大部分的游客用户。博主倾向于不要求用户登录,毕竟小站通常用户就不多,提高参与度才是点赞最核心的任务。 如果某天你的小站火了,就把要求用户登录的交互功能让给“收藏”吧!其次,用户是否可以重复点赞?很多视频平台的用户可以对某个喜欢的女主播疯狂点赞,以表达自己非常非常的喜欢。这对用户较多的平台是没问题的,因为用户数量多了之后,你点几百个赞也只是九牛一毛。但博客网站这样做很容易造成某些文章点赞为零,某些文章点赞数又出奇的高。显然这不代表文章质量的差异。 好了,目前我们的策略是不要求用户登录,也不允许用户重复点赞。下一个问题是,在哪里记录用户的点赞相关的数据呢?点赞数量毫无疑问要保存在数据库里,以便随时取出数据并呈现出来。 但问题是校验用户是否已点赞的记录保存在哪?在数据库中记录用户的IP地址是个方法,但你得处理好记录IP和记录登录用户的关系,稍微有点麻烦。另外每次用户的点赞都需要向后端发送校验请求,增加了服务器的负担。 既然数据保存在后端数据库里不好,那能不能保存在浏览器端呢?答案是可以的,并且有 Cookie 和 LocalStorage 都可以让你保存数据。它两的主要区别如下: 特性CookieLocalStorage生命周期可设置失效时间,默认是关闭浏览器后失效除非被清除,否则永久保存存储空间4K左右一般为5MB与服务器通信每次都会携带在HTTP头中不参与服务器的通信易用性源生接口不友好源生接口可以接受比较下来会发现 LocalStorage 可以永久保存数据,存储空间大,也不参与服务器通信,很适合点赞的需求。由于数据保存在浏览器中,所以也不需要区分用户有没有登录了:实际上每次请求点赞时,校验的是当前这个浏览器是否已经点过赞了,而不是用户! 可能你会反驳说,那要是用户换一个浏览器不就可以重复点赞了吗,更何况浏览器端的数据是非常容易篡改的。但这又有什么关系呢?点赞数据并不需要非常精确,随他去吧。 所有的现代浏览器都支持 LocalStorage 功能。如果你还在用 IE6 ,赶紧考虑升级浏览器吧。总结一下,我们的点赞功能如下: 不要求用户登录不允许重复点赞点赞数保存在服务器数据库中点赞校验数据保存在浏览器的 LocalStorage 中当用户点赞时,前端脚本会在 LocalStorage 里校验是否已经点过赞了;如未点过赞,才会向服务器发送点赞请求,并记录数据。 想清楚需求,难题就迎刃而解了。接下来就是代码实现。 需要说明的是,以上分析并不代表其他方法不好,仅仅是在博客小站的环境下,博主觉得合适的技术路径而已。如果你心中住着另一个哈姆雷特,请想办法去实现它。代码实现准备工作本章的重点工作在前端,因此先把简单的后端代码写了,权当热身。 有的读者听到前端就觉得头疼。你的痛苦我明白,但也是必不可少的。光写 Python 是做不出漂亮网站的。由于点赞数需要保存在数据库中,因此修改文章模型是必须的了: article/models.py...# 文章模型class ArticlePost(models.Model): ... # 新增点赞数统计 likes = models.PositiveIntegerField(default=0) ...迁移数据: (env) > python manage.py makemigrations(env) > python manage.py migrate继续用类视图: article/views.py...# 点赞数 +1class IncreaseLikesView(View): def post(self, request, *args, **kwargs): article = ArticlePost.objects.get(id=kwargs.get('id')) article.likes += 1 article.save() return HttpResponse('success')功能是让点赞数增加1个,并且返回 success 。至于为什么是 success 后面再讲。 ...

September 7, 2019 · 3 min · jiezi

HelloDjango-第-09-篇让博客支持-Markdown-语法和代码高亮

作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库为了让博客文章具有良好的排版,显示更加丰富的格式,我们使用 Markdown 语法来书写博文。Markdown 是一种 HTML 文本标记语言,只要遵循它约定的语法格式,Markdown 的解析工具就能够把 Markdown 文档转换为标准的 HTML 文档,从而使文章呈现更加丰富的格式,例如标题、列表、代码块等等 HTML 元素。由于 Markdown 语法简单直观,不用超过 5 分钟就可以轻松掌握常用的标记语法,因此大家青睐使用 Markdown 书写 HTML 文档。下面让我们的博客也支持使用 Markdown 写作。 安装 Python Markdown将 Markdown 格式的文本解析成标准的 HTML 文档是一个复杂的工程,好在已有好心人帮我们完成了这些工作,直接拿来使用即可。首先安装 Markdown,这是一个 Python 第三方库,在项目根目录下运行命令 pipenv install markdown。 在 detail 视图中解析 Markdown将 Markdown 格式的文本解析成 HTML 文本非常简单,只需调用这个库的 markdown 方法。我们书写的博客文章内容存在 Post 的 body 属性里,回到我们的详情页视图函数,对 post 的 body 的值做一下解析,把 Markdown 文本转为 HTML 文本再传递给模板: blog/views.pyimport markdownfrom django.shortcuts import get_object_or_404, renderfrom .models import Postdef detail(request, pk): post = get_object_or_404(Post, pk=pk) post.body = markdown.markdown(post.body, extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', 'markdown.extensions.toc', ]) return render(request, 'blog/detail.html', context={'post': post})这样我们在模板中显示 {{ post.body }} 的时候,就不再是原始的 Markdown 文本了,而是解析过后的 HTML 文本。注意这里我们给 markdown 解析函数传递了额外的参数 extensions,它是对 Markdown 语法的拓展,这里使用了三个拓展,分别是 extra、codehilite、toc。extra 本身包含很多基础拓展,而 codehilite 是语法高亮拓展,这为后面的实现代码高亮功能提供基础,而 toc 则允许自动生成目录(在以后会介绍)。 ...

August 21, 2019 · 2 min · jiezi

Django搭建个人博客自定义模板过滤器和标签

现在我们已经很熟悉Django的MTV模式了。模板(template)负责如何去展示数据,而视图(view)负责筛选出正确的数据。因此通常来说逻辑都是放到视图中的,但模板也需要一些和表示相关的逻辑:比如循环展示(如{% for ... %})、或者以某种特定格式输出(如{{ ...|date:'Y-m-d' }})等,这些功能都是靠模板的过滤器(filters)和标签(tags)实现的。 Django的模板语言包含了很多内置的过滤器和标签,设计目的是满足应用需要占位逻辑需求。但有的时候这些通用的功能满足不了你的某些需求,这时候就需要自定义过滤器和标签来实现了。 前置条件要在Django中使用模板过滤器或标签,就首先得注册它们。 注册方法如下: 在APP中新建名为templatetags的目录(方便起见,教程选择了article这个APP)在此目录中新建名为__init__.py的空文件,使得此目录被视作一个Python的包在此目录中新建python文件(比如my_filters_and_tags.py),就可以在里面愉快的写代码啦完成后的目录结构如下: article/ __init__.py views.py models.py # 新增目录 templatetags/ __init__.py # 空文件 my_filters_and_tags.py # 即将写代码的地方 ...请注意: 目录必须位于已注册的APP中,这是出于安全性的考虑新建目录后,必须手动重启服务器,里面的过滤器和标签才能生效前置条件就完成了,接下来我们看看如何写一个模板过滤器。 模板过滤器过滤器filter的表现形式为紧跟在上下文后面的管道符|,管道符后面是filter的名称:{{ ...|filter_name }}。有的filter还可以带有参数:{{ ...|filter_name:var }}。 注意过滤器名称的冒号后面不能有空格。filter这个名字可能会让你误认为它只是用来筛选某些特定数据的,但实际上它远不止这点功能。它可以改变上下文的最终展示效果,也可以将上下文通过运算输出为特定的值。 小试牛刀要成为一个可用的filter,文件中必须包含一个名为 register 的模块级变量,它是一个 template.Library 实例,所有的filters均在其中注册。所以在my_filter_and_tags.py文件中输入以下内容: article/templatetags/my_filter_and_tags.pyfrom django import templateregister = template.Library()接下来就可以像写普通的Python函数一样写过滤器了: article/templatetags/my_filter_and_tags.pyfrom django import templateregister = template.Library()@register.filter(name='transfer')def transfer(value, arg): """将输出强制转换为字符串 arg """ return arg@register.filter()def lower(value): """将字符串转换为小写字符""" return value.lower()filter可以通过装饰器进行注册。若注册装饰器中携带了name参数,则其值为此filter的名称;若未携带,则函数名就是filter的名称。filter必须是有一到两个参数的Python函数。第一个参数是上下文本身,第二个参数则由filter提供。举个栗子,在过滤器 {{ var|foo:"bar" }} 中,变量 var 为第一个参数,变量 bar 则作为第二个参数。调用这些filter的方法是在模板文件中用{% load ... %}将filter文件的名称加载进去,像这样: ...

August 18, 2019 · 2 min · jiezi

pymysql-开启调试模式

今天在排查线上一个奇怪的数据库连接问题,所以打开了 pymysql 的源码在阅读,发现 pymysql 在其 connections 模块里内置了一个 DEBUG 变量用于控制是否开启调试模式,是的话,会将当前连接的操作以及报文内容都打印到控制台。 使用方法在你的服务器初始化代码里,加上对 DEBUG 的设置,比如: import pymysqlpymysql.install_as_MySQLdb()pymysql.connections.DEBUG = True # 这是我新加的一行重启服务器后,访问相关接口,会看到标准输出里有类似下面的一些输出:

August 18, 2019 · 1 min · jiezi

Django22中间件详解

中间件是 Django 用来处理请求和响应的钩子框架。它是一个轻量级的、底层级的“插件”系统,用于全局性地控制Django 的输入或输出,可以理解为内置的app或者小框架。 在django.core.handlers.base模块中定义了如何接入中间件,这也是学习Django源码的入口之一。 每个中间件组件负责实现一些特定的功能。例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话机制将用户与请求request关联起来。 中间件可以放在你的工程的任何地方,并以Python路径的方式进行访问。 Django 具有一些内置的中间件,并自动开启了其中的一部分,我们可以根据自己的需要进行调整。 一、如何启用中间件若要启用中间件组件,请将其添加到 Django 配置文件settings.py的 MIDDLEWARE 配置项列表中。 在 MIDDLEWARE 中,中间件由字符串表示。这个字符串以圆点分隔,指向中间件工厂的类或函数名的完整 Python 路径。下面是使用 django-admin startproject命令创建工程后,默认的中间件配置: MIDDLEWARE = [  'django.middleware.security.SecurityMiddleware',  'django.contrib.sessions.middleware.SessionMiddleware',  'django.middleware.common.CommonMiddleware',  'django.middleware.csrf.CsrfViewMiddleware',  'django.contrib.auth.middleware.AuthenticationMiddleware',  'django.contrib.messages.middleware.MessageMiddleware',  'django.middleware.clickjacking.XFrameOptionsMiddleware', ]实际上在Django中可以不使用任何中间件,如果你愿意的话,MIDDLEWARE 配置项可以为空。但是强烈建议至少使用 CommonMiddleware。而笔者的建议是保持默认的配置,这有助于你提高网站的安全性。 二、 中间件最关键的顺序问题MIDDLEWARE 的顺序很重要,具有先后关系,因为有些中间件会依赖其他中间件。例如: AuthenticationMiddleware 需要在会话中间件中存储的经过身份验证的用户信息,因此它必须在 SessionMiddleware 后面运行 。 在请求阶段,调用视图之前,Django 按照定义的顺序执行中间件 MIDDLEWARE,自顶向下。 你可以把它想象成一个洋葱:每个中间件类都是一个“皮层”,它包裹起了洋葱的核心--实际业务视图。如果请求通过了洋葱的所有中间件层,一直到内核的视图,那么响应将在返回的过程中以相反的顺序再通过每个中间件层,最终返回给用户。 如果某个层的执行过程认为当前的请求应该被拒绝,或者发生了某些错误,导致短路,直接返回了一个响应,那么剩下的中间件以及核心的视图函数都不会被执行。 三、Django内置的中间件Django内置了下面这些中间件,满足了我们一般的需求: Cache缓存中间件 如果启用了该中间件,Django会以CACHE_MIDDLEWARE_SECONDS 配置的参数进行全站级别的缓存。 Common通用中间件 该中间件为我们提供了一些便利的功能: 禁止DISALLOWED_USER_AGENTS中的用户代理访问服务器自动为URL添加斜杠后缀和www前缀功能。如果配置项 APPEND_SLASH 为True ,并且访问的URL 没有斜杠后缀,在URLconf中没有匹配成功,将自动添加斜杠,然后再次匹配,如果匹配成功,就跳转到对应的url。 PREPEND_WWW 的功能类似。为非流式响应设置Content-Length头部信息。作为展示的例子,这里额外贴出它的源代码,位于django.middleware.common模块中,比较简单,很容易读懂和理解: class CommonMiddleware(MiddlewareMixin): """ 去掉了doc """ response_redirect_class = HttpResponsePermanentRedirect def process_request(self, request): # Check for denied User-Agents if 'HTTP_USER_AGENT' in request.META: for user_agent_regex in settings.DISALLOWED_USER_AGENTS: if user_agent_regex.search(request.META['HTTP_USER_AGENT']): raise PermissionDenied('Forbidden user agent') # Check for a redirect based on settings.PREPEND_WWW host = request.get_host() must_prepend = settings.PREPEND_WWW and host and not host.startswith('www.') redirect_url = ('%s://www.%s' % (request.scheme, host)) if must_prepend else '' # Check if a slash should be appended if self.should_redirect_with_slash(request): path = self.get_full_path_with_slash(request) else: path = request.get_full_path() # Return a redirect if necessary if redirect_url or path != request.get_full_path(): redirect_url += path return self.response_redirect_class(redirect_url) def should_redirect_with_slash(self, request): if settings.APPEND_SLASH and not request.path_info.endswith('/'): urlconf = getattr(request, 'urlconf', None) return ( not is_valid_path(request.path_info, urlconf) and is_valid_path('%s/' % request.path_info, urlconf) ) return False def get_full_path_with_slash(self, request): new_path = request.get_full_path(force_append_slash=True) if settings.DEBUG and request.method in ('POST', 'PUT', 'PATCH'): raise RuntimeError( "You called this URL via %(method)s, but the URL doesn't end " "in a slash and you have APPEND_SLASH set. Django can't " "redirect to the slash URL while maintaining %(method)s data. " "Change your form to point to %(url)s (note the trailing " "slash), or set APPEND_SLASH=False in your Django settings." % { 'method': request.method, 'url': request.get_host() + new_path, } ) return new_path def process_response(self, request, response): # If the given URL is "Not Found", then check if we should redirect to # a path with a slash appended. if response.status_code == 404: if self.should_redirect_with_slash(request): return self.response_redirect_class(self.get_full_path_with_slash(request)) if settings.USE_ETAGS and self.needs_etag(response): warnings.warn( "The USE_ETAGS setting is deprecated in favor of " "ConditionalGetMiddleware which sets the ETag regardless of " "the setting. CommonMiddleware won't do ETag processing in " "Django 2.1.", RemovedInDjango21Warning ) if not response.has_header('ETag'): set_response_etag(response) if response.has_header('ETag'): return get_conditional_response( request, etag=response['ETag'], response=response, ) # Add the Content-Length header to non-streaming responses if not # already set. if not response.streaming and not response.has_header('Content-Length'): response['Content-Length'] = str(len(response.content)) return response def needs_etag(self, response): """Return True if an ETag header should be added to response.""" cache_control_headers = cc_delim_re.split(response.get('Cache-Control', '')) return all(header.lower() != 'no-store' for header in cache_control_headers)GZip内容压缩中间件 ...

August 7, 2019 · 4 min · jiezi

HelloDjango-第-06-篇博客从裸奔到有皮肤

文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库在此之前我们已经编写了博客的首页视图,并且配置了 URL 和模板,让 django 能够正确地处理 HTTP 请求并返回合适的 HTTP 响应。不过我们仅仅在首页返回了一句话:“欢迎访问我的博客“,这是个 Hello World 级别的视图函数,毫无美感。 这篇文章我们需要编写真正的首页视图函数,当用户访问我们的博客首页时,他将看到我们发表的博客文章列表,就像 演示项目 里展示的这样。 首页视图函数上一节我们阐明了 django 的开发流程。即首先配置 URL,把 URL 和相应的视图函数绑定,一般写在 urls.py 文件里,然后在工程的 urls.py 文件引入。其次是编写视图函数,视图中需要渲染模板,我们也在 settings.py 中进行了模板相关的配置,让 django 能够找到需要渲染的模板。最后把渲染完成的 HTTP 响应返回就可以了。相关的配置和准备工作都在之前完成了,这里我们只需专心编写视图函数,让它实现我们想要的功能即可。 首页的视图函数其实很简单,代码像这样: blog/views.pyfrom django.shortcuts import renderfrom .models import Postdef index(request): post_list = Post.objects.all().order_by('-created_time') return render(request, 'blog/index.html', context={'post_list': post_list})我们曾经在前面的章节讲解过模型管理器 objects 的使用。这里我们使用 all() 方法从数据库里获取了全部的文章,存在了 post_list 变量里。all 方法返回的是一个 QuerySet(可以理解成一个类似于列表的数据结构),由于通常来说博客文章列表是按文章发表时间倒序排列的,即最新的文章排在最前面,所以我们紧接着调用了 order_by 方法对这个返回的 queryset 进行排序。排序依据的字段是 created_time,即文章的创建时间。- 号表示逆序,如果不加 - 则是正序。 接着如之前所做,我们渲染了 blogindex.html 模板文件,并且把包含文章列表数据的 post_list 变量传给了模板。 ...

August 7, 2019 · 4 min · jiezi

Django的models中ondelete参数

在Django2.0以上的版本中,创建外键和一对一关系必须定义on_delete参数,我们可以在其源码中看到相关信息 class ForeignKey(ForeignObject): """ Provide a many-to-one relation by adding a column to the local model to hold the remote value. By default ForeignKey will target the pk of the remote model but this behavior can be changed by using the ``to_field`` argument. """ # Field flags many_to_many = False many_to_one = True one_to_many = False one_to_one = False rel_class = ManyToOneRel empty_strings_allowed = False default_error_messages = { 'invalid': _('%(model)s instance with %(field)s %(value)r does not exist.') } description = _("Foreign Key (type determined by related field)") def __init__(self, to, on_delete, related_name=None, related_query_name=None, limit_choices_to=None, parent_link=False, to_field=None, db_constraint=True, **kwargs):to:关联的表on_delete:当该表中的某条数据删除后,关联外键的操作related_name:反查参数,设置后可以在被关联表中通过该字段反查外键所在表,默认:set_表名to_field:默认主键,因为mysql只支持主键作为外键,就算你没显式的创建主键,Django会给你自动创建,如果你是DB-first,且没创建主键:数据库默认使用隐藏字段:DB_ROW_ID作为主键on_delete参数设置CASCADE:级联删除,当关联表中的数据删除时,该外键也删除PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出ProtectedError错误。SET_NULL: 置空模式,删除的时候,外键字段被设置为空,前提就是blank=True, null=True,定义该字段的时候,允许为空。SET_DEFAULT: 设置默认值,删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。SET(): 自定义一个值,该值当然只能是对应的实体 ...

July 15, 2019 · 1 min · jiezi

Django的models模型

model的常用字段V=models.CharField(max_length=None[, **options]) #varcharV=models.EmailField([max_length=75, **options]) #varcharV=models.URLField([verify_exists=True, max_length=200, **options]) #varcharV=models.FileField(upload_to=None[, max_length=100, **options]) #varchar#upload_to指定保存目录可带格式,V=models.ImageField(upload_to=None[, height_field=None, width_field=None, max_length=100, **options])V=models.IPAddressField([**options]) #varcharV=models.FilePathField(path=None[, match=None, recursive=False, max_length=100, **options]) #varcharV=models.SlugField([max_length=50, **options]) #varchar,标签,内含索引V=models.CommaSeparatedIntegerField(max_length=None[, **options]) #varcharV=models.IntegerField([**options]) #intV=models.PositiveIntegerField([**options]) #int 正整数V=models.SmallIntegerField([**options]) #smallintV=models.PositiveSmallIntegerField([**options]) #smallint 正整数V=models.AutoField(**options) #int;在Django代码内是自增V=models.DecimalField(max_digits=None, decimal_places=None[, **options]) #decimalV=models.FloatField([**options]) #realV=models.BooleanField(**options) #boolean或bitV=models.NullBooleanField([**options]) #bit字段上可以设置上null值V=models.DateField([auto_now=False, auto_now_add=False, **options]) #date#auto_now最后修改记录的日期;auto_now_add添加记录的日期V=models.DateTimeField([auto_now=False, auto_now_add=False, **options]) #datetimeV=models.TimeField([auto_now=False, auto_now_add=False, **options]) #timeV=models.TextField([**options]) #textV=models.XMLField(schema_path=None[, **options]) #text——————————————————————————–V=models.ForeignKey(othermodel[, **options]) #外键,关联其它模型,创建关联索引V=models.ManyToManyField(othermodel[, **options]) #多对多,关联其它模型,创建关联表V=models.OneToOneField(othermodel[, parent_link=False, **options]) #一对一,字段关联表属性经典情景示例书籍,作者,出版社之间的关系,这里为了便于演示,我们尽量精简了表中的字段,书籍表具有书名,出版社同出版社表建立一对多的关系[foreign key],一本书可以具有多个作者,又同作者表建立多对多的关系[many-to-many],作者表有名称,年龄,出版社表有出版社名称。 from django.db import modelsclass Publisher(models.Model): name = models.CharField(max_length=30) def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=30) age = models.IntegerField() def __str__(self): return self.name class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher,on_delete=models.CASCADE) def __str__(self): return self.title选择对象获取全体对象Publisher.objects.all() #获取所有对象筛选对象Publisher.objects.filter(name='人们教育出版社') #获取的是一个对象列表dict = {'name':'lemon','age':18}Author.objects.filter(**dict) #列表传参的方法获取单个对象Publisher.objects.get(name='机械工业出版社') #找不到会报错!!!对象排序Author.objects.order_by("name","-age") #可以按照多个字段排序,- 表示逆向排序连查Author.objects.filter(name='lemon').order_by('-age')[0] 批量更新Author.objects.all().update(age='18')删除对象Author.objects.filter(name='lemon').delete()外键和多对多操作访问外键Book.objects.get(id=1).publisher #得到书籍的出版社反向查询models.Publisher.objects.get(id=1).book_set.all() #反向查询,得到的是一个queryset对象列表多对多操作Book.objects.get(id=1).authors.all() #得到queryset对象列表自定义models方法class Author(models.Model): name = models.CharField(max_length=30) age = models.IntegerField() def __str__(self): return self.name def status(self): if self.name=='lemon': return '帅哥'运行结果: ...

July 14, 2019 · 2 min · jiezi

Django-ORM-常用方法

1、all(): 查询所有结果 2、filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 3、get(**kwargs):返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 4、exclude(**kwargs):它包含了与所给筛选条件不匹配的对象 5、values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列 6、values_list(*field): 它与values()非常相似,它返回的是一个元组序列 7、order_by(*field): 对查询结果排序 8、reverse(): 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。 9、count(): 返回数据库中匹配查询(QuerySet)的对象数量。 10、first(): 返回第一条记录 11、last(): 返回最后一条记录 12、exists(): 如果QuerySet包含数据,就返回True,否则返回False 总结: 1、返回具体对象方法get()、first()、last()2、返回QuerySet 对象方法 all()、filter()、exclude()、order_by()、reverse() values() 返回一个可迭代的字典序列 values_list() 返回一个可迭代的元组序列3、返回Boolean值方法 exists()4、返回数字的方法count()

July 13, 2019 · 1 min · jiezi

对比-C-和-Python谈谈指针与引用

花下猫语:本文是学习群内 樱雨楼 小姐姐的投稿。之前已发布过她的一篇作品《当谈论迭代器时,我谈些什么?》,大受好评。本文依然是对比 C++ 与 Python,来探讨编程语言中极其重要的概念。祝大家读有所获,学有所成! 樱雨楼 | 原创作者 豌豆花下猫 | 编辑润色 本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/k0... 0 引言指针(Pointer)是 C、C++ 以及 Java、Go 等语言的一个非常核心且重要的概念,而引用(Reference)是在指针的基础上构建出的一个同样重要的概念。 指针对于任何一个编程语言而言都是必须且重要的,虽然 Python 对指针这一概念进行了刻意的模糊与限制,但指针对于 Python 而言依然是一个必须进行深入讨论的话题。 本文基于 C++ 与 Python,讨论了 Python 中与指针及引用相关的一些行为。 1 什么是指针?为什么需要指针?指针有两重含义: (1)指代某种数据类型的指针类型,如整形指针类型、指针指针类型 (2)指代一类存放有内存地址的变量,即指针变量 指针的这两重含义是紧密联系的:作为一种变量,通过指针可以获取某个内存地址,从而为访问此地址上的值做好了准备;作为一种类型,其决定了内存地址的正确偏移长度,其应等于当前类型的单位内存大小。 如果一个指针缺少指针类型,即 void *,则显然,其虽然保存了内存地址,但这仅仅是一个起点地址,指针会因为无法获知从起点向后进行的偏移量,从而拒绝解指针操作;而如果一个指针缺少地址,即 nullptr,则其根本无法读取特定位置的内存。 指针存在的意义主要有以下几点: 承载通过 malloc、new、allocator 等获取的动态内存使得 pass-by-pointer 成为可能pass-by-pointer 的好处包括但不限于: 避免对实参无意义的值拷贝,大幅提高效率使得对某个变量的修改能力不局限于变量自身的作用域使得 swap、移动构造函数、移动赋值运算等操作可以仅针对数据结构内部的指针进行操作,从而避免了对临时对象、移后源等对象的整体内存操作由此可见,与指针相关的各操作对于编程而言都是必须的或十分重要的。 2 C++中的引用在 C++ 中,引用具有与指针相似的性质,但更加隐形与严格。C++ 的引用分为以下两种: 2.1 左值引用左值引用于其初始化阶段绑定到左值,且不存在重新绑定。 左值引用具有与被绑定左值几乎一样的性质,其唯一的区别在于 decltype 声明: int numA = 0, &lrefA = numA; // Binding an lvaluecout << ++lrefA << endl; // Use the lvalue reference as lvalue & rvaluedecltype(lrefA) numB = 1; // Error!左值引用常用于 pass-by-reference: ...

July 12, 2019 · 2 min · jiezi

什么是JSONP

简述:jsonp可以解决$.ajax无法跨域请求的问题,其基本原理是利用web调用js文件不受跨域影响,在html标签中类似<script>、<img>、<iframe>具有src属性的都不会受跨域的影响,jsonp就是利用这个特性,实现对跨域数据请求,需要注意的是,jsonp只能是GET请求。 简单实现前端代码 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><script src="http://10.0.0.5/test.js"></script></body></html>其中test.js是在虚拟机nginx服务器放置的一个js文件,内容为: alert('this is a test')访问页面成功弹窗显示: 自定义函数调用这次我们将远端的js文件写上前端的函数名,达到实现数据交互的功能 前端代码 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><script> function Test(data) { alert(data) }</script><script src="http://10.0.0.5/test.js"></script></body></html>目标js文件 Test('来自远方的问候')访问页面成功弹窗显示: 动态调用服务端要是可以动态生成js脚本,这样服务端就可以对客户端发送的请求做验证,从而返回相应的内容。 客户端 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><input type="text"><button onclick="Test()">test</button><script src="jquery.js"></script><script> function Test() { $.ajax({ url:'http://10.0.0.1:8000/index2', data:{'data':$('input').val()}, dataType:'jsonp', Callbacks:'callbacks', jsonpCallback:'fun', }) } function fun(data) { alert(data) }</script></body></html>服务端(django) def index2(req): messget = req.GET.get('data') data = req.GET.get('callback')+'("啦啦啦,服务器收到你的数据啦,你发送的是%s")' %messget print(data) return HttpResponse(data)效果如下,当按下test按钮 备注对了,还有一种请求也可以跨域,就是服务端返回的数据中HTTP响应报文包含了正确CORS响应头

July 9, 2019 · 1 min · jiezi

Django使用MySql数据库

编辑settings.py文件编辑settings.py文件中的DATABASES字段 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', #选择mysql引擎 'NAME': 'test', #数据库名 'USER':'root', #用户 'PASSWORD':'123456', #密码 'HOST':'', #连接IP地址,默认本地 'PORT':'', #端口,默认3306 }}编辑__init__.py文件在配置文件中添加 import pymysql #导入pymysql模块 pymysql.install_as_MySQLdb() #兼容mysqldb

July 7, 2019 · 1 min · jiezi

Django后台admin的使用

简述:Django的admin可以提供一个强大的后台管理功能,可以在web界面对数据库进行操作,我们需要修改admin.py将要操作的数据表注册到后台管理中 创建数据表:为了便于演示,我们在models.py中创建一张img数据表规则 图中 verbo_name 是在admin界面显示表字段的名称,定义的class Meta中的verbo_name是在admin界面显示的表名 修改admin.py文件from django.contrib import adminfrom app1 import models# Register your models here.admin.site.register(models.img) #将表在admin中注册启动服务,进入界面python manage.py makemigrations #生成创建数据表py文件python manage.py migrate #执行py文件,更新数据库python manage.py runserver #启动服务打开浏览器,访问http://127.0.0.1:8000/admin 输入设置的账号密码,进入界面 默认的功能可以对表进行简单的增删改查,如果需要批量更新之类的操作,则需要定制相应动作 自定义内容显示:我们选择进入我们创建的表的界面的时候,默认显示的内容是object 可以在models.py中的相应表写下定义__str__的字段 效果如下 默认显示的内容只有一个,我们可以通过定义ModelAdmin中的list_display来修改它: from django.contrib import adminfrom app1.models import img# Register your models here.class imgAdmin(admin.ModelAdmin): list_display = ('title','summary','file') admin.site.register(img,imgAdmin) 点击一条数据后,将默认显示每个字段不是一个AutoField并且editable=True在单个字段集中具有与模型中定义的字段相同的顺序。 我们可以定义ModelAdmin中的fields或exclude来修改它: class imgAdmin(admin.ModelAdmin): list_display = ('title','summary','file') fields = ('title',) exclude跟fields相反 自定义动作默认动作只有删除一项 我们可以通过ModelAdmin自定义 ...

July 7, 2019 · 1 min · jiezi

Django的基本命令

1. 新建一个Django项目:django-admin startproject <project_name>2. 新建APP:python manage.py startpp <app_name>3. 创建数据库表或更改数据库表字段:python manage.py makemigrations #生成py文件python manage.py migrate #执行py文件,更新数据库4. 启动web服务:python manage.py runserver '<ip:port>' #可指定监听ip端口,默认本地8000监听5. 创建超级管理员:python manage.py cratesuperuser----------pyth manage.py changepassword <username> #更改用户密码6. 清空数据库:python manage.py flush #清空数据库,留下空表7. 数据库命令行:python manage.py dbshellpython manage.py help #显示帮助

July 7, 2019 · 1 min · jiezi

Django搭建个人博客自动化测试

测试是伴随着开发进行的,开发有多久,测试就要多久。本教程已经进行了30多章了,都是如何测试的?当然是runserver啦!每当开发新功能后,都需要运行服务器,假装自己就是用户,测试是否运行正常。 这样的人工测试优点是非常直观,你看到的和用户看到的是完全相同的。但是缺点也很明显: 效率低。在开发时可能你需要反复的修改代码、测试功能,这样重复查看几十次甚至几百次网页时会相当的让人烦躁。容易遗漏bug。随着你的项目越来越复杂,组件之间的交互也更加复杂。修改某一个组件可能会导致另一个组件出现意想不到的bug,但是在人工测试时却很难检查出来,总不能每写几行代码就把整个网站统统检查一遍吧。过了很久之后你终于发现了这个bug,但此时你已经搞不清它来源于什么地方了。有的测试不方便进行。比如说有个功能,限制每个用户每天发表评论不能超过10条,人工测试就显得比较麻烦,特别是需要反复调试的时候。为了解决人工测试的种种问题,Django引入了Python标准库的单元测试模块,也就是自动化测试了:你可以写一段代码,让代码帮你测试!(程序员是最会偷懒的职业..)代码会忠实的完成测试任务,帮助你从繁重的测试工作中解脱出来。除此之外,自动化测试还有以下优点: 预防错误。当应用过于复杂时,代码的意图会变得非常不清晰,甚至你都看不懂自己写的代码,这是很常见的。而测试就好像是从内部审查代码一样,可以帮助你发现微小的错误。有利于团队协作。良好的测试保证其他人不会不小心破坏了你的代码(也保证你不会不小心弄坏别人的..)。现在已经不是单打独斗出英雄的年代了,想要成为优秀的Django程序员,你必须擅长编写测试!虽然学习自动化测试不会让你的博客增加一丝丝的功能,但是可以让代码更加强壮,所以我觉得很有必要拿出一章来专门讲讲。 Django官方文档的第5部分讲测试讲得非常的好,并且有中文版本。本章节就大量借鉴了官方文档,也非常非常推荐读者去拜读。第一个测试给我bug!为了演示测试是如何工作的,让我们首先在文章模型中写个有bug的方法: article/models.pyfrom django.utils import timezoneclass ArticlePost(models.Model): ... def was_created_recently(self): # 若文章是"最近"发表的,则返回 True diff = timezone.now() - self.created if diff.days <= 0 and diff.seconds < 60: return True else: return False这个方法用于检测当前文章是否是最近发表的。 这个方法稍微扩展一下就会变得非常实用。比如可以将博文的发表日期显示为“刚刚”、“3分钟前”、“5小时前”等相对时间,用户体验将大有提升。仔细看看,它是没办法正确判断“未来”的文章的: >>> import datetime>>> from django.utils import timezone>>> from article.models import ArticlePost>>> from django.contrib.auth.models import User# 创建一篇"未来"的文章>>> future_article = ArticlePost(author=User(username='user'), title='test',body='test', created=timezone.now() + datetime.timedelta(days=30))# 是否是“最近”发表的?>>> future_article.was_created_recently()True未来发生的肯定不是最近发生的,因此代码是错误的。 写个测试暴露bug接下来就要写测试用例,将测试转为自动化。 还记得最初生成文章app时候的目录结构吗? article │ admin.py │ apps.py │ models.py │ tests.py │ views.py │ __init__.py │ └─migrations └─ __init__.py这个tests.py就是留给你写测试用例的地方了: ...

July 7, 2019 · 3 min · jiezi

django的forms组件的使用

forms组件导入相关模块from django import formsfrom django.forms import fields 常规使用class F1Form(forms.Form): username = fields.CharField(max_length=18,min_length=2,required=True) pwd = fields.CharField(required=True,max_length=16,min_length=6) age = fields.IntegerField(required=True) email = fields.EmailField(required=True)自定义错误信息error_messagesclass F1Form(forms.Form): username = fields.CharField(max_length=18, min_length=6, required=True, error_messages={ 'required':'用户名不能为空', 'min_length':'用户名过短', 'max_length':'用户名过长' } )html的调用views部分: def rege(req): obj = F1Form() return render(req,'rege.html',{'OBJ':obj})html部分: <form action=""> <p>{{ OBJ.username }}</p> <p>{{ OBJ.pwd }}</p> <p>{{ OBJ.age }}</p> <p>{{ OBJ.email }}</p></form>后台数据验证:obj = F1Form(req.POST)if obj.is_valid(): #判断传来的值是否通过验证 models.UserInfo.objects.create(**obj.cleaned_data) #将值写入数据库 ~需要注意的是create(**obj.cleaned_data)方法写入数据库的方式,是在forms提交的name名跟数据库的一致才可以.

July 4, 2019 · 1 min · jiezi

ajax的使用

*通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新,发送请求。 常规使用:$.ajax({ url:"http://www.microsoft.com", //请求的url地址 dataType:"json", //返回格式为json async:true,//请求是否异步,默认为异步,这也是ajax重要特性 data:{"id":"value"}, //参数值 type:"POST", //请求方式 beforeSend:function(){ //请求前的处理 }, success:function(req){ //请求成功时处理 }, complete:function(){ //请求完成的处理 }, error:function(){ //请求出错处理 }});和页面from表单配合: <form> {% csrf_token %} <input type="text" name="username"> <input type="submit" value="提交" onclick="sum()"> </form> <script> function sum() { $.ajax({ url:'http://127.0.0.1:8000/test/', data: $('form').serialize(), type:'post', dataType:'json', success: function (req) { alert(req.name) } }) } </script>

July 4, 2019 · 1 min · jiezi

python-django-logging-小结

python & django logging 小结[TOC] python基本一次配置,多处生效 import logginglogging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')logging.warning('is when this event was logged.')>>>12/12/2010 11:46:36 AM is when this event was logged.高级log_recordfilters会用到,一个和log信息有关的对象 loggers接口,被应用层(我们开发者)调用的 handlers写文件的,挂在logger上 filters过滤文本的, 可以给logger用,也可以给handler用 formatters定义log的格式, 给handler用 formatterfmt message的格式 datefmt时间格式 style 分隔符 %(asctime)s - %(levelname)s - %(message)s&(asctime)s - &(levelname)s - &(message)s例子my_formater = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', style='%') 详细参数源码里面有,也可以看文档 filter返回True或者FalseFalse将会被过滤可以是一个类,也可以是函数.建议用类的形式, 因为带name,将来想remove filter更方便Filter类长这样 class Filter(object): def __init__(self, name=''): self.name = name self.nlen = len(name) def filter(self, record): return True函数 ...

July 3, 2019 · 2 min · jiezi

djangoallauth入门学习和使用

django-allauth是集成的Django应用程序,用于解决网站身份验证,用户的注册登录及账户管理,以及第三方(社交)账户的身份验证。 既然你知道并准备使用django-allauth,所以本文假定你已经掌握了基本的django知识(比如会用django搭建Web App,甚至是一个小型博客网站)。 安装与基本配置安装 pip install django-allauth基本配置1.在你项目的settings.py里加上以下相对应的代码 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.request', ], }, },]AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'allauth.account.auth_backends.AuthenticationBackend',)INSTALLED_APPS = ( # 这两个django本身的app也是需要的,但不需要重复添加 'django.contrib.auth', 'django.contrib.sites', 'allauth', 'allauth.account', 'allauth.socialaccount', # 下面是django-allauth目前支持的社交账号,加上你需要的就行了,不用全加上: 'allauth.socialaccount.providers.amazon', # 亚马逊 'allauth.socialaccount.providers.angellist', 'allauth.socialaccount.providers.asana', 'allauth.socialaccount.providers.auth0', 'allauth.socialaccount.providers.authentiq', 'allauth.socialaccount.providers.baidu', # 百度 'allauth.socialaccount.providers.basecamp', 'allauth.socialaccount.providers.bitbucket', 'allauth.socialaccount.providers.bitbucket_oauth2', 'allauth.socialaccount.providers.bitly', 'allauth.socialaccount.providers.coinbase', 'allauth.socialaccount.providers.dataporten', 'allauth.socialaccount.providers.daum', 'allauth.socialaccount.providers.digitalocean', 'allauth.socialaccount.providers.discord', 'allauth.socialaccount.providers.douban', # 豆瓣 'allauth.socialaccount.providers.draugiem', 'allauth.socialaccount.providers.dropbox', 'allauth.socialaccount.providers.dwolla', 'allauth.socialaccount.providers.edmodo', 'allauth.socialaccount.providers.eveonline', 'allauth.socialaccount.providers.evernote', 'allauth.socialaccount.providers.facebook', 'allauth.socialaccount.providers.feedly', 'allauth.socialaccount.providers.fivehundredpx', 'allauth.socialaccount.providers.flickr', 'allauth.socialaccount.providers.foursquare', 'allauth.socialaccount.providers.fxa', 'allauth.socialaccount.providers.github', # GitHub 'allauth.socialaccount.providers.gitlab', 'allauth.socialaccount.providers.google', 'allauth.socialaccount.providers.hubic', 'allauth.socialaccount.providers.instagram', 'allauth.socialaccount.providers.kakao', 'allauth.socialaccount.providers.line', 'allauth.socialaccount.providers.linkedin', 'allauth.socialaccount.providers.linkedin_oauth2', 'allauth.socialaccount.providers.mailru', 'allauth.socialaccount.providers.mailchimp', 'allauth.socialaccount.providers.meetup', 'allauth.socialaccount.providers.naver', 'allauth.socialaccount.providers.odnoklassniki', 'allauth.socialaccount.providers.openid', 'allauth.socialaccount.providers.orcid', 'allauth.socialaccount.providers.paypal', 'allauth.socialaccount.providers.persona', 'allauth.socialaccount.providers.pinterest', 'allauth.socialaccount.providers.reddit', 'allauth.socialaccount.providers.robinhood', 'allauth.socialaccount.providers.shopify', 'allauth.socialaccount.providers.slack', 'allauth.socialaccount.providers.soundcloud', 'allauth.socialaccount.providers.spotify', 'allauth.socialaccount.providers.stackexchange', 'allauth.socialaccount.providers.stripe', 'allauth.socialaccount.providers.trello', 'allauth.socialaccount.providers.tumblr', 'allauth.socialaccount.providers.twentythreeandme', 'allauth.socialaccount.providers.twitch', 'allauth.socialaccount.providers.twitter', 'allauth.socialaccount.providers.untappd', 'allauth.socialaccount.providers.vimeo', 'allauth.socialaccount.providers.vk', 'allauth.socialaccount.providers.weibo', # 新浪微博 'allauth.socialaccount.providers.weixin', # 微信 'allauth.socialaccount.providers.windowslive', 'allauth.socialaccount.providers.xing', )SITE_ID = 1 # 不要漏了这句哦2.在项目的urls.py(即与setting.py在同一文件夹的urls.py)里加上下面这句: ...

July 2, 2019 · 1 min · jiezi

运行Django项目出现RuntimeError错误的解决方法

运行Django项目出现: RuntimeError: cryptography is required for sha256_password or caching_sha2_password使用: pip install cryptography安装cryptography即可解决。

July 2, 2019 · 1 min · jiezi

python-time-datetime小结

[TOC] python -- time datetime小结time基本import timetime.time()Out[3]: 1561107970.0175698 # 时间戳time.localtime()Out[4]: time.struct_time(tm_year=2019, tm_mon=6, tm_mday=21, tm_hour=17, tm_min=6, tm_sec=20, tm_wday=4, tm_yday=172, tm_isdst=0) # 本地时间带格式 time.struct_timetime.gmtime()Out[5]: time.struct_time(tm_year=2019, tm_mon=6, tm_mday=21, tm_hour=9, tm_min=6, tm_sec=35, tm_wday=4, tm_yday=172, tm_isdst=0)# UTC时间带格式 time.struct_timetime.localtime().tm_ydayOut[6]: 172 # time.struct_time可以获取你想要的东西转换 -- 时间戳和struct timetime_stamp = time.time()local_struct_time = time.localtime(time_stamp) # 时间戳转struct timetime.mktime(local_struct_time)Out[13]: 1561108315.0 # struct time 转时间戳转换 -- 字符串和struct timetime.strftime("%Y-%m-%d, %H:%M:%S, %w", local_struct_time)Out[16]: '2019-06-21, 17:11:55, 5'==================================================================================time.strptime('2019-06-21, 17:11:55, 5', "%Y-%m-%d, %H:%M:%S, %w")Out[17]: time.struct_time(tm_year=2019, tm_mon=6, tm_mday=21, tm_hour=17, tm_min=11, tm_sec=55, tm_wday=4, tm_yday=172, tm_isdst=-1)转换 -- 格式化字符串和时间戳 -- 不可以time.strftime("%Y-%m-%d, %H:%M:%S, %w", time.time())Traceback (most recent call last): File "C:\Users\zhicfeng\Envs\etisalat\lib\site-packages\IPython\core\interactiveshell.py", line 2910, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "<ipython-input-15-fa1c7b40ca19>", line 1, in <module> time.strftime("%Y-%m-%d, %H:%M:%S, %w", time.time())TypeError: Tuple or struct_time argument requireddatetimeThe time module is written in C, and is mostly interface to low-level operating system functions. The datetime module is written in Python.基本类关系图 ...

July 2, 2019 · 2 min · jiezi

Django中Queryset的使用一

Queryset的使用Queryset是懒加载的,部分支持链式调用。 支持链式调用的接口: all接口: 用于查询所有数据filter接口: 根据条件进行过滤exclude接口: 同filter,只是相反的逻辑reverse接口: 把Queryset中的结果倒序排列distinct接口: 用来进行去重查询none接口: 返回空的Queryset不支持链式调用的接口: get接口:用于查询,存在返回对应的实例,不存在,则抛出DoesNotExist异常create接口:直接创建一个Model对象get_or_create接口:根据条件查找,如果没有查找到,就调用create创建update_or_create接口:同get_or_create,只是用来做更新操作count接口:用于返回Queryset有多少条记录latest接口:用于返回最新一条记录,但需要在Model的Meta中定义:get_latest_by = <用来排序的字段>earliest接口:同上,返回最早的一条记录first接口:从当前Queryset记录中获取第一条last接口:同上,获取最后一条exists接口:返回True或者False,只需要判断Queryset是否有数据用这个接口最合适bulk_create接口:同create,用来批量创建记录in_bulk接口:批量查询update接口: 用来根据条件批量更新记录delete接口: 同update,这个接口是用来根据条件批量删除记录。update和delete都会出发Django的signalvalues接口:当我们明确知道只需要返回某个字段的值,不需要Model实例时,可以使用values_list接口:同values,但直接返回的是包含tuple的Queryset

June 30, 2019 · 1 min · jiezi

我对-Django-中-froms-使用的理解

这篇文章谈一下我对 Django 中 forms的简单理解,确切的说像是一种代码的说明。 我觉得 Django 中的forms 就是view用与渲染成html语言的代码。用来帮助我们减少写html的工作量。 下面的代码就是本博客实现评论功能的forms。 from django import formsfrom .models import Commentimport mistuneclass CommentForm(forms.ModelForm): nickname = forms.CharField( label='昵称', max_length=50, widget=forms.widgets.Input( attrs={'class': 'form-control', 'style': 'width: 60%;'} ) ) email = forms.CharField( label='Email', max_length=50, widget=forms.widgets.Input( attrs={'class': 'form-control', 'style': 'width: 60%;'} ) ) website = forms.CharField( label='网站', max_length=100, widget=forms.widgets.URLInput( attrs={'class': 'form-control', 'style': 'width: 60%;'} ) ) content = forms.CharField( label='内容', max_length=500, widget=forms.widgets.Textarea( attrs={'row': 6, 'class': 'form-control'} ) ) def clean_content(self): content = self.cleaned_data.get('content') if len(content) < 10: raise forms.ValidationError('内容长度太短了!') content = mistune.markdown(content) return content class Meta: model = Comment fields = ['nickname', 'email', 'website', 'content'] 下面我一段一段的解释一下: ...

June 30, 2019 · 1 min · jiezi

从Django部署中学到的

修改pip的源 pip 的配置文件在 ~/.pip/pip.conf 文件内容如下: [global]index-url = http://mirrors.tencentyun.com/pypi/simpletrusted-host = mirrors.tencentyun.com豆瓣源的网址是: http://pypi.doubanio.com/simple/ 只需将 index-url = http://mirrors.tencentyun.com... trusted-host = mirrors.tencentyun.com 替换为: Index-url =http://pypi.doubanio.com/simple/ trusted-host = pypi.doubanio.com 即可。 杀死进程 例子: fuser -k 9090/tcp 上线typeidea 项目 我写的是一个django项目,使用uwsgi + nginx 因为上次上线时间久远,而且我也不了解nginx的配置文件怎么写。所以这次使用万能的重装大法,来上线这个博客网站。 输入: yum remove nginx卸载nginx 输入: rm -rf /etc/nginx/删除之前的配置文件 输入: yum install nginx安装nginx 输入: cd /etc/nginx/进入nginx 的配置文件夹 输入: ll 可以看到如下文件 输入: systemctl nginx.service start 启动nginx服务,打开浏览器输入服务器的公网IP见到下面的界面证明安装成功。 接下来安装uwsgi 输入: pip3 install uwsgi ...

June 30, 2019 · 1 min · jiezi

编程语言之问何时该借用何时该创造

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/Oy... 6 月 22 日,Python 之父 Guido 发了一条推特,说了 Python 的一则历史故事,他说 elif 是从 C 语言中偷过来的: elif 是“else if”的简写,用于条件判断。当只有两个分支时,我们会写成“if...else...”,当出现更多分支时,我们会写成如下格式: if 判断条件1: 做事情1elif 判断条件2: 做事情2else: 做其它事简写而成的 elif 不仅是减少了几个字符,而且由于单一而清晰的用途,它还不会给我们带来理解或使用上的困惑。 但是,简写法并不是主流,完整写法才是主流,C 语言中就是采用完整的写法: if(判断条件1){ 做事情1}else if(判断条件2){ 做事情2}else { 做其它事}没错,C 语言使用的是全拼写法,但是在它的预处理/预编译语句中,还有一个 elif 指令,Guido 所说的“偷”,就是从这来的: #if 常量表达式1// 编译1#elif 常量表达式2// 编译2#else// 编译3#endifPython 没有预编译,所以所谓的偷,跟预编译没有关系,只是在对比两种写法后,借用了更简洁的写法而已。 为什么 C 语言不把两种写法统一起来呢?这我不得而知了,而 Guido 在两种写法中,选择了后一种非主流却更好用的写法。我想对他说,你“偷”得好啊! 实际上,留言区里的人也有同感,纷纷表示:不介意、很 okay、非常喜欢,还有人说“不是偷,而是收获(harvested)”、“不是偷,而是把它提升了一些高度”…… 前不久,我写了一篇《聊聊 print 的前世今生》,print 这个词就是从 C 语言中借用来的。除此之外,如果有人仔细比较这两种语言的关键字和习惯命名,肯定会发现不少相同的内容。 编程语言间有一些共享的元素,这很常见,创造一门语言并不意味着要原创每一个词句,毕竟大部分思想是共通的,作为基础设施的词语更是如此。 那么,我突然好奇了:创造一门编程语言时,什么时候该借用,什么时候该创造呢? 这个问题看起来可能没啥意义,因为终其一生,我们多数人也不大可能会参与创造一门编程语言。 但我觉得它还是极有意义的,首先,提问精神值得肯定,其次,它还提供了一种溯源、甄别、遴选、创造的体系性视角,我认为这是求知的正确思维方式。 带着这个疑惑,我特别想要考察的是 Python 的 for 循环。 ...

June 30, 2019 · 3 min · jiezi

django修改数据库表名

前提数据库的表名自动生成,按照APPname+表名生成的,但是由于接口需要,表名不可以是这样的构造,只能是表名称自己,不可以加前缀,以下记录一下修改过程以及命令。 Django版本:1.6 第一步:生成空文件,以记录变更 命令:python manage.py schemamigration appname --empty name_of_migrationappname:app的name根据实际情况name_of_migration:最终生成修改记录文件的名称,可以用这个也可以随便取一个别的,就是0001开头的文件,自动保存在migrations目录下第二步:修改生成的文件 打开第一步生成的文件修改如下部分内容class Migration(SchemaMigration):def forwards(self, orm): db.rename_table('yourapp_foo', 'yourapp_bar')def backwards(self, orm): db.rename_table('yourapp_bar','yourapp_foo') 旧名字:yourapp_foo新名字:yourapp_bar(这里可以用appname+名字,也可以直接写名字)第三步:同步数据库 python manage.py migrate appname

June 27, 2019 · 1 min · jiezi

当谈论迭代器时我谈些什么

花下猫语:之前说过,我对于编程语言跟其它学科的融合非常感兴趣,但我还说漏了一点,就是我对于 Python 跟其它编程语言的对比学习,也很感兴趣。所以,我一直希望能聚集一些有其它语言基础的同学,一起讨论共通的语言特性间的话题。不同语言的碰撞,常常能带给人更高维的视角,也能触及到语言的根基,这个过程是极有益的。 这篇文章是群内 樱雨楼 小姐姐的投稿,她是我们学习群里的真·大佬,说到对 Python 的研究以及高阶知识的水平,无人能出其右(群里很多同学都被她实力圈粉啦)。除了 Python,她对 C++、Perl、Go 与 Fortran 等语言都有涉猎,本文主要是对比了 Python 与 C++,来深入谈谈迭代器。话不多说,请看正文。 樱雨楼 | 原创作者 豌豆花下猫 | 编辑润色 本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/Be... 0 前言迭代器(Iterator)是 Python 以及其他各种编程语言中的一个非常常见且重要,但又充满着神秘感的概念。无论是 Python 的基础内置函数,还是各类高级话题,都处处可见迭代器的身影。 那么,迭代器究竟是怎样的一个概念?其又为什么会广泛存在于各种编程语言中?本文将基于 C++ 与 Python,深入讨论这一系列问题。 1 什么是迭代器?我们为什么要使用迭代器?什么是迭代器?当我初学 Python 的时候,我将迭代器理解为一种能够放在“for xxx in ...”的“...”位置的东西;后来随着学习的深入,我了解到迭代器就是一种实现了迭代器协议的对象;学习 C++ 时,我了解到迭代器是一种行为和指针类似的对象... 事实上,迭代器是一个伴随着迭代器模式(Iterator Pattern)而生的抽象概念,其目的是分离并统一不同的数据结构访问其中数据的方式,从而使得各种需要访问数据结构的函数,对于不同的数据结构可以保持相同的接口。 在很多讨论 Python 迭代器的书籍与文章中,我看到这样两种观点:1. 迭代器是为了节约数据结构所产生的内存;2. 遍历迭代器效率更高。 这两点论断都是很不准确的:首先,除了某些不定义在数据结构上的迭代器(如文件句柄,itertools 模块的 count、cycle 等无限迭代器等),其他迭代器都定义在某种数据结构上,所以不存在节约内存的优势;其次,由于迭代器是一种高度泛化的实现,其需要在每一次迭代器移动时都做一些额外工作(如 Python 需要不断检测迭代器是否耗尽,并进行异常监测;C++ 的 deque 容器需要对其在堆上用于存储的多段不连续内存进行衔接等),故遍历迭代器的效率一定低于或几乎接近于直接遍历容器,而不太可能高于直接遍历原容器。 综上所述,迭代器存在的意义,不是为了空间换时间,也不是为了时间换空间,而是一种适配器(Adapter)。迭代器的存在,使得我们可以使用同样的 for 语句去遍历各种容器,或是像 C++ 的 algorithm 模块所示的那样,使用同样的接口去处理各种容器。 ...

June 27, 2019 · 2 min · jiezi

如何挑选注册到合适的域名

为了选择一个好域名,有很多需要注意的地方。 我已经准备了一份详细的参考准则列表,尽管无法满足所有这些标准(这是不可能的),但仍然为你提供了一些需要考虑的提示信息。在文章最后,我会告诉你在哪里注册域名最好。 好了,我们继续吧! 1、选择.com后缀大多数时候,互联网用户在浏览时会假设网址以 .com 结束,如果采用 .net、.info、.tv,或其他后缀,那很可能会给潜在的访客留下障碍。话虽如此,仍然有很多成功的网站使用 .net(或其他)后缀,但对于绝大部分人来说,.com 是最理想的。 2、短小精致越短越好,足够表明意思。 3、容易朗读和拼写目的是让你和别人可以轻松分享,如果大家不必停下来思考如何朗读或拼写,那么分享起来就更加容易。 4、不含连字符包含连字符的域名不够流畅,看起来也很奇怪。 5、使用关键字你的域名是使用一两个关键字的最佳位置之一,而且越紧凑、越接近域名的开头越好。 6、考虑使用你的名字我强烈建议你将名字注册为域名,即使你没有计划对其进行任何操作。为什么?因为你永远不知道自己将来是否会成为家喻户晓的人物。 如果你打算使用博客来销售服务,或者你希望演讲或成为出版作家,那么你的名字可能就是最完美的域名。 如果你的名字非常难以阅读或拼写,那你可以考虑使用昵称,或者定义一个全新的名字。 7、使其可扩展你永远不知道自己的业务可能会如何扩展,所以请避免使用限制性名称。例如,LovelyCat.com很不错,但是如果你想谈论狗狗呢?此外,我还建议避免使用特定于生命阶段的名称,例如AllAboutMyCrazyBabies.com,现在好了,但他们蹒跚学步的岁月过得这么快! 8、避免使用单词如果你拥有广泛的兴趣爱好,并且还希望在域名中使用所有这些关键字,那你很可能想要将它们全部串在一起。但我建议不要这样做,因为这可能会令人困惑。 9、避免使用晦涩的术语如果你试图吸引广泛的受众群体,那就请避免在域名中使用特定范围的字词,因为这会导致你的 Niche 之外的访客不熟悉。 10、但是所有好名字都被拿走了!你很可能已经想出了一个完美的域名,但是在注册时才发现它已被采用。不要害怕失败,尝试在词库中查找类似的单词,询问别人的想法,向上或向左混合单词,使用你一直说的标语、昵称或短语。 有时候实在对域名想不出来,我们可以借助一些工具来给我们 idea,比如: namemesh.comBustname.comdomainr.com 这三个网站使用大数据,有时取出来的名字挺酷的, 有时会加一些像 ”ly” 或者 “ista” 的后缀,让整个域名看起来更酷,像一个品牌官方网站。Faucetista.com 或 Brushtoothly.com 相比 BestBrushTooth.com 或 FaucetReviews.com 更加吸引人,对吧? 11、确保该名称在其他社交媒体网站上可用选择你的域名时,请检查其他社交媒体,以确保它可以在这些网站上使用。如果你在博客、Twitter、Facebook 等媒体上使用相同的名称,那么你的品牌会更加巩固,并且更令人难以忘怀。 12、不要过度思考我听到很多人在这个问题上陷入困境,因为他们害怕做出错误的选择。最常见的理由是他们找不到可用的 .com。如果你也这样,那就做出最好的猜测并继续前进,一个不太完美的域名比没有更好,尽力而为,开始注册吧! 希望我已经说服了你,那么该怎样注册域名呢? 如果你没有网站并且不打算尽快创建一个,只需在输入框中键入你想要的域名,然后点击搜索按钮即可开始使用。

June 25, 2019 · 1 min · jiezi

django之modeltodict

def model_to_dict(instance, fields=None, exclude=None): """ Returns a dict containing the data in ``instance`` suitable for passing as a Form's ``initial`` keyword argument. ``fields`` is an optional list of field names. If provided, only the named fields will be included in the returned dict. ``exclude`` is an optional list of field names. If provided, the named fields will be excluded from the returned dict, even if they are listed in the ``fields`` argument. """ from django.db import models opts = instance._meta data = {} for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many): if not getattr(f, 'editable', False): continue if fields and f.name not in fields: continue if exclude and f.name in exclude: continue data[f.name] = f.value_from_object(instance) # Evaluate ManyToManyField QuerySets to prevent subsequent model # alteration of that field from being reflected in the data. if isinstance(f, models.ManyToManyField): data[f.name] = list(data[f.name]) return data @total_ordering@python_2_unicode_compatibleclass Field(RegisterLookupMixin): # "省略其他代码..." def value_from_object(self, obj): """ Returns the value of this field in the given model instance. """ return getattr(obj, self.attname)上面是django中model_to_dict的源码,通过注释我们可以非常的明白这个方法的作用,然而在实体项目中,因为习惯了构造dict的方式作为返回值,所以大多数的时候我们也不太会想到它,以至于我都忘了他,然而却不能忽略拥有它的方便. ...

June 24, 2019 · 1 min · jiezi

聊聊-print-的前世今生

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/Nu... (一) 上周,我翻译了一篇文章,解释了为什么 Python 3 把 print 改为函数? 概括有如下几点原因:1、print 不适宜作为应用程序级的语句。2、改为一个函数,可以实现更复杂的功能。3、改为一个函数,能方便地进行替换。 在 Python 2 中,print 是个语句(statement),它的级别就跟 for、if、def 等关键字相同,这是一个古老的设计(毕竟 Python 诞生于 1989 年),改成 print() 函数,意味着它升级了。 在查阅资料的时候,我发现 print 在历代版本中,一直发展变化,到了今天,它自身已足够完善了,可是外部的挑战一直不断。 因此,这篇文章再来聊聊它:介绍 print 的现状,追溯它的历史,说说它的挑战者,挖挖那些更加本质的东西。 (二) 在 3.0 版本中,print() 函数全新登场,开发者可以自定义打印对象的间隔(默认是空格)、终止方式(默认是换行)、以及输出位置(默认是标准输出 sys.stdout)。 而到了 3.3 版本,它还添加了一个新的参数,可以决定是否要刷新数据流。 至此,这个函数的完整格式就变成了 print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False) ,与升级前的 print 语句是天壤之别啦。 优点是显而易见的,可定制的参数带来了使用场景的扩充。 (三) 其实,在这次大版本的改动之前,早期的 print 语句并非是一成不变的,核心开发者们一直在完善它。 例如,在 2000 年的 PEP-214 之前,print 语句只能用于标准输出(sys.stdout),只有当提出这个提案后,print 才可以打印内容到类文件对象(file-like object)中。 (注:PEP 即 Python 改进提案,更多介绍详见旧文《学习Python,怎能不懂点PEP呢?》) ...

June 23, 2019 · 2 min · jiezi

Django搭建个人博客用djangoallauth实现第三方登录

现在我们已经拥有一个可以进行用户本地登录的博客系统了。如果有人欣赏你的文章,说不定就会注册成为本地用户,并和你好好交流一番。 但头疼的是,用户可能每天都在互联网上浏览很多非常棒的博客,如果每个博客都要去注册才能评论,对用户是个不小的负担。对个人博客这类草根网站,说不定用户就懒得去注册了,你也就损失了一个潜在的”粉丝“。 比较流行的解决方案是允许用户通过第三方登录,即可以通过GitHub、微博这类知名社区的授权,从而登录你的小站,免去了注册的麻烦。 本章会介绍一个强大的库:Django-allauth,它不仅包含一整套的本地注册、登录、管理的解决方案,还支持GitHub、Twitter、微博、微信甚至百度等几十种第三方登录方式,真的是当爹又当妈啊... 本地登录先看看django-allauth的本地登录如何配置。 安装django-allauth: (env) > pip install django-allauth修改配置文件: my_blog/settings.py...TEMPLATES = [ { ... 'OPTIONS': { 'context_processors': [ # allauth 启动必须项 'django.template.context_processors.request', ], }, },]AUTHENTICATION_BACKENDS = ( # Django 后台可独立于 allauth 登录 'django.contrib.auth.backends.ModelBackend', # 配置 allauth 独有的认证方法,如 email 登录 'allauth.account.auth_backends.AuthenticationBackend',)INSTALLED_APPS = [ ... # allauth 启动必须项 'django.contrib.auth', 'django.contrib.messages', 'django.contrib.sites', 'allauth', 'allauth.account', 'allauth.socialaccount', # 可添加需要的第三方登录 'allauth.socialaccount.providers.github', 'allauth.socialaccount.providers.weibo', ...]# 设置站点SITE_ID = 1# 登录成功后重定向地址LOGIN_REDIRECT_URL = '/article/article-list'...注意上面的配置中,有的内容是创建项目时本来就有的,检查一下你的项目中是否包含;有的内容是完全新增的,不要漏掉了。 django-allauth也是一个app,因此需要分配给它url: ...

June 21, 2019 · 2 min · jiezi

怎么选择确定个人博客网站的主题

除非你的博客完全是为了满足自己的乐趣,否则你肯定希望获得读者。因此,考虑别人可能喜欢的内容非常重要。多年来我一直在关注博客圈,在吸引读者方面,有些方法确实非常有效,下面是一些选择博客主题的实用技巧。 读者想要什么1. 读者想要解决问题 人们对此感到沮丧吗?你有解决方案吗?这是大多数博主成功的方式。很多人非常喜欢写作,但对如何建设一个博客网站毫无头绪,所以我分享了一系列文章、提示、工具和教程来帮助解决这个问题。 2. 读者想要减轻他们的恐惧 人们害怕什么?你怎样能帮助缓解这些恐惧?也许有些人曾经失去过人生中最重要的东西,分享你的希望和治愈故事对很多人都非常有帮助。此外,你还可以为那些面临失业或金融灾难的人提供帮助。 3. 读者想要学习新东西 如果有一样知识或技能,只有你知道,那你会怎么做?为什么不把自己擅长的东西交给别人呢?也许你是编织高手,也许你有写作诀窍,或者你有一种独特的教学方式。很多人都有一些难以解决的烦恼,但不知道从哪里开始,教他们吧。 4. 读者希望达成目标 人们有哪些共同的目标?你有没有设定并达到一些重要目标?你是如何做到这一点的?分享出来并激励他们吧。最容易想到的可能是健身、减肥、摆脱债务。过于远大的目标可能会令人沮丧和孤独,但有些人确实一直在创造奇迹。 5. 读者想要放松一下 你有一个引人入胜的故事吗?你过着有趣的生活吗?你是一个搞笑的人吗?每个人都需要停下来休息,大量博客纯粹是为了娱乐。不过,我认为这是一条棘手的道路,因为在互联网、杂志和电视上,娱乐的内容并不缺乏,但这并不意味着这条路完全不可行。关键是提供一些独特的东西,当然,作为奖励,你和别人都可以获得快乐。例如,如果你家里养了一头骆驼,谈谈你是怎样养骆驼的。 博客主题提示现在你已经了解了其他人正在寻找的内容,这里列出了一些提示,可以帮助你形成一个更好的博客主题。 我的博客主题是好的吗? 在开始撰写文章之前,你可能要先考虑自己的博客主题是否是一个好主意,例如: 是否会有人阅读?这个主题是否能赚钱?这可能是两个最常见的问题,毕竟,谁想把时间和精力浪费在没有访客,或者无法赚钱的事物上呢? 事实上,几乎每个你能想到的主题都能建立一个博客,从尴尬的家庭照片,到美味的佳肴食谱,以及介于两者之间的一切内容。简而言之,话题本身通常并不是问题,大多数主题都能吸引访客,并让你从中赚到一些钱。 主要的问题在于: 你对这个话题的热情是否具有感染性,那些感兴趣的人会被你吸引,但那些不感兴趣的人也会变成这样吗?你能够持之以恒地写下去吗?大多数博客需要很长时间才能获得收入,并产生动力。一旦到了这个时候,你就必须继续前进。所以,你必须长期参与其中。怎样在一个竞争市场中脱颖而出?互联网上有数以百万计的博客,有很多人可能正在撰写跟你主题相同(或非常相似)的文章,你如何保持独特性?世界各地的读者会喜欢你的文章,并分享它们吗?我的博客主题是否能够赚钱?博客的好处是几乎任何话题都可以产生收入,但是没有办法保证特定主题会产生多少收入。例如,有很多摄影博客赚取了大量利润,但更多的却没赚多少。 重要的不是主题,而是这个主题呈现的资源。以某种方式书写出来,吸引更多的读者阅读,然后与他们建立关系,创建或推广他们愿意购买的产品。 专业提示:有一种方法可以快速判断特定主题或 Niche 市场是否赚钱,那就是在 Google 、百度中搜索特定关键字。如果页面顶部拥有大量广告,那就说明这个市场非常有利可图。但需要注意的是,缺少广告并不一定表明这个市场无法赚钱,更深入的挖掘可能会让你另辟蹊径。 为别人而写很多新博主在创建博客时都没有超出自己的兴趣,毫无疑问,你的博客应该是你的延伸,但如果你的文章不是为别人带来好处,那么写日记可能更好。 选择一个Niche不要写出任何想到的东西,试着围绕一个特定的话题来写,这将成为你的 Niche。不仅实现起来更容易,也会为你带来更多读者。虽然不是必需的,但 Niche 提供了重点和方向,让你的博客更加易于理解。 选定的 Niche 太宽泛还是太具体?如果你的博客主题过于宽泛,那么与其他的博客和网站相比,你将很难脱颖而出。另一方面,如果你的主题太过狭隘,那么感兴趣的读者群体将非常小,导致你无法获得足够的收入。 例如,摄影是一个非常广泛的主题,但美国50人镇的摄影无法给你带来很多观众,黑白摄影更好,张家界黑白摄影则可能更好一些。你的目标是找到一个包含大量感兴趣的人,以及大量潜在子主题的主题,这需要做一些研究来缩小范围。 这个 Niche 是否已经饱和?回到互联网诞生之初,那个时候的在线博客寥寥无几,你几乎可以挑选任何主题。但发展到现在,几乎所有的 Niche 都已经被充分挖掘,因此想要占据一席之地非常困难。当你想到一个主题时,请去搜索关于这个主题的数十个热门博客,然后再决定自己的主题是否合适。 然而,仅仅因为一个Niche很大并不意味着你要避开,毕竟,一个巨大的 Niche 意味着它有一个不错的市场。如果你能从中脱颖而出,那么必将受益无穷。 这个 Niche 的读者是否愿意花钱?如果你希望创造不错的收入,那么这是一个重要的问题,想想你的 Niche 与读者之间的交集。例如,如果你希望推广高端服装产品,那么针对陷入困境的大学生可能不是一个好主意。 另一种看待这个问题的方式:这个 Niche 的其他博客赚钱吗?那些博客有广告商吗?他们是否活跃,充满吸引力并且在不断增长? 你有足够的内容可写吗?选择一个可以定期和永久撰写的主题,请记住,你很有可能需要长期处于这种状态。如果你每周发布一次,那么每年只有 52 篇文章;一周三次?156 篇;一周五次?260 篇…… 而这只是刚刚开始!不要选择一些过于狭窄的东西,以免在几周或几个月之后,发现没有内容可写。 有一个好方法可以测试这个问题:集体讨论与你的主题相关的文章或子主题,如果你能够轻松提出几十个关于如何分支的列表,那么这可能是一个好兆头。但如果你想不到很多,那可能就需要重新考虑这个主题。此外,解决此问题的另一种方法是多个作者一起来发布文章。 选择一个你真正热爱的话题如果你对所写的内容没有真正的兴趣,那将是一种可怕的拖累和负担。在现实生活中,如果你可以和朋友就一个话题谈论几个小时,那么这就是用于创建博客的一个很好的主题。 选择一个可以成为权威的 Niche 市场这里的关键是思考细分领域,或者选择一个较小的 Niche 市场,然后深入一个主题来讨论。尽管可能会有一小部分的潜在读者,但你也可以更快地获得更多关注。 ...

June 20, 2019 · 1 min · jiezi

django中的cachedproperty

今天在修改之前做的一个搜索接口,虽然使用了haystack,但是由于需要修改请求参数和响应数据格式,所以大费周折调试了老一会儿,问题是这样的,大家如果有好的点子可以留言哦: haystack默认的请求接口为.../search?q=搜索关键字&models=xxxx.xxxx&models=xxxx.xxxx我设计的接口.../search?q=搜索关键字&f=搜索类型我们的项目分为搜索全部及类型搜索,所以像默认的接口太暴露,所以我设计的短小干练了点,但是怎样去实现,看源码我实现了第一版: class KaokaoSearchView(SearchView): def __call__(self, request): type = int(request.GET.get('f', 0)) models = { 0: 'xxx.xxxxx', 1: 'xx.xxxxx', 2: 'xxxxxx.xxxxxxxxx' } data = request.GET _mutable = data._mutable data._mutable = True data['models'] = models.get(type, '') data._mutable = _mutable self.request = request但是问题来了,多个model搜索怎样实现?我反正首先是进行照葫芦画瓢: models = { 0: 'xxx.xxxxx', 1: 'xx.xxxxx', 2: 'xxxxxx.xxxxxxxxx', 9: 'xxx.xxxx&xx.xxxxx&xxxxxx.xxxxxxxxx' }结果是这样的: 而正确的应该是这样的: 原来models需要的是一个list,这好办 models = { 0: 'xxx.xxxxx', 1: 'xx.xxxxx', 2: 'xxxxxx.xxxxxxxxx', 9: ['xxx.xxxx', 'xx.xxxxx', 'xxxxxx.xxxxxxxxx'] }出现了这种情况: ...

June 20, 2019 · 1 min · jiezi

聊聊-Python-的内置电池

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/XzCqoCvcpFJt4A-E4WMqaA (一) 最近,我突然想到一个问题:相比其它语言,有哪些概念或习惯叫法是 Python 特有的? 在朋友圈提出这个问题后,我得到最多的回复是——Pythonic 。这个回复一点都不意外,名字中自带 Python 的,当然是特有的啦,与它相似的,还有 Pythonista 。 这两个词是啥意思呢?Python 圈内流传着一个说法“人生苦短,我用 Python”,人们相信存在着最佳的实践方式,采用这种方式是最美的、最高效的、最优雅的,也即是 Pythonic ,而这样做的人(或以此为追求的人)则自称是 Pythonista。这个称号是有别于 Pythoner 或者 Pythonist 的,简单地说就是,它更有追求、更有逼格。 除了以上两个,Python 还有众多独特的叫法,例如终生仁慈独裁者、装饰器、上下文管理器、推导式与生成式、鸭子类型、猴子补丁、魔术方法、GIL、内置电池,等等。它们有的并不是 Python 所原创或独有,但是却因为它才广为人知,它们在 Python 中是代表性的存在物。 (二) 这些内容都很有意思,本文唯独想聊聊它——内置电池 。 Batteries Included 这个叫法是 Python 特有的,它指的是 Python 拥有“内置电池”,也就是自带丰富多样的标准库,开箱即用,动力十足。 在《PEP 206 -- Python Advanced Library》中,它提出了“内置电池的哲学”(Batteries Included Philosophy):拥有丰富而通用的标准库,无需用户单独下载就能立即使用。还说这使得 Python 领先于很多项目。 根据官方文档显示,Python 内置了 200 多个标准库,类型丰富多样,包括字符处理、数据类型、数值计算、文件处理、并发执行、网络通信、多媒体服务、图形界面、调试与开发、以及操作系统专有服务等等。 内置电池为 Python 提供了一种自给自足的能力(self-sufficient),在大多数情况下,用户不需要再去下载和安装单独的软件包,因此也免去一大堆的依赖问题的折磨。 (三) 某些编程语言中也有内置电池的概念,例如 Perl、Ruby、PHP等等,还有的语言会强调自己内置了强大的功能,例如 Erlang(一切皆进程)、Go(goroutine 机制)。 然而,这个叫法在 Python 中被叫得最响,也被推广到了技术生态中的其它项目里,几乎成了 Python 的专有名词。 在维基百科上搜索“Batteries Included”,该条目有 4 个解释,其中之一表明它是 Python 的 Motto ,这个词的意思是座右铭、格言、箴言,足见分量之重了吧。 ...

June 15, 2019 · 1 min · jiezi

三步实现Django-Paginator-分页

Django提供了一个新的类来帮助管理分页数据,这个类存放在django/core/paginator.py.它可以接收列表、元组或其它可迭代的对象。本文将分三步介绍Django Paginator 分页的实现步骤 一、通过模型创建SubjectDjango models from django.db import models class Subject(models.Model): """学科""” no = models.AutoField(primary_key=True, verbose_name="编号") name = models.CharField(max_length=31, verbose_name="名称") intro = models.CharField(max_length=511, verbose_name="介绍") def __str__(self): return self.name class Meta: db_table = 'tb_subject’ verbose_name_plural = "学科"二、通过view模块,要呈现到前端的数据Django view 的配置 from django.shortcuts import renderfrom django.core.paginator import Paginator, InvalidPage, EmptyPage, PageNotAnIntegerfrom vote.models import Subjectdef show_subject(request): """查询学科""" #查询Subject 表的所有数据 subjects = Subject.objects.all().order_by("no”) #使用Paginator模块对数据分页,一页5条数据 paginator = Paginator(subjects, 5) #使用request.GET.get()函数获取uri中的page参数的数值 page = request.GET.get('page') try: #通过获取上面的page参数,查询此page是否为整数并且是否可用 subject_obj = paginator.page(page) except PageNotAnInteger: subject_obj = paginator.page(1) except (EmptyPage, InvalidPage): subject_obj = paginator.page(paginator.num_pages) return render(request, "vote/subject.html", {'subject_list': subject_obj})三、将view中的数据渲染到前端模版上前端分页代码块: ...

June 11, 2019 · 1 min · jiezi

django

django

June 6, 2019 · 1 min · jiezi

Django搭建个人博客锚点定位

老读者注意:上一章消息通知有个bug,即发给管理员的notify必须移动到new_comment.save()的后面,否则会导致action_object存储为NULL,并且导致本章的html拼接锚点失效。原文已更正,为博主的疏忽表示歉意。 上一章已经实现了消息通知功能,可以很人性化的把用户引导到被他人回复的页面中去。 但是仔细想想,似乎还有不方便的地方:如果页面中评论较多,想找到感兴趣的那一条评论还是要费点功夫的。所以这个消息通知,最好是能够不仅前往正确的页面,还要前往正确的位置(需求是无穷无尽的..)。 为了实现这个功能,本章就要介绍一个非常古老的功能:锚点定位。以及如何在Django中实现它。 锚点是什么我们在写html文件的容器时,经常会用到id属性: <div id="fruit">apple</div>这个id属性不仅可以作为Javascript或者css代码查询某个容器的标记,还可以作为锚点,定位页面应该前往的位置。输入下面的地址: http://www.myblog.com/home#fruit浏览器就会打开home页面,并且视窗前往id="fruit"的容器。 明白了锚点是什么,下面就通过三种不同的实现方法,看看锚点在Django博客项目中是如何应用的。 三种实现html拼接锚点首先要实现的功能,就是当管理员点击消息通知时,浏览器视窗前往此通知的评论位置。 因此首先修改文章详情页面,给渲染评论的div容器添加id属性: templates/article/detail.html...<!-- 已有代码,遍历树形结构 -->{% recursetree comments %}{% with comment=node %}<!-- 唯一新增代码:id属性 --><div class="..." id="comment_elem_{{ comment.id }}" > ... <!-- 下面都是已有代码 --> <div class="children"> {{ children }} </div> {% endif %}</div>{% endwith %}{% endrecursetree %}...我们还是用comment.id来给每条评论赋予唯一的id值。注意id属性保持唯一性。前面在二级回复的Modal中用了comment_{{ comment.id }},这里千万不要重复了。 然后修改通知列表模板,添加锚点: templates/notice/list.html...{% for notice in notices %}<li ...> <!-- 新增 comment_elem_{{ notice.action_object.id }} 锚点 --> <a href="{% url "notice:update" %}?article_id={{ notice.target.id }}&notice_id={{ notice.id }}#comment_elem_{{ notice.action_object.id }}" target="_blank" > ... </a> ...</li>{% endfor %}...注意这里url中拼接了两种玩意儿: ...

June 5, 2019 · 2 min · jiezi

Python猫荐书系列之七Python入门书籍有哪些

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/ArN-6mLPzPT8Zoq0Na_tsg 最近,猫哥的 Python 技术学习群里进来了几位比较特殊的同学:一位初三的以编程为兴趣的女生、一位在大学里刚开始执教 Python 的老师、一位四十多岁仍在编程一线的工程师。 自从写公众号以来,我就遇到了各色各样的人,比如,一位代替小学生儿子来加群的牙医父亲、一位多年自由职业每天炒股的前黑客、一位来咨询课程的自学编程的听障人士…… 其实,这些人都是极少数的个例,读者里绝大部分应该都是在校学生、程序员或即将转行成为程序员的人,但是,这些身份特殊的少数人群却触动了我。 一方面,我看到了 Python 的强大吸引力,另一方面,我也看到了 Python 学习群体的多元化。 近些年,为什么各类培训机构会大行其道呢?也许正是因为这庞大而多元的学习人群,想要挤上通往 Python 引力中心的桥梁啊! 我以前总是有意无意地忽略了这些读者的存在。前几天,我接了极客时间的一个专栏推广,在跟一些读者的互动中,以及在一些现象的观察中,我加深了对这些非主流人群的认识。 意识到了这一点后,我想,或许我也能为他们做点什么?至少以后在写文章的时候,应该设法做到兼顾吧。 正好,最近又有几位不同身份的初学者来咨询,要我推荐几本入门书籍,而我们荐书系列已经停更了两个多月,所以,本期荐书就来推荐一些入门书籍吧。 为了准备这期荐书,我专门搜集了 40 本 Python 入门书籍,现在全部加入到了一份豆瓣豆列里,方便大家查看。 先给大家看看完整的书单吧。 豆列:https://www.douban.com/doulist/114507342/ 《“笨办法”学Python》 https://book.douban.com/subje... 《python学习手册(原书第5版)》https://book.douban.com/subje... 《Head First Python(中文版)》https://book.douban.com/subje... 《Python基础教程(第3版)》https://book.douban.com/subje... 《Python编程无师自通》https://book.douban.com/subje... 《从Python开始学编程》https://book.douban.com/subje... 《Python编程之美:最佳实践指南》https://book.douban.com/subje... 《Python语言及其应用》 https://book.douban.com/subje... 《Python编程:从入门到实践》 https://book.douban.com/subje... 《像计算机科学家一样思考Python (第2版)》https://book.douban.com/subje... 《Python编程快速上手》 https://book.douban.com/subje... 《Python游戏编程快速上手》https://book.douban.com/subje... 《爱上Python》https://book.douban.com/subje... 《Python编程初学者指南》 https://book.douban.com/subje... 《Python语言程序设计基础(第2版)》https://book.douban.com/subje... 《Python语言程序设计》https://book.douban.com/subje... 《Python入门经典》https://book.douban.com/subje... 《Python入门经典》https://book.douban.com/subje... 《Python编程导论(第2版)》https://book.douban.com/subje... 《计算机编程导论—Python程序设计》https://book.douban.com/subje... 《趣学Python编程》 https://book.douban.com/subje... 《Python带我起飞:入门、进阶、商业实战》https://book.douban.com/subje... 《Python趣味编程入门》https://book.douban.com/subje... 《从问题到程序-用Python学编程和计算》https://book.douban.com/subje... 《跟老齐学Python》https://book.douban.com/subje... 《零基础学Python》https://book.douban.com/subje... 《Python程序设计入门到实战》https://book.douban.com/subje... 《从零开始学Python网络爬虫》https://book.douban.com/subje... 《零基础学Python图文版》https://book.douban.com/subje... ...

May 26, 2019 · 1 min · jiezi

创建第一个django项目

环境 系统:win10python: 3.6.3django:2.1.7查看python版本python查看django版本python -m django --version创建django项目django-admin startproject firstDjango生成如下目录 firstDjango/ manage.py db.sqlite3 firstDjango/ __init__.py settings.py urls.py wsgi.py这些目录和文件的用处是: 最外层的 firstDjango/ 根目录只是你项目的容器, Django 不关心它的名字,你可以将它重命名为任何你喜欢的名字。manage.py: 一个让你用各种方式管理 Django 项目的命令行工具。你可以阅读 django-admin and manage.py 获取所有 manage.py 的细节。里面一层的 firstDjango/ 目录包含你的项目,它是一个纯 Python 包。它的名字就是当你引用它内部任何东西时需要用到的 Python 包名。 (比如 firstDjango.urls).firstDjango/__init__.py:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。如果你是 Python 初学者,阅读官方文档中的 更多关于包的知识")。firstDjango/settings.py:Django 项目的配置文件。如果你想知道这个文件是如何工作的,请查看 Django settings 了解细节。firstDjango/urls.py:Django 项目的 URL 声明,就像你网站的“目录”。阅读 URL调度器 文档来获取更多关于 URL 的内容。firstDjango/wsgi.py:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。阅读 如何使用 WSGI 进行部署 了解更多细节。cd 进入项目cd firstDjango运行python manage.py runserver得到如下输出:浏览器访问 https://127.0.0.1:8000/,可以看到如下页面。

May 19, 2019 · 1 min · jiezi

Django搭建个人博客用djangonotifications实现消息通知

凭借你勤奋的写作,拜读你文章的用户越来越多,他们的评论也分散在众多的文章之中。作为博主,读者的留言肯定是要都看的;而读者给你留言,自然也希望得到回复。 怎么将未读的留言呈现给正确的用户呢?总不能用户自己去茫茫文章中寻找吧,那也太蠢了。给评论增加通知功能就是很流行的解决方案:比如微信朋友圈留言的通知、新浪微博留言的通知、以及各种社交平台的“小红点”。 本篇将以django-notifications为基础,非常高效的搭建一个简易的通知系统。 发送通知前面的步骤我们已经很熟悉了。 首先安装django-notifications: (env) > pip install django-notifications-hq注册app: my_blog/settings.py...INSTALLED_APPS = [ ... 'notifications', ...]...在根路由中安装路径: my_blog/urls.py...import notifications.urlsurlpatterns = [ ... path('inbox/notifications/', include(notifications.urls, namespace='notifications')), ...]...注意这里的notifications.urls没有像之前一样用字符串,是为了确保模块安装到正确的命名空间中。 数据迁移: (env) > python manage.py migrateapp就安装好了。 接下来你就可以在项目的任何地方发送通知了!像这样: from notifications.signals import notifynotify.send(actor, recipient, verb, target, action_object)其中的参数释义: actor:发送通知的对象recipient:接收通知的对象verb:动词短语target:链接到动作的对象(可选)action_object:执行通知的对象(可选)有点绕,举个栗子:杜赛 (actor) 在 Django搭建个人博客 (target) 中对 [你]() (recipient) [发表了]() (verb) [评论]() (action_object)。 因为我们想要在用户发表评论的时候发送通知,因此修改一下发表评论的视图: comments/views.py...from notifications.signals import notifyfrom django.contrib.auth.models import User...def post_comment(...): ... # 已有代码,创建新回复 if comment_form.is_valid(): ... # 给管理员发送通知 if not request.user.is_superuser: notify.send( request.user, recipient=User.objects.filter(is_superuser=1), verb='回复了你', target=article, action_object=new_comment, ) # 已有代码,二级回复 if parent_comment_id: ... # 给其他用户发送通知 if not parent_comment.user.is_superuser: notify.send( request.user, recipient=parent_comment.user, verb='回复了你', target=article, action_object=new_comment, ) # 下面都是已有代码 return HttpResponse('200 OK') new_comment.save() return redirect(article)...增加了两条notify的语句,分别位于两个if语句中: ...

May 18, 2019 · 2 min · jiezi

Django搭建个人博客用djangomptt实现多级评论功能

现在我们的博客已经具有评论功能了。随着文章的评论者越来越多,有的时候评论者之间也需要交流,甚至部分评论还能合并成一个小的整体。因此最好是有某种方法可以将相关的评论聚集到一起,这时候多级评论就非常的有用了。 多级评论意味着你需要将模型重新组织为树形结构。“树根”是一级评论,而众多“树叶”则是次级评论。本教程会以第三方库django-mptt为基础,开发多级评论功能。 django-mptt模块包含了树形数据结构以及查询、修改树形数据的众多方法。任何需要树形结构的地方,都可以用 django-mptt 来搭建。比如目录。 注意:本章新知识点较多,请读者做好心理准备,一定要耐心阅读。 重构模型既然要建立树形结构,老的评论模型肯定是要修改了。 首先安装django-mptt: (env) > pip install django-mptt安装成功后,在配置中注册: my_blog/settings.py...INSTALLED_APPS = [ ... 'mptt', ...]...这些你已经轻车熟路了。 接下来,修改评论模型: comment/models.py...# django-mpttfrom mptt.models import MPTTModel, TreeForeignKey# 替换 models.Model 为 MPTTModelclass Comment(MPTTModel): ... # 新增,mptt树形结构 parent = TreeForeignKey( 'self', on_delete=models.CASCADE, null=True, blank=True, related_name='children' ) # 新增,记录二级评论回复给谁, str reply_to = models.ForeignKey( User, null=True, blank=True, on_delete=models.CASCADE, related_name='replyers' ) # 替换 Meta 为 MPTTMeta # class Meta: # ordering = ('created',) class MPTTMeta: order_insertion_by = ['created'] ...先引入MPTT相关模块,然后改动下列几个位置: ...

May 4, 2019 · 5 min · jiezi

Nginx-配置-Https-免费证书访问

配置HTTPS现在做博客或者做网站没有 https 已经不行了,就记录一下我在腾讯云配置 https 的过程吧,非常简单,1个小时就可以了。 还涉及到 http 访问自动转发到 https 访问路径。同时把不带 www 的顶级域名转发到带 www 的二级域名上,有利于 SEO. 申请证书不管是腾讯云还是阿里云都提供免费的证书使用,不过有效期是 1 年,到时候我们重新申请就可以了。我们以腾讯云为例子,申请证书。 然后验证一下 DNS 记录就好了,如果域名是在使用腾讯云解析的话直接选用自动 DNS 验证即可。 官方说法是申请证书需要10分钟-1天的时间,我大概就是十几分钟吧,很快就收到通知了。下载的证书包含 Apache、Nginx、Tomcat、IIS等服务器的配置文件。我们把 Nginx 文件夹下的两个文件传到服务器就行了。 配置证书我们在 etc/nginx/ 目录下新建 ssl 文件夹来存放证书。把 crt 证书文件和 key 私钥文件上传到这里。然后就可以配置 Nginx 配置文件了。 我的配置文件放在 sites-enabled 文件夹里,我们删掉默认的文件新建一个,具体配置内容可以参考腾讯云的操作指导。 下面是我的配置文件 # 配置 http 访问时通过 301 转发到 https 上。server{ listen 80; server_name example.com www.example.com; return 301 https://www.example.com$request_uri;}# 证书部分内容配置,注意证书路径写对,其他地方照抄就行了server { listen 443 ssl default_server; server_name www.example.com; ssl on; ssl_certificate /etc/nginx/ssl/1_www.example.com_bundle.crt; ssl_certificate_key /etc/nginx/ssl/2_www.example.com.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; location / { proxy_pass http://127.0.0.1:8000; }}# 这一步把 顶级域名转发到 www 二级域名上,有利于 SEOserver { listen 443 ssl; server_name example.com; return 301 https://www.example.com$request_uri;}第一个server 配置的是把普通 80 端口访问的 http 协议转发到 https 访问。第二个server 配置的就是证书路径和一些参数,这个照抄就行了,只要把证书路径写对第三个server 配置的是把不带 www 的顶级域名转发到带 www 的二级域名,利于 SEO. 比如 example.com 会自动跳转到 www.example.com 。 ...

April 29, 2019 · 1 min · jiezi

Django-web框架url-path-name详解

Django web框架-----url path name详解quicktool/view.py文件修改视图函数index(),渲染一个home.html模板 from django.shortcuts import renderdef index(request): return render(request, 'home.html')quicktool(应用app)下创建一个templates文件夹(模板),在templates下创建一个home.html <!DOCTYPE html><html><head> <title>学习Django</title></head><body><a href="/add2/4/5/">计算 4+5</a></body></html>mytestsite/urls.py文件将视图函数index()对应的网址取名name为home(只要这个名字不变,网址变了也能通过名字获取到) urlpatterns = [ path('admin/', admin.site.urls), path('', quicktool_views.index, name='home'), # 新增name值 path('add/', quicktool_views.add, name='add'), # 新增 path('add2/<int:a>/<int:b>/', quicktool_views.add2, name='add2'), # 新增]运行开发服务器,访问http://127.0.0.1:8000/,可以看到 点击标签a会执行脚本进行计算,但是home.html中代码a标签的url是"写死"的,我们可以通过url的name传参,home.html增加以下代码 #不带参数的:#{% url 'name' %}#带参数的:参数可以是变量名#{% url 'name' 参数 %}<a href="/add2/4/5/">计算 4+5</a><a href="{% url 'add2' 70 80 %}">计算 70+80</a> # 新增运行开发服务器,访问http://127.0.0.1:8000/,可以看到 点击第一个a标签跳转请求的url为http://127.0.0.1:8000/add2/4/5/ 点击第二个a标签跳转请求的url为http://127.0.0.1:8000/add2/70/80/ 第二个a标签的写法是根据url name 值传参进行跳转,即根据mytestsite/urls.py文件的url path()的属性值name匹配,再传相应的参数值,进行"不写死"请求。 在url path()的属性值name不修改的前提下,修改url path()的第一个路径参数,那么a标签获取的网址也会动态相应变化。 比如 path('add2/<int:a>/<int:b>/', quicktool_views.add2, name='add2'), 改成 ...

April 28, 2019 · 1 min · jiezi

sum-函数性能堪忧列表降维有何良方

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/mK1nav2vKykZaKw_TY-rtw Python 的内置函数 sum() 可以接收两个参数,当第一个参数是二维列表,第二个参数是一维列表的时候,它可以实现列表降维的效果。 在上一篇《如何给列表降维?sum()函数的妙用》中,我们介绍了这个用法,还对 sum() 函数做了扩展的学习。 那篇文章发布后,猫哥收到了一些很有价值的反馈,不仅在知识面上获得了扩充,在思维能力上也得到了一些启发,因此,我决定再写一篇文章,继续跟大家聊聊 sum() 函数以及列表降维。若你读后有所启发,欢迎留言与我交流。 有些同学表示,没想到 sum() 函数竟然可以这么用,涨见识了!猫哥最初在交流群里看到这种用法时,也有同样的想法。整理成文章后,能得到别人的认可,我非常开心。 学到新东西,进行分享,最后令读者也有所获,这鼓舞了我——应该每日精进,并把所学分享出去。 也有的同学早已知道 sum() 的这个用法,还指出它的性能并不好,不建议使用。这是我不曾考虑到的问题,但又不得不认真对待。 是的,sum() 函数做列表降维有奇效,但它性能堪忧,并不是最好的选择。 因此,本文想继续探讨的话题是:(1)sum() 函数的性能到底差多少,为什么会差?(2)既然 sum() 不是最好的列表降维方法,那是否有什么替代方案呢? 在 stackoverflow 网站上,有人问了个“How to make a flat list out of list of lists”问题,正是我们在上篇文章中提出的问题。在回答中,有人分析了 7 种方法的时间性能。 先看看测试代码: import functoolsimport itertoolsimport numpyimport operatorimport perfplotdef forfor(a): return [item for sublist in a for item in sublist]def sum_brackets(a): return sum(a, [])def functools_reduce(a): return functools.reduce(operator.concat, a)def functools_reduce_iconcat(a): return functools.reduce(operator.iconcat, a, [])def itertools_chain(a): return list(itertools.chain.from_iterable(a))def numpy_flat(a): return list(numpy.array(a).flat)def numpy_concatenate(a): return list(numpy.concatenate(a))perfplot.show( setup=lambda n: [list(range(10))] * n, kernels=[ forfor, sum_brackets, functools_reduce, functools_reduce_iconcat, itertools_chain, numpy_flat, numpy_concatenate ], n_range=[2**k for k in range(16)], logx=True, logy=True, xlabel='num lists' )代码囊括了最具代表性的 7 种解法,使用了 perfplot (注:这是该测试者本人开发的库)作可视化,结果很直观地展示出,随着数据量的增加,这几种方法的效率变化。 ...

April 27, 2019 · 2 min · jiezi

我是如何自学-Python-的

不少初学 Python 或者准备学习 Python 的小伙伴问我如何学习 Python。今天就说说我当时是怎么学习的。 缘起 我大学专业是电气工程,毕业后做的是自动化方面的工作。对于高级语言编程基本是 0 基础,那时刚毕业在车间做设备调试,工资也只有三四千块钱。2014年底在知乎看到搞 IT 的薪资动辄 10k 起步,所以我也动了学习编程的念头。 当时 Python 已经开始流行。虽然远没有今天热度这么高,但是已经有一些大V在鼓励大家开始学习 Python了。对我影响最大的是知乎ID为:"萧井陌"的大神。我觉得他至少影响了上万人学习 Python 。那时候他的《编程入门指南》很火,而且一直在鼓励初学编程的人去学习 Python。其中他的这个回答对我影响最大,因为这个回答特别笃定,把步骤写好了,照做就是了。 然后我买了他推荐的这本书,现在已经出第二版了。当时看第二遍时还是糊里糊涂的,因为你学了 Python 基础后,还要了解 WEB 开发的一些概念,包括数据库的基本用法。所以当时又看了 WEB 方面包括 HTML/CSS/JS,和 HTTP协议一些知识。买了本 SQL 必知必会来了解简单的 SQL 语句。总之是 Flask 这本书看了三遍,对书中所写的项目理解了80%左右吧。到这里基本算是入门吧,之后就开始做 IT 相关工作了。 学习方法如果是 0 基础学习,还是推荐《笨办法学Python》这本小册子开始。很直白,没有上来就讲语法,仅仅是照着敲就行了。这个小册子看完后我当时看的是《Python核心编程-第二版》上面讲的还是 Python 2.5。现在出了第三版,但是已经不推荐初学者去看了。现在你可以直接去看人民邮电出版社的《Python编程从入门到实践》,这本书我简单翻过,内容还是很不错的,包括大量的实际案例,可以亲手做出一点好玩的应用来。 除了 Python 外还要了解基本的 HTML/CSS/JS。这些东西花几天时间在 W3School 看一看就差不多了。在这个过程中可以到网上看看别人都用 Python 来做哪些好玩的事情,可以跟着学学。知乎上有很多好的问题和答案,非常值得学习。 在学习过程中不必要求 100% 掌握,一些高级用法不理解没关系,等代码写的多了就懂了。上面基础知识看完后就要选择一个方向了,比如 WEB,数据分析等。做 WEB 的话 Python 最流行的两个框架 Django 和 Flask 选一个深入学一下就好了,我当时学的是 Flask,不过 Django 是一个大而全的框架,不需要你去找各种第三方模块来使用,文档也很全面,都很适合来学习。 ...

April 24, 2019 · 1 min · jiezi

django-query模块

为什么会接触这个模块?最近在接触一个Django项目,使用的是fbv(function-base views)模式,看起来特别不舒服,项目中有一个模型类117个字段,看我的有点晕,不过还是得干呀,生活呀,头发呀。 个人觉得这个模块用处不是很大,并且写的也不是很好。看了看源码如此简单,就给大家分享一下,也好久没有给大家分享东西了。 import jsondef get_default_valcastfunc(val_cast_type=None): """通过一下方法传入的字符串判断使用哪种数据返回""" if val_cast_type == 'int': return 0, int elif val_cast_type == 'listjson': return '[]', json.loads elif val_cast_type == 'dictjson': return '{}', json.loads return None, Nonedef get_query_value(request, key, default=None, val_cast_func=None, val_cast_type=None): """ 通过val_cast_type获取到优化数据的函数,或者通过val_cast_func函数传入val """ if val_cast_type in ['int', 'listjson', 'dictjson']: default, val_cast_func = get_default_valcastfunc(val_cast_type) value = request.POST.get(key) or request.GET.get(key) or default return val_cast_func(value) if val_cast_func else (value or '')自认为的几个重点request.POST.get这样取值即使key不存在也不会报错,而是返回None,而request.POST[key]这样会报错KeyError ,GET同POSTDjango的模型类使用get若无值,则会报错。在返回的时候使用参数val_cast_func,而val_cast_func并未传入,而是通过get_default_valcastfunc返回设置

April 22, 2019 · 1 min · jiezi

len(x) 击败 x.len(),从内置函数看 Python 的设计思想

内置函数是 Python 的一大特色,用极简的语法实现很多常用的操作。 它们预先定义在内置命名空间中,开箱即用,所见即所得。Python 被公认是一种新手友好型的语言,这种说法能够成立,内置函数在其中起到了极关键的作用。 举个例子,求字符串 x 的长度,Python 的写法是 len(x) ,而且这种写法对列表、元组和字典等对象也同样适用,只需要传入对应的参数即可。len() 函数是共用的。 这是一种极简哲学的体现:Simple is better than complex。 但是,有些语言并不是这样,例如在 Java 中,字符串类有一个求长度的方法,其它类也有自己的求长度的方法,它们无法共用。每次使用时,通过类或实例来调用。 同样是求字符串长度,Python 的写法: saying = "Hello world!"print(len(saying))# 结果:12而在 Java 中,写法可能如下(简化起见): String saying = "Hello world!";System.out.println(saying.length());// 结果:12Python 采用的是一种前缀表达式 ,而 Java 采用的则是后缀表达式 。 除了求长度,Python 的某些内置函数也能在 Java 中找到对应的表达。例如,数值型字符串 s 转化为整型数字,Python 可以用 int(s) 函数,而 Java 可以用 Integer.parseInt(s) ;整型数字转化为字符串,Python 可以用 str(i) ,而 Java 也有 String.valueOf(i) 。 Python 的内置函数不与特定的类绑定,它们是一级对象。而 Java 的“函数”则无法脱离类而存在,它们只是附属品。 从直观角度来看,Python 的表达似乎是更优的。但是,它们并不具有可比性 ,因为这是两套语言系统,各有独特的范畴背景,并不能轻易地化约。 ...

April 21, 2019 · 2 min · jiezi

Django2.2图文教程

原文链接1.准备工作开发环境: python: 3.7.3 下载地址:https://www.python.org/downlo… pip: pip3 Django: 2.2 下载地址: https://www.djangoproject.com… PyCharm: 2017.2 下载地址:https://www.jetbrains.com/pyc…2.基础知识1.Python: Python发源于八十年代后期。开发者是Centrum Wiskunde & Informatica的Guido van Rossum,这是位于荷兰阿姆斯特丹科学园区的一个数学和计算机科学研究中心。之后Van Rossum一直是Python开发很有影响的人物。事实上,社区成员给了他一个荣誉称号:终生仁慈独裁者(BDFL)。 2.web框架介绍web框架的概念就像建房子,地基、支撑柱、大梁的骨架还有其他沙石、地板等等材料已经有了,剩下的就是靠你自己组装起来。犹如积木一样。说明:图片来源百度。 Django: Python代表性web框架,遵循MMVC架构模式的开源框架。它的名字来自Django Reinhardt,一个法国作曲家和吉他演奏家,很多人认为他是历史上最伟大的吉他演奏家。位于堪萨斯洲的Lawrence城的Lawrence Journal-World报社有两位程序员,Adrian Holovaty和Simon Willison,他们在2003的时候开发出了Django,用于给报纸开发web程序。其他框架:tornado、flask、webpy3.实例Python的和Pycharm的安装忽略,安装比较简单。1.安装Mac终端下:$ pip3 install django # 或 pip3 install django==2.2Windows是在cmd里面,命令相同。2.创建项目打开Pycharm,欢迎页create project,然后 然后点击create就可以了。 PS:创建项目也可以使用命令这里不做介绍.django-admin createproject DjangoQuickdjango-admin startapp quick3.项目结构4.启动项目Pycharm菜单栏 直接点击运行。或者左下角Terminal输入python3 manage.py runserver 将会在浏览器看到 这样表示项目已经启动成功,但是我们没有编写任何代码,所以出现了默认的错误提示。5.添加视图打开quick目录下的views.py,输入一下内容from django.shortcuts import renderfrom django.http import HttpResponse # 新添加# Create your views here.# 新添加def index(request): return HttpResponse(‘success’)6.添加url打开DjangoQuick下的urls.py,修改如下:from django.contrib import adminfrom django.urls import pathfrom quick.views import index # 导入包urlpatterns = [ path(‘admin/’, admin.site.urls), path(’’, index) # 添加新的映射关系]如果使用Pycharm的按钮启动项目,需要点击stop,重新运行,如果在terminal中运行项目,会自动重启。 然后刷新浏览器。 然后就会看到我们在views.py的index方法中的输出。7.返回Html(模版)在quick目录新建templates目录 然后右键-new-html file, 新建一个index.html文件 index.html<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>Title</title></head><body>这是一个html from:https://litets.com</body></html>修改views.py文件from django.shortcuts import renderdef index(request): return render(request, ‘index.html’)重启服务器,刷新浏览器将会看到这是一个html from:https://litets.com8.静态文件上面我们已经成功输出了一段html代码,并且成功展示。大家都知html的美化需要依靠css, css写法有两种:内联式和外联式。内联就不用多说,直接写在html同文件里面就可以了,现在我们使用外联文件改怎么办呢?在quick目录下新建static/css/style.css文件。.desc { color: red;}然后修改index.html<!DOCTYPE html>{% load static %}<html lang=“en”><head> <meta charset=“UTF-8”> <title>Title</title> <link rel=“stylesheet” href="{% static ‘css/style.css’ %}"></head><body> <div class=“desc”> 这是一个html from:https://litets.com </div></body></html>刷新浏览器,将会看到字变成红色了。9.接收用户数据,处理后返回修改index.html文件<div class=“desc”> 这是一个html from:https://litets.com</div><!–一下是新添加–><form action="/" method=“post”> {% csrf_token %} 用户名: <input type=“text” name=“username” placeholder=“请输入用户名”> <br> 密码: <input type=“password” name=“password” placeholder=“请输入密码”> <br> <input type=“submit” value=“提交”></form><!–users来自view中的context–>{% if users %} <table border=“1px”> <tr> <td>序号</td> <td>用户名</td> <td>密码</td> </tr> {% for user in users %} <tr> <td>{{ forloop.counter }}</td> <td>{{ user.username }}</td> <td>{{ user.password }}</td> </tr> {% endfor %} </table>{% endif %}views.py修改from django.shortcuts import renderdef index(request): # 判断是否是post请求 if request.method == ‘POST’: # 获取到请求参数, username的写法,如果username不存在不会抛异常 # password 会抛异常 username = request.POST.get(‘username’) password = request.POST[‘password’] # 业务 需求: users = [] for x in range(0,3): users.append( {‘username’: ‘%s-%d’ % (username, x), ‘password’: ‘%s-%d’ % (password, x)} ) # 返回给用户 模版中使用到的users就是这里传递进去的 return render(request, template_name=‘index.html’, context={ ‘users’: users }) return render(request, ‘index.html’)这样我们就实现了一个基本的网站了,但是有一个问题,用户传递过来的数据只是单次有效,无法持久化。 通常一个网站应用到需要持久化数据,比如文件、数据库等。10.数据库持久化数据我们打开DjangoQuick目录下的setting.py找到DATEBASES 默认项目使用sqlite3最为数据库。还支持 MySQL, PostgreSQL等可用的引擎有:‘django.db.backends.sqlite3’‘django.db.backends.postgresql’‘django.db.backends.mysql’‘django.db.backends.oracle’Mysql的设置:DATABASES = { ‘default’: { ‘ENGINE’: ‘django.db.backends.mysql’, ‘NAME’: ’test’, # 数据库名称, ‘HOST’: ’localhost’, # 主机地址 ‘USER’: ‘user’, # 数据库用户 ‘PASSWORD’: ‘pwd’, # 密码 ‘PORT’: 3306 # mysql的端口默认3306 }}这里我们使用默认的sqlite3。 打开quick下的models.pyfrom django.db import modelsclass User(models.Model): username = models.CharField(max_length=25) password = models.CharField(max_length=18)我们增加了两个字段username和password,Django默认支持orm。 同步数据库,在终端中执行$ python3 manage.py makemigrations此命令执行完成后,会在migrations下生产记录models变动的记录。 此时数据库并未变动,然后执行$ python3 manage.py migrate这样models的变动,立即同步到数据库中。 修改views.pyfrom django.shortcuts import renderfrom .models import Userdef index(request): # 判断是否是post请求 if request.method == ‘POST’: # 获取到请求参数, username的写法,如果username不存在不会抛异常 # password 会抛异常 username = request.POST.get(‘username’) password = request.POST[‘password’] u = User(username=username, password=password) u.save() # 业务 需求:查询出所有数据 users = User.objects.all() # 返回给用户 return render(request, template_name=‘index.html’, context={ ‘users’: users })这样添加用户后就会持久报错了,即使关闭浏览器,重新访问数据也是存在的。我们查看db.sqlite3文件查看里面的数据 原文链接 ...

April 18, 2019 · 2 min · jiezi

Python3基础知识

Python3基础知识 | 基础语法 Python3基础知识 | 编程第一步 Python3基础知识 | 基本数据类型Python3基础知识 | 解释器 Python3基础知识 | 注释 Python3基础知识 | 运算符Python3基础知识 | 数字 Python3基础知识 | 字符串 Python3基础知识 | 列表Python3基础知识 | 元组 Python3基础知识 | 字典 Python3基础知识 | 条件控制Python3基础知识 | 循环 Python3基础知识 | 迭代器与生成器 Python3基础知识 | 函数Python3基础知识 | 数据结构 Python3基础知识 | 模块 Python3基础知识 | 输入和输出Python3基础知识 | File 方法 Python3基础知识 | OS 文件目录方法 Python3基础知识 | 错误和异常Python3基础知识 | 标准库概览 Python3基础知识 | 日期和时间 Python3基础知识 | 正则表达式Python3基础知识 | XML解析 Python3基础知识 | JSON 数据解析 Python3基础知识 | MySQL 数据库连接Python3基础知识 | 多线程 Python3基础知识 | 面向对象 ...

April 16, 2019 · 1 min · jiezi

视频当道的时代,这些珍藏的优质 Python 播客值得推荐

我国互联网的发展道路与欧美不同,在内容的形式上,我们似乎实现了跨越式的发展——早早进入了移动互联网时代,直播和短视频等形式的内容成为了潮流,而文字形式的博客(blog)与声音形式的播客(podcast)则(逐渐)成为了小众。智能手机极大地改变了我们的上网习惯。诚然,仍有一些受众广泛的聚合类的平台,例如微信公众号、CSDN、掘金、极客时间、喜马拉雅、荔枝FM,为我们提供丰富的博客与播客,但是,不依赖平台的个人博客与个人播客,则鲜有人知。依我的使用习惯,我很喜欢听音频节目,也即是播客。中文的播客听了不少,但是,免费的 Python 播客是极其稀少。直到发现了 Full Stack Python 网站上的一篇文章,它汇总介绍了一些非常棒的 Python 播客,大部分节目仍在持续更新中。我特翻译出来,分享给大家。英文节目对大多数人来说,可能门槛较高,但是英文是程序员的必修功课 ,聆听英文节目,正好可以一边学技术,一边练习英语,一举两得。英文 | Best Python Podcasts[0]译者 | 豌豆花下猫Python 社区里有很多免费或低成本的学习资源,对新手与有经验的开发者来说,是一大福音。这些优秀的资源就包括很多定期更新的 Python 播客节目。本文介绍了一些活跃的、与 Python 或软件工程相关的、高质量的播客。Python 相关的播客这些播客的运营者都是 Python 开发者,他们关注的都是我们领域内很重要的话题。每个播客系列都有很长的历史列表,有的节目录于几年前,因此我们有很丰富的材料可以聆听与学习。Talk Python to Me[1] 专注于 Python 开发者和组织,每期节目会邀请不同的嘉宾来谈论 ta 的工作Podcast.init[2] 提供有关 Python 的故事,以及“与那些让它变得更棒的人们的访谈”Python Bytes[3] 是来自“Talk Python to Me”和“Test and Code Podcast”创作者的新播客Test and Code Podcast[4] 侧重于测试与相关主题,如模拟(mock)和代码度量Philip Guo 教授有一个名为 PG Podcast[5] 的视频播客,基本是关于 Python 主题的Import This[6] 是 Kenneth Reitz 和 Alex Gaynor 间歇更新的播客,对有影响力的 Python 社区成员进行深度的采访最喜欢的播客节目以下是我从各大播客中收集的最喜欢的一些节目,听听这些内容,你可以感受到其余播客节目的风格。SQLAlchemy and data access in Python[7] 让我理解了对象关系映射库 SQLAlchemy 的知识及其演变过程。这期节目采访了 SQLAlchemy 的作者,主持人 Michael Kennedy 根据他对 SQLAlchemy 的深入研究和使用经验提出了很多问题。Python past, present, and future with Guido van Rossum[8] 涵盖了 Python 的历史、Guido 创造并持续三十年来发展这门语言的动机。有趣的事实:当播客主持人迈克尔·肯尼迪向我征询话题时,我贡献了一个问题,即 Python 的开源是否是促使它成功的原因?Deploying Python Web Applications[9] 剧透预警:这是我在 Talk Python to Me 上的一期节目,介绍了 Python Web 应用程序部署的工作原理。Python Bytes 栏目在第 39 集中广泛地讨论了 object-relational mappers (ORMs)[10] ,其中不少讨论是基于 Full Stack Python 上的文章。谢谢大家对我们提出的反馈与建议。Python at Netflix[11] 出自 Talk Python to Me ,通过一个非常棒的视角,介绍了 Python 是怎么运用于这家最大的网络流媒体公司,以及如何适应它们的多语言组织。另一个很棒的 Talk Python to Me 节目, Python in Finance[12],介绍了 Python 在金融行业中的广泛用途:股票交易、定量分析和数据分析。如果你想知道像对冲基金这样的不透明的私营企业是如何利用 Python 赚取(大量)钱财的,一定要听听这个。通用软件开发的播客这些播客主要探讨的是软件开发相关的主题,但经常也会涉及 Python 的内容。聆听和学习这些播客,你将会成为更加优秀的软件开发者。Software Engineering Daily[13] 令人难以置信的是每天邀请不同的开发者嘉宾,谈论话题非常广泛,与开发相关。All things Git[14] 教人如何使用、构建及将 Git 用于工作,每两周一更。CodeNewbie[15] 采访新入行的开发者,谈论为什么他们要从事编程工作,以及他们的工作内容。该栏目也会采访一些经验丰富的、打造了知名项目的开发者。Developer on Fire[16] 采访程序员、架构师和测试人员,讲述他们成功、失败和卓越的故事。Command_line Heroes[17] 涵盖操作系统级的主题以及 DevOps。Embedded.fm[18] 涵盖嵌入式系统和硬件黑客攻击。The Changelog[19] 周更播客,关于常规软件开发的问题。Full Stack Radio[20] 虽与 Full Stack Python 无关,但值得关注!Exponent[21] 不是一个软件开发的播客,但它以深入的方式揭示了企业的战略和技术,使我能够更好地理解企业在构建和发布软件时所做出的决策。我听了每一集(以 1.5 倍速),非常推荐每周花 45 到 60 分钟,听 Ben Thompson 和 James Allworth 深入讨论一个主题。Test Talks[22] 每周考察一个软件测试的主题,通常会特邀一位钻研该领域的嘉宾。The Cloudcast[23] 聚焦于云计算和 DevOps 的相关主题。数据科学与数据分析的播客Python 不仅是数据科学社区的核心编程语言,而且几乎在每个使用数据分析的组织中都发挥着重要作用。 以下播客广泛地涵盖数据科学,并经常涉及到 Python 生态系统中的特定的工具。DataFramed[24] 是一个数据科学播客,内容涵盖 Python 标准库,以及数据分析者感兴趣的其它内容。Data Skeptic[25] 涵盖数据科学、统计、机器学习、人工智能,以及“科学怀疑论”(scientific skepticism)等内容。Data stories[26] 是一个关于数据可视化的播客。Partially Derivative[27] 是一个关于机器学习、人工智能和数据行业的播客,在 2017 年底已停播,节目列表包含了大量的内容。References[0] Best Python Podcasts: https://www.fullstackpython.c…[1] Talk Python to Me: https://talkpython.fm/[2] Podcast.init: http://podcastinit.com/[3] Python Bytes: https://pythonbytes.fm/[4] Test and Code Podcast: http://pythontesting.net/test…[5] PG Podcast: http://pgbovine.net/PG-Podcas…[6] Import This: https://www.kennethreitz.org/…[7] SQLAlchemy and data access in Python: https://talkpython.fm/episode…[8] Python past, present, and future with Guido van Rossum: https://talkpython.fm/episode…[9] Deploying Python Web Applications: https://talkpython.fm/episode…[10] object-relational mappers (ORMs): https://www.fullstackpython.c…[11] Python at Netflix: https://talkpython.fm/episode…[12] Python in Finance: https://talkpython.fm/episode…[13] Software Engineering Daily: https://softwareengineeringda…[14] All things Git: https://www.allthingsgit.com/[15] CodeNewbie: https://www.codenewbie.org/po…[16] Developer on Fire: http://developeronfire.com/[17] Command_line Heroes: https://www.redhat.com/en/com…[18] Embedded.fm: http://embedded.fm/[19] The Changelog: https://changelog.com/[20] Full Stack Radio: http://www.fullstackradio.com/[21] Exponent: http://exponent.fm/[22] Test Talks: https://joecolantonio.com/tes…[23] The Cloudcast: http://www.thecloudcast.net/[24] DataFramed: https://www.datacamp.com/comm…[25] Data Skeptic: https://www.dataskeptic.com/[26] Data stories: http://datastori.es/[27] Partially Derivative: http://partiallyderivative.com/公众号【Python猫】, 专注Python技术、数据科学和深度学习,力图创造一个有趣又有用的学习分享平台。本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、优质英文推荐与翻译等等,欢迎关注哦。PS:后台回复“爱学习”,免费获得一份学习大礼包。 ...

April 12, 2019 · 2 min · jiezi

Django搭建个人博客:回到顶部浮动按钮、矢量图标、页脚沉底和粘性侧边栏

本章集中介绍四个重要的小功能:回到顶部浮动按钮、矢量图标、页脚沉底和粘性侧边栏。这几个功能与Django基本没啥关系,更多的是前端知识,但是对博客网站都很重要,问的读者也比较多,因此也集中讲一下好了。回到顶部浮动按钮当用户拜读完你的博文后,可能想回到文章开头重新阅读,或者审视其中的某些内容。如果文章内容较多,不停滑动滚轮回页面顶部未免有点太让人烦躁了。一种解决办法是增加一个回到顶部的浮动按钮。当页面向下滚动到某个位置后,按钮就呈现在页面右下角;点击按钮,页面就回到顶部。这个功能 Bootstrap 4 似乎没有提供,但也不复杂,就自己用 JavaScript 和 CSS 写吧。在templates目录新建back_to_top_func.html文件,写入以下代码:templates/back_to_top_func.html<!– 参考了 鹦鹉 的代码,在此致谢 –><!– https://ezbox.idv.tw/131/back-to-top-button-without-images –><button type=“button” id=“BackTop” class=“toTop-arrow” style=“z-index: 100;"></button><script> // 向上滚动的函数 $(function () { $(’#BackTop’).click(function () { $(‘html,body’).animate({scrollTop: 0}, 500); }); $(window).scroll(function () { if ($(this).scrollTop() > 300) { $(’#BackTop’).fadeIn(300); } else { $(’#BackTop’).stop().fadeOut(300); } }).scroll(); });</script><style> /* 按钮边框的大小、位置、样式 / .toTop-arrow { width: 3.5rem; height: 3.5rem; padding: 0; margin: 0; border: 0; border-radius: 33%; opacity: 0.7; background: black; cursor: pointer; position: fixed; right: 1.5rem; bottom: 1.5rem; display: none; } / 绘制按钮中的向上箭头 / .toTop-arrow::before, .toTop-arrow::after { width: 31px; height: 7px; border-radius: 3px; background: orange; position: absolute; content: “”; } .toTop-arrow::before { transform: rotate(-45deg) translate(0, -50%); left: 0.4rem; } .toTop-arrow::after { transform: rotate(45deg) translate(0, -50%); right: 0.4rem; } / 取消点击按钮时的聚焦 */ .toTop-arrow:focus { outline: none; }</style>代码分成html、javascript、css三部分。HTML部分只有一行,用button标签表示了浮动按钮的容器。JavaScript部分主要用到了Jquery的语法。页面加载完成后开始监听两个事件:当用户点击浮动按钮时,将页面滚动到顶部当页面滚动时,根据页面距离顶部的距离,决定按钮显示或隐藏CSS部分最长但也很简单,主要定义了按钮的位置、大小、图案等样式。读者可以试着、改动、删除部分代码,看看按钮形态会怎样变化。核心代码就写好了。有点小瑕疵的是前面在footer.html中定义了class=“fixed-bottom”,这个属性的显示层级很高,会将浮动按钮给覆盖掉。因此删除templates/footer.html中的fixed-bottom属性:templates/footer.html…<!– 删除了 fixed-bottom 属性 –><footer class=“py-3 bg-dark”> …</footer>z-index这个css样式决定了页面中容器的显示顺序,数值越大则显示优先级越高。之所以fixed-bottom会覆盖掉浮动按钮,就是因为它将z-index设置成了一个很大的数值。因为我们想在全站都拥有这个按钮,所以将刚写好的模块引用到base.html中:templates/base.html…<body> … <!– jquery.js –> <script src=”{% static ‘jquery/jquery-3.3.1.js’ %}"></script> … <!– 在jquery后面引入 –> {% include ‘back_to_top_func.html’ %}</body>…注意模块用到了Jquery,因此要在Jquery后面引入。效果如下:点击按钮后,页面滚回到顶部。矢量图标与老版本不同,Bootstrap 4 中也没有自带图标。作为补偿,官方也推荐了几款强大且免费的第三方矢量图标提供商。我比较喜欢的是Font Awesome,提供1500+免费图标(以及5000+付费图标),完全够用了。各种你想得到想不到的图标都有:用法也很简单,你甚至不用将其下载到本地(当然想下载也可以)。根据官网的提示,直接在base.html中引入:templates/base.html…<link rel=“stylesheet” href=“https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity=“sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf” crossorigin=“anonymous”>…然后在页面中插入某个图标的标签就可以用了。首先在官网图标库搜索想要的图标,比如eye:点击图标进去后就能看到它的标签名称:将此标签名称复制到你的网页中,图标就渲染出来了。很神奇的是,矢量图标跟普通的字体是完全类似的,你可以通过CSS定义图标的颜色(color)、大小(font-size)等样式。尝试一下。将图标代码添加到templates/article/list.html中的列表循环:templates/article/list.html…<!– 注脚 –><p> <!– 附加信息,增加了图标 –> <span> <i class=“fas fa-eye” style=“color: lightskyblue;"></i> {{ article.total_views }}&nbsp;&nbsp;&nbsp; </span> <span> <i class=“fas fa-comments” style=“color: yellowgreen;"></i> <!– 修改为评论的计数 –> {{ article.comments.count }}&nbsp;&nbsp;&nbsp; </span> <span> <i class=“fas fa-clock” style=“color: pink;"></i> {{ article.created|date:‘Y-m-d’ }} </span></p>…看看效果:好玩吧。读者朋友慢慢挑选心仪的图标,到自己的博客中吧。相比写代码来说,这是个相当愉悦的过程。页脚沉底刚才做浮动按钮时,取消了页脚固定在底部的fixed-bottom。按钮倒是没被遮盖了,但又冒出来另一个烦人的问题,请看下图:当页面内容较少时,页脚下方居然空出来一大块地方,太丑了。《Sticky Footer, Five Ways》罗列了5种方法解决这个问题,有兴趣的同学可深入了解。需要修改base.html和footer.html两个文件。先贴改动代码:templates/base.html…<body> {% include ‘header.html’ %} <!– 新增两个 div 容器 –> <div id=“wrapper”> {% block content %}{% endblock content %} <div id=“push”></div> </div> {% include ‘footer.html’ %} … <!– 增加样式 –> <style> html, body { height: 100%; margin: 0; } #wrapper { min-height: 100%; margin-bottom: -60px; } #footer, #push { height: 60px; } </style> </body>…templates/footer.html…<!– 增加 id=“footer” 属性 –><footer … id=“footer”> …</footer>代码通过CSS样式控制页面尺寸不小于屏幕的高度,以及页脚的高度为60px。不太好理解的主要有两个地方:#push容器留出一段与页脚等高的空隙,避免正文内容与页脚重叠。#wrapper容器的底部有一个负边距,作用是给页脚容器让出位置。这个负边距你不设置也可以,无非就是底部多出高度为60px的空白罢了。刷新页面:舒服了。随着项目逐渐增大,HTML、JavaScript、CSS交织在一起,也更加混乱。虽然教程没有把这三种类型的代码分离开,但是你应该考虑这样做。粘性侧边栏目前教程将文章目录放置在文章的右侧,这就是相当于是个侧边栏。问题是当用户向下阅读文章时,目录却不会固定在页面中,而是几下就翻得没影了,影响体验。粘性侧边栏就是来解决这个问题的。当页面向下滚动时,粘性侧边栏会灵活的固定在屏幕中,保证用户在任何位置都可以看到侧边栏中的内容。具体工作模式如下图:考虑到侧边栏有可能会很长,因此设计出足够“聪明”的粘性侧边栏也不那么容易。教程将用到Abouolia的粘性侧边栏插件,强大且小巧,读者可以去官方示例感受一下。将插件的GitHub库下载到本地后,因为博客项目已经加载好了Jquery,所以只需要用到dist目录下的jquery.sticky-sidebar.min.js这个文件就可以了。在项目的static目录下新建目录sticky_sidebar,将其粘贴进去:/static/sticky_sidebar/jquery.sticky-sidebar.min.js因为只需要在文章详情页面用到,所以在详情页中引入模块就够用了:templates/article/detail.html…<!– 目录 –><div … id=“sidebar” class=“sidebar”> <div class=“sidebar__inner”> <h4><strong>目录</strong></h4> <hr> <div> {{ toc|safe }} </div> </div></div>…<!– 粘性侧边栏样式 –><style> .sidebar{ will-change: min-height; } .sidebar__inner{ transform: translate(0, 0); transform: translate3d(0, 0, 0); will-change: position, transform; }</style>…{% block script %}<!– 引入粘性侧边栏 –><script src=”{% static ‘sticky_sidebar/jquery.sticky-sidebar.min.js’ %}"></script><script type=“text/javascript”> $(’#sidebar’).stickySidebar({ topSpacing: 20, bottomSpacing: 20, });</script>…{% endblock script %}按照插件的要求,侧边栏套上了两层容器,第一层含有属性id=“sidebar” class=“sidebar”,第二层含有属性class=“sidebar__inner”。然后设置样式,引入静态文件并调用插件,没什么好说的,照做就可以了。与前面的章节相同,由于插件需求Jquery,一定要把 JavaScript 语句放到{% block script %}中,否则会报错哦。插件还有其他可设置的规则,详情见官方文档刷新页面,不管你怎么滚动页面,目录都显示在屏幕中,并且随着滚轮很自然的上下移动了:总结本章学习了回到顶部浮动按钮、矢量图标、页脚沉底和粘性侧边栏四个功能。就像前面说的,这几个功能跟Django没什么关系,但是既然要想做一个完整的博客网站,就不要抱有幻想。光靠那么一点点Django代码是不可能的,什么知识你都得会一点才行。读者以后会遇到更加多样的编程工具,一定不要被“Django程序员”这个头衔所束缚,勇敢去学吧。谁让你已经上了贼船呢。有疑问请在杜赛的个人网站留言,我会尽快回复。或Email私信我:dusaiphoto@foxmail.com项目完整代码:Django_blog_tutorial ...

April 11, 2019 · 2 min · jiezi

Django后台 + Wordpress主题快速搭建个人博客

既然学习了Python Web怎么能没有自己的一个小站呢?没有自己精心打造的一个小站怎么敢说自己学习过 Python Web呢?说的再多不如直接干,我的个人网站也已经部署上线。Django后台 + Wordpress主题,只要自己看上的主题都可以让它变成自己的为什么要选择 Wordpress 主题呢?自己在刚开始学习Python Web时最大的困惑就是:Django后台开发没什么难度,但是想搭起自己的一个站点却难的让我无从下手,什么 HTML、CSS、Jquery、JS、AJAX的前端知识太多,听起来就头大,即使学会了前端技术,你能写出一个自己满意的前端页面吗?没有一点审美和设计能力,好像并不大行。当我遇到 Wordpress 时,这一切都变得如此简单,Wordpress社区有丰富的主题,可以挑出自己喜欢的随意摆弄,只需能看懂前端代码即可,加上 Django 类似API式的视图和前端模板语法,简直完美结合。当初学习 Django 一心想搭起一个自己满意的小站,但始终未完成心愿,了解到 Wordpress 后让我打开了新思路,我的个人网站,是 Django1.11 + 崔庆才个人博客 Wordpress 欲思主题搭建:https://www.stormsha.com/初学Python web时对前端不熟练是其实是最难的点,为了让更多的人少走弯路,自己写了一下个人博客搭建的教程,这个教程我希望靠每个浏览过的人都能给出更多建议把它完善,所以希望更多的人看到,提出意见,帮助更多的人

April 1, 2019 · 1 min · jiezi

别开心太早,Python 官方文档的翻译差远了

近几天,很多公众号发布了 Python 官方文档的消息。然而,一个特别奇怪的现象就发生了,让人啼笑皆非。Python 文档的中文翻译工作一直是“默默无闻”,几个月前,我还吐槽过这件事《再聊聊Python中文社区的翻译》,当时我们的进度是 10.3%,远远落后于日本和法国,甚至落后于巴西!这次所谓的中文版,当然是未完成翻译的残品。刚查了下,整体进度是 19.7%。有的公众号在发布消息的时候,说明了这不是官宣、不是正式发布版,还指出了中文版的访问地址是隐藏入口。这都是忠于事实的。然而,怪异的事情就在于,还有一些公众号在发布时,不知怎么误传,这个消息变成了官方正式发布、全部翻译完成、激动人心期盼已久,至于这个隐藏入口跳转问题、下载的文档为何是英文版的问题,则完全无法解释。这带来了极大的误导。由于曾搜集过 PEP 文档的翻译,我无意中也了解到关于翻译官方文档的一些情况。有以下几个现状吧:1、人员分散,缺乏核心。就我所见,在V站、华蟒邮件组、简书、知乎,分别有不同的人发起过翻译召集或者咨询,然而应者无几,并没有形成过足够大的核心组织。2、官方的翻译?Python 官方在 2017 年的 PEP-545 中推出了一种翻译模式,各国语言的翻译在协作平台Transifex 上进行。实际上,这才是官方认可的版本,也是最终发布的依据。前文说的进度,就是指在这个平台上的进度。3、野生的翻译?所谓野生,这里指的是不在Transifex 上的翻译。网上能看到有人零星地翻译了一些部分,但成果没有合入到官方平台上。社区内的译者还是挺多的,能力也有,只是太分散了。邮件组里就有位大佬,他说翻译过 40 多个标准库以及 C 模块的文档,但懒得组织。有人尝试组织过,时间久远的不说,就在去年夏天,某位在 PHP 界知名的站长开了个 Python 社区,召集了一批译者。他们译出了 Python 3.7 官方文档的入门教程部分,然而,后续内容的计划,似乎被放弃了。关于对待翻译的态度,似乎多数人表示:感兴趣,但是时间少,希望有人牵头组织,可以参与作贡献。我本人也怀着同样的想法。作为参与者、见证者、沾光者就好了,谁愿意花费那么多精力,承担重任,周旋策划,最后可能还讨不到好呢?写文章是重口难调,翻译文档更是如此,碰上质疑翻译水平的,还可商榷一下,而遇到下面这种杠精,只能是破坏心情。前面提到的那位站长,提出在他的社区维护一份长久维护的版本。事实上,他们真的做出了点实事,除了入门教程,还完成了两本经典书籍的翻译。然而,他们也招到了非议:不当的“官方文档”措辞、不合入官方使用的平台、网站的商业化运营……空谈的人总是有他们的理,不对事情做贡献,还无视别人的贡献。诚然,宣称“官方”中文文档,确实不妥,这只是个人/社区的行为,改正就好了;至于合入官方的途径,只需有翻译成果,也不难做到;最后,一个站点接些贴片广告,哪有什么不妥?我所了解到的社区翻译情况,大致如上。总体上,分裂分散现象严重,随性自由之处跟 Python 这语言倒挺像,而各怀能力各出成绩的现象,也跟为数众多的三方模块神似。也有默默在做事的人。从 4 个月前的 10% ,增长到现在的 20%,我们的翻译进度暴涨,这背后不知有几人在持续作出贡献?而他们还不为人知。距离官方文档全部译出,还有大步路要走,现实情况得认清。我总体上是乐观的。所以,最后聊个题外话。这几天,有个热得不行的话题——996.ICU ,才仅仅一周,Github star 数已经破 10 万,绝对创造纪录了。程序员发起的活动,就是有如此大的力量。就在本文写作过程中,Python 之父也给了这个项目 star ,而且发推声援。在官方文档的翻译事情上,或许我们是有点脱轨了,不过不要紧,在使用全球最大的同性交友平台上,我们是与国际接轨的。还有啊,等过完了愚人节,我们还有个节日也是与国际接轨的——国际劳动节,纪念 1886 年芝加哥工人大罢工,确立每日 8 小时工作制的节日。相关链接: 翻译进度:https://www.transifex.com/python-doc/python-newestV站话题:https://neue.v2ex.com/t/477400#reply147邮件列表:https://groups.google.com/forum/#!topic/python-cn/8H4qhhI6khw

March 30, 2019 · 1 min · jiezi

联科首个开源项目启动!未来可期,诚邀加入!

OpenEA开源组织是广州市联科软件有限公司旗下的一个“开放·共享·全球化”的开源组织。OpenEA全称“Open+Enterprise+Application”,意为开放的企业应用,致力让所有企业都能轻松用上流程应用开发平台。2019年联科将逐步开源基于流程应用的快速开发平台,以“专业·高效·创新·自由·开放·回馈”为理念,以“开源之林”为目标,构建开放的技术生态圈。最近【流程设计器组件FlowDesigner】作为第一个开源项目,主要用于设计和控制流程各个运转过程,欢迎广大技术好友前来码云交流探讨!流程设计器组件FlowDesigner前言FlowDesigner来源于Linkey BPM中的流程设计器,作用于流程运行过程中的图形描述。它的操作简捷轻巧,能快速绘制出流程图。组件单独也可以使用,并能嵌入到任何需要该组件的系统中。分享,是“开源”的真谛。机不可失失不再来,准备好加入我们了吗?立即前往码云Fork项目吧,地址:https://gitee.com/openEA/Flow…

March 25, 2019 · 1 min · jiezi

buzzfe 社交网站开发——(一)项目准备

BUZZFE信息社交网站开发过程记录本网站打算解决的问题 1. 如果我是文章作者, 是否能自己定制发布到首页的时间 2. 如果我是读者, 是否能逃离机器算法形成的信息茧房网站开发所使用的技术选型开发系统: manjaro开发语言: python3.7开发框架: django2.1.7开发工具: vscode数据库: postgresql, redis项目名称: buzzfe项目开发中所需的其他第三方库, 会在具体的应用开发中列出各种软件安装及配置在win10上安装vmware15 然后在虚拟机中安装 manjaromanjaro自带最新版python3.7创建python虚拟环境python -m venv venv/buzzfeenv进入虚拟环境,创建django工程source venv/buzzfeenv/bin/activate安装django2.1.7pip install django创建django工程django-admin startproject buzzfe将vscode中pythonpath 配置为刚刚创建的python虚拟环境"python.pythonPath": “/home/dpeng/app/venv/buzzfeenv/bin/python”,启动djangopython manage.py runserver打开浏览器, 输入 127.0.0.1:8000,你将看见 django 启动的默认页面使用github进行项目管理和版本控制注意 你需要在版本管理前 务必添加 .gitignore 可选添加 README.md 和 LICENSEgit add .git statusgit commit -m ‘第一次提交’git push -u origin master项目应用创建accountpython manage.py startapp accountarticlepython manage.py startapp articlebuzzfe项目配置settings使用django自带 auth.user, 需将account放置在第一个’account.apps.AccountConfig’,‘django.contrib.admin’,‘django.contrib.auth’,‘django.contrib.contenttypes’,‘django.contrib.sessions’,‘django.contrib.messages’,‘django.contrib.staticfiles’,‘article.apps.ArticleConfig’,时间及时区及语言设置TIME_ZONE = ‘Asia/Shanghai’USE_TZ = Falsetemplates设置’DIRS’: [os.path.join(BASE_DIR, ’templates’).replace(’\’, ‘/’),],postgresql 数据库配置DATABASES = { ‘default’: { ‘ENGINE’: ‘django.db.backends.postgresql’, ‘NAME’: ‘db_buzzfe’, ‘USER’: ‘db_admin’, ‘PASSWORD’: ‘db_mima’, }}static 静态文件配置STATIC_URL = ‘/static/‘STATICFILES_DIRS = ( os.path.join(BASE_DIR, “static”),)欲知项目开发后事如何,且等下回更新… ...

March 19, 2019 · 1 min · jiezi

Python进阶:如何将字符串常量转化为变量?

前几天,我们Python猫交流学习群 里的 M 同学提了个问题。这个问题挺有意思,经初次讨论,我们认为它无解。然而,我认为它很有价值,应该继续思考怎么解决,所以就在私密的知识星球上记录了下来。万万没想到的是,在第二天,有两位同学接连给出了解决方法!由此,群内出现了一轮热烈的技术交流。本文将相关的内容要点作了梳理,并由此引申到更进一步的学习话题,希望对你有所帮助。1、如何动态生成变量名?M 同学的问题如下:打扰一下大家,请教一个问题,已知 list = [‘A’, ‘B’, ‘C’, ‘D’] , 如何才能得到以 list 中元素命名的新列表 A = [], B = [], C = [], D = [] 呢?简单理解,这个问题的意思是,将字符串内容作为其它对象的变量名。 list 中的元素是字符串,此处的 ‘A’-‘D’ 是常量 ,而在要求的结果中,A-D 是变量 。如果强行直接将常量当做变量使用,它会报错:>>> ‘A’ = []…SyntaxError: can’t assign to literal报错中的literal 指的是字面量 ,这是计算机科学中常见的一个概念,用于表达源代码中的固定值。 例如,整数、浮点数、字符串等基本类型,就是字面量。字面量指的就是一个量本身,可以理解为一种原子性的实体,当然不能再被赋值了。所以,取出的字符串内容,并不能直接用作变量名,需要另想办法。有初学者可能会想,list[0] = [] 行不行?当然不行,因为没有出现 A 。那 A = list[0] ,接着 A = [] 呢?那也不行,因为这里的 A 是你凭空定义出来的,而不是从已有条件中生成的。当时,群里只有两三个同学参与了讨论,我们没想到解决办法。但是,我觉得这个题目很有意思,值得玩味。因为,如果能解决这个问题,那就意味着可以不作预先定义,而是动态地生成变量名,这不仅能减少给变量取名的麻烦,还实现了自动编码!可以设想一下未来,人工智能在编写代码的时候,如果能根据已知条件,动态生成变量名,那编写代码的过程不就顺利多了么?(据说,现在已经有人工智能可以编写代码了,不知它在取变量名时,是用的什么方法?)2、办法总是有的最近,学习群里蒙混进来了几个打广告的,为此,我决定提高审核门槛,例如,用群里的问题来作个考核。万万没想到的是,第一个被考核到的 Q 同学,几乎不假思索地就说出了一个解决上述问题的思路。而偏偏就是那么巧 ,几乎在同时,群内的 J 同学给出了另外一个解决方法(他没看到群内的讨论,而是看到了知识星球的记录,才知道这个问题的)。也就是说,前一晚还以为无解的问题,在第二天竟得到了两种不同的解决方法!那么,他们的答案是什么呢?# J 同学的解答>>> list1 = [‘A’, ‘B’, ‘C’, ‘D’]>>> for i in list1:>>> globals()[i] = []>>> A[]这个方法通过修改全局命名空间,巧妙地“定义”出了新的变量。globals() 方法取出来的是一个字典,字符串 ‘A’ 是其中一个键值(key),而这个键值恰恰是全局命名空间中的一个变量,这就实现了从常量到变量的转化。在数据结构层面上,空列表 [] 作为一个值(value)跟它的字符串键值绑定在一起,而在运用层面上,它作为变量内容而跟变量名绑定在一起。看到这个回答的时候,我就突然想起来了,上个月转载过一篇《Python 动态赋值的陷阱》,讲的正是动态地进行变量赋值 的问题啊!我似乎只关注了 globals() 与 locals() 用法的区别,却没有真正地掌握它们的原初用途。J 同学说,他正是看了那篇文章,才学得了这个方法。这就有意思了,我分享了一个自己囫囵吞枣的知识,然后它被 J 同学吸收掌握,最后反馈回来解决了我的难题。我真切地感受到了知识分享的魅力:知识在流动中获得生命,在碰撞中锃亮色泽。 同时,我也真切地明白了一个互助的学习团体的好处:利人者也利己,互助者共同进步。3、动态执行代码的方法新进群的 Q 同学,提供了一个不同的答案:# Q 同学的解答>>> list1 = [‘A’, ‘B’, ‘C’, ‘D’]>>> for i in list1:>>> exec(f"{i} = []")>>> A[]他的写法用到了 Python 3.6 才引入的 f-strings 特性,事实上,在较低版本中,也是可以实现的,只需要保证 exec() 方法接收的参数是包含了变量 i 的字符串即可,例如这样写:# 以下代码可替换上例的第 4 行exec(i + " = []")# 或者:exec("{} = []".format(i))# 或者:exec(’ ‘.join([i, ‘= []’]))这几种写法的区别只是字符串拼接法的区别,关于如何拼接字符串,以及不同方法之间的区别,可参看《详解Python拼接字符串的七种方式》。Q 同学这个答案的核心在于 exec() 方法,它是内置的,用途是执行储存在字符串或文件中的代码段。 它的基础用法如下:>>> exec(‘x = 1 + 2’)>>> x3# 执行代码段>>> s = “”">>> x = 10>>> y = 20>>> sum = x + y>>> print(sum)>>> “”">>> exec(s)30看完了 exec() 的用法,我们再回来看 Q 同学的答案。for-循环中取出来的 i 是字符串,而拼接后的字符串经过 exec() 的处理,就获得了动态编写代码的效果。也就是说,因为字符串常量的内容被当做有效代码而执行了,其中的 ‘A’-‘D’ 元素,就取得了新的身份,变成了最终的 A-D 变量名。这个方法看起来很简单啊,可是由于 exec() 方法太生僻了,直到 Q 同学提出,我们才醒悟过来。注意:在 Python3 中,exec() 是个内置方法;而在 Python2 中,exec 是个语句(statement),另外有个 execfile() 方法,两者相合并,就成了 Python3 中的 exec() 方法。本文使用的是 Python3。4、总结抽象一下最初的问题,它实际问的是“如何将字符串内容作为其它对象的变量名”,更进一步地讲是——“如何将常量转化为变量 ”。使用直接进行赋值的静态方法,行不通。两位同学提出的方法都是间接的动态方法:一个是动态地进行变量赋值,通过修改命名空间而植入变量;一个是动态地执行代码,可以说是通过“走后门”的方式,安插了变量。 两种方法殊途同归,不管是白猫还是黑猫,它们都抓到了老鼠。这两种方法已经给我们带来了很有价值的启发,同时,因为它们,群内小伙伴们更是发散地讨论一些相关联的话题,例如:S 同学提出了另一种修改命名空间中变量的写法、L 同学提到了 eval() 的意义、eval() 与 exec() 的区别、我查到了为什么要慎用 eval() 、C 与 H 同学提到了 eval() 的安全用法……虽然,某些话题无法在群聊中充分展开,但是,这些话题知识的延展联系,大大地丰富了本文开头的问题,这一个微小的问题,牵连出来了两个大的知识体系。最后,真得感谢群内的这些爱学习的优秀的同志们!除了文中提及的,还有一些同学也做了积极贡献,大家都很给力!相关链接: 《Python 动态赋值的陷阱》《详解Python拼接字符串的七种方式》eval()、exec()及其相关函数:https://www.tuicool.com/wx/vE…公众号【Python猫】, 专注Python技术、数据科学和深度学习,力图创造一个有趣又有用的学习分享平台。本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、优质英文推荐与翻译等等,欢迎关注哦。PS:后台回复“爱学习”,免费获得一份学习大礼包。 ...

March 17, 2019 · 1 min · jiezi

Django学习——Django的基础(1)

Django简单介绍django和mvc,mvt设计模式,如果已知悉可以直接跳过。什么是Django是由python写的一个开源的web应用程序框架,于2005年7月初次发布,并在2008年9月发布了第一个正式版本1.0MVCmvc是一种业界比较通用的软件设计典范,该设计模式于1982年首次被提出,有很多的web框架都使用了该设计模式比如(struts,spring,.net,Thinkphp等等),一直沿用到现在。所以我们有必要了解一下它.MVCmvc主要是由model(模型)、View(视图)、Controller(控制器)三部分组成。将业务逻辑,数据和界面显示分开,功能划分清晰,有利于降低软件的耦合度和团队开发。甚至还可以提高代码的可维护性,延长软件的生命周期。(图片来自百度图片)模型(model)是程序中用于处理应用程序数据逻辑的部分,通常模型对象负责存取数据库中的数据。例如学生管理中学生、成绩等就可以被当作模型定义视图(view)是程序中处理数据显示的部分,通常是依据模型的数据来显示的。例如显示学生信息、成绩等由视图来负责控制器(controller)是程序中处理数据与人的交互的部分,例如负责从视图读取输入的数据,处理后传输给模型存储等。例如新建学生信息、修改、删除等操作MTV本质思想上与mvc差不多,只不过各个的功能有些变动模型(model)负责业务对象数据和数据库的存取(相当于mvc的model)模板(template)负责吧页面展示给用户(相当于mvc的view)视图(view)负责业务逻辑,并在适当的时候调用相应的model和template路由django除了mvt之外,它还有一个url分发器,我一般称之为路由。主要的作用就是将一个url请求分发到不同的view处理,view再调用相应的model和template关于django的安装及使用将在下一章共同学习。

March 17, 2019 · 1 min · jiezi

Python猫荐书系列:文也深度学习,理也深度学习

最近出了两件大新闻,相信大家可能有所耳闻。我来当个播报员,给大家转述一下:1、中国队在第 11 界罗马尼亚数学大师赛(RMM)中无缘金牌。该项赛事是三大国际赛事之一,被誉为中学奥数的最高难度。其中一道题,令中国队全军覆没。 2、一个出自清华姚班,毕业于斯坦福的女博士,她的毕业论文成了学术圈的“爆款”。这篇论文研究的主题是——如何让机器学会理解人类语言? 每天的新闻多如牛毛,唯独这两件引起了我的注意。它们跟本期的荐书栏目也是强关联,下面就给大家说道说道。上图标出了中国队成绩最好的三名队员。前两人在其它题目全部满分的情况下,第三题竟然是 0 分!什么样的题目能让我们的顶尖高手都束手无策呢?算了,题目我就不放出来了(我看不懂,不自找其辱。总之你们知道它很难就得了)。但是,那道题是图论的问题,关于图论,我们可以说说它跟计算机科学的关系。图论是数学的一个分支,它研究的最著名问题有柯尼斯堡七桥问题 与 四色地图问题 ,相信大家都曾见过,而在计算机领域,它也带来了诸多的研究成果:最小生成树问题、旅行商问题(NP困难)、拓扑排序算法、广度优先算法、深度优先算法,等等。奥数就这样跟程序员的职业联系了起来。然而,更值得一提的是第二个新闻:它研究的是人工智能领域最前沿的话题,想构建一个在深度神经网络之上的阅读理解模型 。简单地说是,教会计算机来阅读文本的能力。这项研究与大家熟知的数字个人助理不同(如 Alexa、Siri、Google Assistant、Cortana),它的难度超越了简单会话与信息匹配的一般性问题,想克服的是文本级阅读理解,与开放性问答等高度抽象层面的难关。它的研究成果将给数字个人助理带来质的提升,而对于人类语言文本的阅读理解能力,也必然带来更广阔的应用前途。这一切,都归功于深度学习。深度学习是我很感兴趣的领域。我们有幸生在这个时代,见证了 AlphaGo 打败人类的顶尖棋手,正在见证各种 AI 技术的出现,无人驾驶、医疗诊断、AI 翻译、金融科技、深度法律……我们的未来将被人工智能深远地影响。本期Python 猫荐书栏目(系列之六),就以此为话题,推荐给大家两本书:它们都叫《深度学习》,但是内容很不一样。第一本从应用数学,到深度学习的各种模型、算法与科研问题,走的是极其专业的路线。而另一本讲的是深度学习的 60 年发展史,以及对智能时代的一些前瞻性预测,走的是通俗科普的路线。如果要强行划分的话,前一本属理科,主要给相关领域的学生与程序员阅读,而后一本则属文科,面向所有对人工智能的历史与未来感兴趣的人群。事实上,第一本书被很多人誉为深度学习的圣经,知名度极高,有一个昵称叫作“花书”。简单梳理一下它的内容:第一部分是深度学习的基础,包含线性代数与概率论等数学知识,以及梯度优化、拟合、偏差、最大似然估计与监督学习等基础概念;第二部分是深度学习的关键部分,涉及深度前馈网络、正则化、模型优化的方法、卷积网络、序列建模、与实践应用内容;第三部分是深度学习研究,例如线性因子模型、自编码器、表示学习、结构化概率模型、蒙特卡罗方法、直面配分函数、近似推断、深度生成模型,等等。要知道,本专栏是兴趣大于能力,没办法深入剖析这本书的精华,再讲出些令行家也折服的话,但是,这本书值得推荐之处也很显著:它是一种正统的、学院派的、知识全面的、一丝不苟的、偏重理论的书籍,没错,正像是大学里相关专业的指定参考书。这就意味着,如果想进入深度学习领域,这本书将是你最好的老师。(而且不用考试,手动滑稽)至于第二本《深度学习》,书的副标题是“智能时代的核心驱动力量 ”。其实这只是翻译的结果,原书的英文名是《The Deep Learning Revolution》。20 世纪 70 年代到 90 年代是深度学习(神经网络)的寒冬,本书作者既是深度学习的先驱与奠基者,也是打破此寒冬,令深度学习东山再起的大功臣。他名叫特伦斯·谢诺夫斯基 (Terrence Sejnowski)。特伦斯是谁呢?世界十大AI科学家之一,美国四大国家学院(国家科学院、国家医学院、国家工程院、国家艺术与科学学院)在世仅3位的“四院院士 ”之一,全球AI专业会议NIPS基金会主席。深度学习的核心技术玻尔兹曼机 ,正是由特伦斯与杰弗里·辛顿共同建立的。那书的内容是什么呢?这本书在前言中称:这是一本关于深度学习的过去、现在和未来的指南。 在如此宏观的视角下,它主要讲到了一些重要概念的发展、科研群体研究的内容和传承,以及深度学习对当今社会的影响。也就是说,它不再关心微观的原理、底层的细节、繁复的逻辑。与第一本书的调性截然不同。这本书以第一人称视角讲述,带入了很多个人的动态:读书经历、研究课题、演讲与会议、人际关系、趣闻、甚至还有八卦(例如差点跟女朋友分手的一次会议。PS:他们在一起了,现在也没分开)。因此,第二本书的阅读门槛不高,还饶有趣味。往期荐书回顾: 第一期:《编写高质量代码改善 Python 程序的 91 个建议》第二期:《Python最佳实践指南》第三期:《黑客与画家》第四期:《Python源码剖析》第五期:《Python高性能编程》————-荐书完————-世事无巧不成书。似乎每期荐书都会发生一些巧合,因此我得额外交代几句:1、我早知第一本书的大名,也翻看过数学部分的一些内容,但是兴趣就止步于此。有打算纳其入荐书系列,但没想到会这么快。至于第二本书,恰好是在上期荐书发布后,中信出版社的营销人员找我约稿,当时这本书还没上市。我并非深度学习领域的专家,只能写写旁观者的言语,既然无法深入,干脆就将它们凑在一起了。2、荐书栏目不是专业书评,无法讲透全书的技术精粹,但我仍大着胆写了(之所以拖了这么久才动笔,就是因为过于担心)。一方面逼使自己阅读和查资料,快速归纳与写作;另一方面也确实是希望通过自己的文笔,能够使一部分读者获知到原先不知的信息,产生阅读的兴趣。3、就在前几天(2 月 28 日),一位知名的 Python 博主@Vamei 因抑郁症自杀了。我在看资料的时候,发现他也写了第二本《深度学习》的书评。他发布的时间是 1 月 31 日,而在这个时间,新书还未上市。这意味着他可能跟我一样,都收到了出版社的预读本,我们就是那么巧合地在同样的时间里阅读着同一本还未上市的新书。我想,这本书大概就是在给我传递一个讯息。我有很多次想过放弃邀约(无稿费,赠书一本)、放弃写这一篇荐书,直到前几天才真正开始动笔。这个神秘的讯息就这么巧地传过来了。荐书,见人。写完这篇荐书,我要写写他了。4、Vamei 的豆瓣主页写道:Vamei 是赤道附近一个台风的名字。按照气象规律,台风不常出现在赤道。所以,Vamei是一个离群的风,无所顾忌地生长,不着边际地游荡。公众号【Python猫】, 专注Python技术、数据科学和深度学习,力图创造一个有趣又有用的学习分享平台。本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、优质英文推荐与翻译等等,欢迎关注哦。PS:后台回复“爱学习”,免费获得一份学习大礼包。

March 8, 2019 · 1 min · jiezi

个推微服务网关架构实践

作者:个推应用平台基础架构高级研发工程师 阿飞在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求。因此,在客户端和服务端之间增加一个API网关成为多数微服务架构的必然选择。在个推的微服务实践中,API网关也起着至关重要的作用。一方面,API网关是个推微服务体系对外的唯一入口;另一方面,API网关中实现了很多后端服务的共性需求,避免了重复建设。个推微服务网关的设计与实现个推微服务主要是基于Docker和Kubernetes进行实践的。在整个微服务架构中,最底层的是个推私有部署的Kubernetes集群,在集群之上,部署了应用服务。个推的应用服务体系共分为三层,最上一层是网关层,接着是业务层,最下面是基础层服务。在部署应用服务时,我们使用了Kubernetes的命名空间对不同产品线的产品进行隔离。除了应用服务外, Kubernetes集群上还部署了Consul来实现配置的管理、Kube-DNS实现服务注册与发现,以及一些辅助系统来进行应用和集群的管理。下图是个推微服务体系的架构图。个推对API网关的功能需求主要有以下几方面:要支持配置多个产品,为不同的产品提供不同的端口;动态路由;URI的重写;服务的注册与发现;负载均衡;安全相关的需求,如session校验等;流量控制;链路追踪;A/B Testing。在对市面上已有的网关产品进行调研后,我们的技术团队发现,它们并不太适合应用于个推的微服务体系。第一,个推配置的管理都是基于Consul实现的,而大部分网关产品都需要基于一些DB存储,来进行配置的管理;第二,大部分的网关产品提供的功能比较通用,也比较完善,这同时也降低了配置的复杂度以及灵活性;第三,大部分的网关产品很难直接融入到个推的微服务架构体系中。最终,个推选择使用了OperResty和Lua进行自研网关,在自研的过程中,我们也借鉴了其他网关产品的一些设计,如Kong和Orange的插件机制等。个推的API网关的插件设计如下图所示。OpenResty对请求的处理分为多个阶段。个推API网关的插件主要是在Set、Rewrite、Access、Header_filter、Body_filter、Log这六个阶段做相应的处理,其中,每一个插件都可以在一个或多个阶段起到相应的作用。在一个请求到达API网关之后,网关会根据配置为该请求选择插件,然后根据每个插件的规则,进一步过滤出匹配规则的插件,最后对插件进行实例化,对流量进行相应的处理。我们可以通过举例来理解这个过程,如上图所示,localhost:8080/api/demo/test/hello这个请求到达网关后,网关会根据host和端口确定产品信息,并提取出URI(/api/demo/test/hello),然后根据产品的具体配置,筛选出需要使用的插件——Rewrite_URI、Dyups和Auth,接下来根据每个插件的规则配置进行过滤,过滤后,只有Rewrite_URI和Dyups两个插件被选中。之后实例化这两个插件,在各个阶段对请求进行处理。请求被转发到后端服务时,URI就被rewrite为“/demo/test/hello”,upstream也被设置为“prod1-svc1”。请求由后端服务处理之后,响应会经网关返回给客户端,这就是整个插件的设计和工作的流程。为了优化性能,我们将插件的实例化延缓到了请求真正开始处理时,在此之前,网关会通过产品配置和规则,过滤掉不需要执行的插件。从图中也可以看出,每个插件的规则配置都很简单,并且没有统一的格式,这也确保了插件配置的简单灵活。网关的配置均为热更新,通过Consul和Consul-Template来实现,配置在Consul上进行更新后,Consul-Template会将其实时地拉取下来,然后通过以下两种方式进行更新。(1)通过调用Update API,将配置更新到shared-dict中。(2)更新配置文件,利用Reload OpenResty实现配置文件的更新。个推微服务网关提供的主要功能1.动态路由动态路由主要涉及到三个方面:服务注册、服务发现和请求转发。如下图所示,服务的注册和发现是基于Kubernetes的Service和Kube-DNS实现的,在Consul中,会维持一个服务的映射表,应用的每一个微服务都对应Kubernetes上的一个Service,每创建一个Service都会在Consul上的服务映射表中添加一项(会被实时更新到网关的共享内存中)。网关每收到一个请求都会从服务映射表中查询到具体的后端服务(即Kubernetes中的Service名),并进行动态路由。Kube-DNS可以将Service的域名解析成Kubernetes内部的ClusterIP,而Service代理了多个Pod,会将流量均衡地转发到不同的Pod上。2.流量控制流量控制主要是通过一个名为“Counter”的后端服务和网关中的流控插件实现的。Counter负责存储请求的访问次数和限值,并且支持按时间维度进行计数。流控插件负责拦截流量,调用Counter的接口进行超限查询,如果Counter返回请求超限,网关就会直接拒绝访问,实现限次的功能,再结合时间维度就可以实现限频的需求。同时流控插件通过输出日志信息到fluent-bit,由fluent-bit聚合计次来更新Counter中的计数。3.链路追踪整个微服务体系的链路追踪是基于分布式的链路追踪系统Zipkin来实现的。通过在网关安装Zipkin插件和在后端服务中引入Zipkin中间件,实现最终的链路追踪功能。具体架构如下图所示。4. A/B测试在A/B测试的实现中,有以下几个关键点:(1)所有的策略信息都配置在Consul上,并通过Consul-Template实时生效到各个微服务的内存中;(2)每条策略均有指明,调用一个微服务时应调用A还是B(默认为A);(3)网关中实现A/B插件,在请求到达网关时,通过A/B插件配置的规则,即可确定请求适用的A/B策略;(4)网关会将请求适用的A/B策略通过URL参数传递下去;(5)每个微服务通过传递下来的策略,选择正确的服务进行访问。下图给出了两种场景下的调用链路。总结以上就是个推微服务网关的设计和主要功能的实现。之后,个推的技术团队会不断提升API网关的弹性设计,使其能够在故障出现时,缩小故障的影响范围;同时,我们也会继续将网关与DevOps平台做进一步地结合,以确保网关在迭代更新时,能够有更多的自动化测试来保证质量,实现更快速地部署。

March 5, 2019 · 1 min · jiezi

django总结六:视图的使用and url的配置 and 模版的使用

视图1.概述:django的视图是指对请求进行回应,即是一个函数,对应url请求。在view.py中定义。如何定义:1)from django.http import HttpResponsedef index(request):return HttpResponse(‘hello python!!!!’)url1.修改urls.pyfrom django.conf.urls import url, includefrom django.contrib import adminurlpatterns = [url(r’^admin/’, admin.site.urls),url(r’^’, include(‘myApp.urls’))]2.在app目录下新建urls.py, 进行如下设置from django.conf.urls import urlfrom . import viewsurlpatterns = [url(r’^$’, views.index)]然后访问http://127.0.0.1:8000/,页面即显示hello python!!!模版概述:是指html页面,将视图传过来的数据进行填充创建模版目录,在应用或者总目录下创建均可以templates配置模版路径。修改settings.py中的TEMPLATES = [{‘BACKEND’: ‘django.template.backends.django.DjangoTemplates’,‘DIRS’: [os.path.join(BASE_DIR, ’templates’)],视图,模版,url联合使用显示一个数据库表1.写grades。html页面 {% for grade in grades %} <li> <a href="#"> {{ grade.ganme }} </a> </li> {% endfor %}2.定义grades 对应的视图from .models import Gradesdef grades(request):gradeslist = Grades.objects.all()return render(request, ‘myApp/grades.html’, {‘grades’:gradeslist})3.配置grades对应的urlurl(r’^grades/$’, views.grades)访问http://127.0.0.1:8000/grades即显示班级信息。

March 4, 2019 · 1 min · jiezi

django总结五:启动服务器and admin站点管理

启动服务器命令:python manage.py runserver ip:portport:默认值是8000ip:本机上操作可以不加ip2.admin站点管理1)配置admin应用:在setting.py文件中的INSTALLED_APPS添加django.contrib.admin,默认是已经添加好的。2)创建管理员用户:python manage.py createsuperuser按照提示输入用户名密码邮箱,比如adminlin lin12345678然后http://127.0.0.1:8000/admin即可进入站点登录界面若需要中文显示,更改setting.py里面的设置即可:LANGUAGE_CODE = ‘zh-Hans’TIME_ZONE = ‘Asia/Shanghai'3)管理数据表在admin.py里面注册数据库表from .models import Grades, Studentadmin.site.register(Grades)admin.site.register(Student)重新刷新页面即可显示注册后的数据库名称。4)自定义站点管理页面:修改admin.py,增加表对应的类,然后再注册上class GradesAdmin(admin.ModelAdmin):list_display = [‘gname’, ‘gdate’, ‘ggirlnum’, ‘gboynum’,‘isDelete’]list_filter = [‘gname’]search_field = [‘gname’]list_per_page = 5fields = [‘gname’, ‘gdate’, ‘ggirlnum’, ‘gboynum’,‘isDelete’]admin.site.register(Grades, GradesAdmin)属性说明:列表页面属性list_display 显示字段list_filter 过滤字段search_field 搜素字段list_per_page分页添加修改页属性fields 属性的先后顺序fieldsets 给属性分组他们俩不能同时使用注册也可以使用装饰器完成注册。

March 4, 2019 · 1 min · jiezi

整理了一周的Python资料,包含各阶段所需网站、项目,收藏了慢慢来

这周应该有不少学校已经开学了,那么同学们都该动起来了,把家里面的那些懒习惯给扔掉了可以。不知怎么的,最近不少关注我的读者都开始私信我怎么学好python?零基础转行是不是合适,还有希望吗?今年30了,还能不能转IT?其实关于零基础转型的,我以前写过一篇文章,没有看过的都可以看看:「零基础非科班如何成长为五百强Arch」,另外还有一篇知乎点赞1k+关于如何学习python的也建议都看下:「万字谏言,给那些想学Python的人,建议收藏后细看!」。今天就把剩余板块给一一填充,意在做成一个系列,让大家看了这个系列后,明白自己选择了IT这条路后,应该干什么,怎么干。相信大家看完以上两篇文章后多少都会有个问号,除了我推荐的「笨办法」外,就没什么资料的,而很多新手村玩家都喜欢问一个问题:有什么资料可以参考的吗?有什么实战项目可以借鉴的吗?今天这篇文章,我花了一周的时间搜索、整理、调研、筛选,最后定稿。希望能够帮助到大家,减少在起步阶段的油耗,集中精神突破技术。我把链接一起贴出来,大家收藏后,可以去电脑上打开,比较方便。虽然强调过很多次了,但是还是要多提一句,不要看python2.x,如果你是零基础过来的,请直接开始你的py3.x 之路。建议3.6,3.7的一些特性可能对你不是很重要。1.初出茅庐我不会推荐你们去看官方文档的,因为我知道,你们不会去看的1.廖雪峰老师,包括我自己,我相信很多读者应该都多少看过:「廖雪峰的官方网站」:https://www.liaoxuefeng.com/w…2.Vamei老师在cnblogs上的一个目录,我基本都看过,内容比较基础,很适合零基础的同学看:「python快速教程」:http://www.cnblogs.com/vamei/…3.实验楼,这个网站其实做得蛮好的,虽然是收费项目,但是说实话,如果你想认真学习的话,这些学费该付的就付吧。网站主要是将python的知识点和小项目结合起来了:「Python基础+项目实战课程」:https://www.shiyanlou.com/cou…4.我一直推荐的「笨办法学 Python」现在有了在线版,只不过是英文版的,别和我说英语看不懂!你这是要我去接英语广告吗……,当然他还有收费的课程,看你自己喜欢咯:「learnpythonthehardway」:https://learnpythonthehardway…5.这个网站我是订阅了的,每周都会有更新,内容的话基本都和Python相关:文章、教程、演讲、书籍、项目、工作等都有:「Python Weekly」:https://www.pythonweekly.com/6.「Pycrumbs」是搜集了各种免费Python的资料,你可以收藏后慢慢看:「Pycrumbs」:https://github.com/kirang89/p…2.小试牛刀经过以上内容的学习,基本的语法、函数、类的定义和调用应该都掌握了,接下来就是找些小练习,试试自己的三把刷子了。1.首当其冲的就是很多人都会推荐的「Python challenge」,现在已经到33关了,可以看看自己能到第几关哦:「pythonchallenge」:http://www.pythonchallenge.com2.对于很多人来说,英文看不懂,我又没接到英语广告的,我帮你们找了一个中文网站:「Python中文学习大本营」:http://www.pythondoc.com/3.再给大家推荐一个爬虫er必看的博客,我们的崔大系列:「崔庆才的个人博客」:https://cuiqingcai.com/4.虽然已经不更新了,但是已有的联系够大家琢磨半天了,建议认真的去把每一题都做了:「每天一点小练习」:https://github.com/Yixiaohan/…3.登堂入室经过小试牛刀后,我相信现在的你应该已经跃跃欲试了,心里一句话:还有谁。那么是时候开始找些完整项目跟着抄了,哦不,是临摹,是跟着敲。你们千万别ctrl c,ctrl v 的把内容拷过来了,这没有任何效果。1.简书上我找到一个非常棒的「Django By Example」的中文翻译系列,推荐给大家,「Django By Example」本身就是一本非常不错的Django实战书:「Django By Example」:https://www.jianshu.com/p/058…2.再给大家推荐一个Flask写网站的教程,我推荐给很多人过,非常好,跟着作者一步步学习如何用flask开发一款属于自己的博客管理系统:「Flask 10天开发一个网站」:https://zhuanlan.zhihu.com/p/…3.在学习爬虫的路上,你一定会遇到一个叫scrapy的怪物,别人都告诉你要用它,所以你就去搜了,发现全特么是英文,又溜了。我给大家找了一份「Scrapy Cookbook」的中文版:「Scrapy Cookbook」:https://scrapy-cookbook.readt…4.「超级马里奥第一关」用pygame写的,这个就比较有意思了,大家有兴趣的可以跟着敲敲:「Super Mario Bros Level 1」:https://github.com/justinmeis…4.游刃有余再经过以上环节后,你必须要开始修炼心法了,练武之人,必修内功,否则就是花拳绣腿,形如:1.「LeetCode」想必大家都知道,也有一些读者刷过,但有多少人坚持下来了?LeetCode可以说是Python内功的九阴真经,哦不,是九阳神功。在平时写代码的时候你不一定会用到,但是他却是你解决问题的思想源泉:「LeetCode China」:https://leetcode-cn.com/2.如果说算法是一个程序员的九阳神功,那么设计模式就是你的乾坤大挪移。设计模式在日常工作中会经常用到,对于不同的场景会需要用到不同的模式。「python-patterns」是我觉得非常棒的一个项目,如果你可以跟着他一个个学的话,你回头看看昨天写的Django Example里的代码,会有种“哪个傻逼写的垃圾代码啊”的错觉:「python-pattern」:https://github.com/faif/pytho…我有一个项目,上面有大多数的练习代码,包含:leet-code刷题,设计模式练习,爬虫项目,小应用,微信机器人等等。关注公众后,后台回复关键字:python资料,获取项目包本篇文章对不同阶段的人群都适用,别再说Python怎么学,没有实战项目了。撸袖子干呗,别墨迹了。

March 1, 2019 · 1 min · jiezi

Django搭建个人博客:给文章加个漂亮的标题图

现在虽然博客的功能大都实现了,但是界面还是比较朴素,特别是首页的文章列表几乎全是文字,看多了难免疲劳。因此,给每个文章标题配一张标题图,不仅美观,用户也能通过图片快速了解文章内容。实际上大部分社交网站也都是这么干的,毕竟人的天性就是懒,能看图就坚决不看字。在上传用户头像章节中,我们已经接触过上传、展示图片了。标题图的实现也差不多,不同的是本章会更近一步,对图片进行缩放等处理,使页面整洁美观、并且高效。准备工作与用户头像类似,标题图是属于每篇博文自己的“资产”,因此需要修改model,新建一个字段:article/models.pyclass ArticlePost(models.Model): … # 文章标题图 avatar = models.ImageField(upload_to=‘article/%Y%m%d/’, blank=True) …注意上传地址中的%Y%m%d是日期格式化的写法。比如上传时间是2019年2月26日,则标题图会上传到media/article/20190226这个目录中。记得数据迁移。标题图通常在创建新文章的时候就设置好了,而新文章是通过表单上传到数据库中的。因此接下来就是修改发表文章的表单类:article/forms.py…class ArticlePostForm(forms.ModelForm): class Meta: … fields = (’title’, ‘body’, ’tags’, ‘avatar’)增加了avatar字段而已,没有新内容。下一步就是修改视图。因为POST的表单中包含了图片文件,所以要将request.FILES也一并绑定到表单类中,否则图片无法正确保存:article/views.py…def article_create(request): if request.method == “POST”: # 增加 request.FILES article_post_form = ArticlePostForm(request.POST, request.FILES) …很好,功能差不多已经通了,接下来就是对图片进行处理。处理图片写代码之前先构思一下需要进行怎样的处理:标题图对画质没有太高的要求,因此需要缩小图片的体积,以便提高网页的加载速度。其次还需要对图片的长宽进行规范化。我比较喜欢将图片的宽度设置得相同,这样标题可以比较整齐。下一个问题是,代码应该写到什么地方呢?似乎在model、form或者view里处理图片都可以。在这里我打算把代码写到model中去,这样不管你在任何地方上传图片(包括后台中!),图片都会得到处理。想好之后,就要行动了。还记得Pillow这个库吗,我们很早就把它安装好了,现在是使用它的时候了:article/models.py…# 记得导入!from PIL import Imageclass ArticlePost(models.Model): … # 前面写好的代码 avatar = models.ImageField(upload_to=‘article/%Y%m%d/’, blank=True) # 保存时处理图片 def save(self, *args, **kwargs): # 调用原有的 save() 的功能 article = super(ArticlePost, self).save(*args, **kwargs) # 固定宽度缩放图片大小 if self.avatar and not kwargs.get(‘update_fields’): image = Image.open(self.avatar) (x, y) = image.size new_x = 400 new_y = int(new_x * (y / x)) resized_image = image.resize((new_x, new_y), Image.ANTIALIAS) resized_image.save(self.avatar.path) return article …代码不多,但是有很多细节,值得仔细推敲。不急,一行一行来:save()是model内置的方法,它会在model实例每次保存时调用。这里改写它,将处理图片的逻辑“塞进去”。super(ArticlePost, self).save(*args, **kwargs)的作用是调用父类中原有的save()方法,即将model中的字段数据保存到数据库中。因为图片处理是基于已经保存的图片的,所以这句一定要在处理图片之前执行,否则会得到找不到原始图片的错误。if判断语句的条件有两个:博文的标题图不是必须的,self.avatar剔除掉没有标题图的文章,这些文章不需要处理图片。不太好理解的是这个not kwargs.get(‘update_fields’)。还记得article_detail()视图中为了统计浏览量而调用了save(update_fields=[’total_views’])吗?没错,就是为了排除掉统计浏览量调用的save(),免得每次用户进入文章详情页面都要处理标题图,太影响性能了。这种判断方法虽然简单,但会造成模型和视图的紧耦合。读者在实践中可探索更优雅的方法,比如专门设置一个参数,用来判断是哪类视图调用了save()。接下来都是Pillow处理图片的流程了:打开原始图片,取得分辨率,将新图片的宽度设置为400并根据比例缩小高度,最后用新图片将原始图片覆盖掉。Image.ANTIALIAS表示缩放采用平滑滤波。最后一步,将父类save()返回的结果原封不动的返回去。完美!模板与测试剩下的工作就比较简单了。修改发表文章的模板,让表单能够上传图片:templates/article/create.html…<!– 记得增加 enctype ! –><form … enctype=“multipart/form-data”> … <!– 文章标题图 –> <div class=“form-group”> <label for=“avatar”>标题图</label> <input type=“file” class=“form-control-file” name=“avatar” id=“avatar”> </div> …</form>…然后修改文章列表模板,让其能够展现标题图。为了美观,这里稍微改动了列表循环的整体结构:templates/article/list.html…<!– 列表循环 –><div class=“row mt-2”> {% for article in articles %} <!– 标题图 –> {% if article.avatar %} <div class=“col-3”> <img src="{{ article.avatar.url }}" alt=“avatar” style=“max-width:100%; border-radius: 20px” > </div> {% endif %} <div class=“col”> <!– 栏目 –> … <!– 标签 –> … … <hr style=“width: 100%;”/> {% endfor %}</div>…接下来又是喜闻乐见的测试环节。启动服务器,打开发表文章页面:选择几张分辨率各不相同的图片作为标题图,发表几篇文章并回到文章列表页面:看起来似乎不错。查看一下media目录下实际保存的图片:确实保存到想要的目录下,并且左下角显示图片的宽度全都为400了。扫尾工作功能已经实现了,但还有扫尾工作需要去做:需要对上传的图片做更多的验证工作,比如上传的文件是否为图片、分辨率是否满足要求。虽然在个人博客项目中这些验证并不是特别重要,但在其他项目中就说不好了:谁知道用户会上传些什么奇奇怪怪的东西?编辑文章、删除文章也同样需要处理上传的图片。你还可以将缩放分辨率的技术应用到用户头像上,比如裁剪成方形。注意:删除数据库中的avatar条目只是断开了数据表和图片的链接而已,实际上图片还保存在原来的位置。要彻底删除图片,你还得写操作系统文件的代码才行。怎么实现这些功能就不赘述了,留给读者自己去折腾吧。轮子虽然本文是自己动手写的代码(严格说来Pillow也是轮子),但想必你也猜到了,还有更加智能的轮子:django-imagekit,这个库可以直接继承到model字段里面,比如这样:article/models.py# 引入imagekitfrom imagekit.models import ProcessedImageFieldfrom imagekit.processors import ResizeToFitclass ArticlePost(models.Model): … avatar = ProcessedImageField( upload_to=‘article/%Y%m%d’, processors=[ResizeToFit(width=400)], format=‘JPEG’, options={‘quality’: 100}, )字段中定义好了上传位置、处理规则、存储格式以及图片质量,你不需要写任何处理图片的代码了。更多的用法见官方介绍。总结本章学习了如何上传并处理文章的标题图,从此博客首页就有了漂亮的外观。需要指出的是,个人博客所采用的服务器通常性能不佳,用来保存文章缩略图等小尺寸的图片倒还好,但是千万不要存储大尺寸的图片文件,否则用户等待几分钟都刷不开你的图片,那是很悲剧的。因此建议你将大尺寸的图片、视频等放到专业的云对象存储服务商中,比如七牛云、又拍云等,在你存储量很小时(10G以内)是花不了多少钱的。有疑问请在杜赛的个人网站留言,我会尽快回复。或Email私信我:dusaiphoto@foxmail.com项目完整代码:Django_blog_tutorial ...

February 27, 2019 · 1 min · jiezi

Python与家国天下

导读:Python猫是一只喵星来客,它爱地球的一切,特别爱优雅而无所不能的 Python。我是它的人类朋友豌豆花下猫,被授权润色与发表它的文章。如果你是第一次看到这个系列文章,那我强烈建议,请先看看它写的前几篇文章(链接见文末),相信你一定会爱上这只神秘的哲学+极客猫的。不多说啦,一起来享用今天的“思想盛宴”吧!喵喵,好久不见啦朋友们。刚吃完一餐美食,我觉得好满足啊。自从习惯了地球的食物以后,我的肠胃发生了一些说不清道不明的反应。我能从最近的新陈代谢中感觉出来,自己的母胎习性正在逐渐地褪逝。人类的食物在改变着我,或者说是在重塑着我。说不定哪天,我会变成一棵白菜,或者一条鱼呢……呸呸呸。我还是想当猫。喵生苦短,得抓紧时间更文才行。最近,我看到了两件事,觉得有趣极了,就从这开始说吧。第一件事是,一个小有名气的影视明星因为他不配得到的学术精英的身份而遭到讽刺性的打假制度的口诛笔伐;第二件事是,一个功成名就的企业高管因为从城市回到乡村而戏谑性地获得了猫屎的名号。身份真是一个有魔力的话题。看见他们的身份错位,我又总会想起自己的境况。我(或许)知道自己在过去时态中是谁,但越来越把握不住在现在时态中的自己,更不清楚在未来时间中会是怎样。该怎样在人类世界中自处呢?又该怎样跟你们共处呢?思了好久,没有答案。脑壳疼,尾巴疼。还是不要想了啦喵。继续跟大家聊聊 Python 吧。上次我们说到了对象的边界问题 。无论是固定边界还是弹性边界,这不外乎就是修身的两种志趣,有的对象呢独善其身其乐也融融,有的对象呢兼容并包其理想之光也莹莹。但是,边界问题还没讲完。正如儒家经典所阐述:修身–齐家–治国–平天下。里层的势能推展开,走进更广阔的维度。Python 对象的边界也不只在自身。这里有一种巧妙的映射关系:对象(身)–函数(家)–模块(国)–包(天下)。个体被纳入到不同的命名空间,并存活在分层的作用域里。(当然,幸运的是,它们并不会受到道德礼法的森严压迫~__~)1、你的名字我们先来审视一下模块。这是一个合适的尺度,由此展开,可以顺利地连接起函数与包。模块是什么? 任何以.py 后缀结尾的文件就是一个模块(module)。模块的好处是什么? 首先,便于拆分不同功能的代码,单一功能的少量代码更容易维护;其次,便于组装与重复利用,Python 以丰富的第三方模块而闻名;最后,模块创造了私密的命名空间,能有效地管理各类对象的命名。可以说,模块是 Python 世界中最小的一种自恰的生态系统——除却直接在控制台中运行命令的情况外,模块是最小的可执行单位。 前面,我把模块类比成了国家,这当然是不伦不类的,因为你难以想象在现实世界中,会存在着数千数万的彼此殊然有别的国家(我指的可是在地球上,而喵星不同,以后细说)。类比法有助于我们发挥思维的作用 ,因此,不妨就做此假设。如此一来,想想模块间的相互引用就太有趣了,这不是国家间的战争入侵,而是一种人道主义的援助啊,至于公民们的流动与迁徙,则可能成为一场探险之旅的谈资。我还对模块的身份角色感兴趣。恰巧发现,在使用名字的时候,它们耍了一个双姓人的把戏 。下面请看表演。先创建两个模块,A.py 与 B.py,它们的内容如下:# A 模块的内容:print(“module A : “, name)# B 模块的内容:import Aprint(“module B : “, name)其中,name 指的是当前模块的名字。代码的逻辑是:A 模块会打印本模块的名字,B 模块由于引入了 A 模块,因此会先打印 A 模块的名字,再打印本模块的名字。那么,结果是如何的呢?执行 A.py 的结果:module A : __main__执行 B.py 的结果:module A : testmodule B : __main__你们看出问题的所在了吧!模块 A 前后竟然出现了两个不同的名字。这两个名字是什么意思,又为什么会有这样的不同呢?我想这正体现的是名字的本质吧——对自己来说,我就是我,并不需要一个名字来标记;而对他人来说,ta 是芸芸众生的一个,唯有命名才能区分。所以,一个模块自己称呼自己的时候(即执行自身时)是“main”,而给他人来称呼的时候(即被引用时),就会是该模块的本名。这真是一个巧妙的设定。由于模块的名称二重性,我们可以加个判断,将某个模块不对外的内容隐藏起来。# A 模块的内容:print(“module A : “, name)if name == “main”: print(“private info.")以上代码中,只有在执行 A 模块本身时,才会打印“private info”,而当它被导入到其它模块中时,则不会执行到该部分的内容。2、名字的时空对于生物来说,我们有各种各样的属性,例如姓名、性别、年龄,等等。对于 Python 的对象来说,它们也有各种属性。模块是一种对象,”name“就是它的一个属性。除此之外,模块还有如下最基本的属性:>>> import A>>> print(dir(A))[’builtins’, ‘cached’, ‘doc’, ‘file’, ‘loader’, ‘name’, ‘package’, ‘spec’]在一个模块的全局空间里,有些属性是全局起作用的,Python 称之为全局变量 ,而其它在局部起作用的属性,会被称为局部变量 。一个变量对应的是一个属性的名字,会关联到一个特定的值。通过 globals() 和 locals() ,可以将变量的“名值对”打印出来。x = 1def foo(): y = 2 print(“全局变量:”, globals()) print(“局部变量:”, locals())foo()在 IDE 中执行以上代码,结果:全局变量: {’name’: ‘main’, ‘doc’: None, ‘package’: None, ‘loader’: <_frozen_importlib_external.SourceFileLoader object at 0x000001AC1EB7A400>, ‘spec’: None, ‘annotations’: {}, ‘builtins’: <module ‘builtins’ (built-in)>, ‘file’: ‘C:/pythoncat/A.py’, ‘cached’: None, ‘x’: 1, ‘foo’: <function foo at 0x000001AC1EA73E18>}局部变量: {‘y’: 2}可以看出,x 是一个全局变量,对应的值是 1,而 y 是一个局部变量,对应的值是 2.两种变量的作用域不同 :局部变量作用于函数内部,不可直接在外部使用;全局变量作用于全局,但是在函数内部只可访问,不可修改。与 Java、C++ 等语言不同,Python 并不屈服于解析的便利,并不使用呆滞的花括号来编排作用域,而是用了轻巧简明的缩进方式。不过,所有编程语言在区分变量类型、区分作用域的意图上都是相似的:控制访问权限与管理变量命名。关于控制访问权限,在上述例子中,局部变量 y 的作用域仅限于 foo 方法内,若直接在外部使用,则会报错“NameError: name ‘y’ is not defined”。关于管理变量命名,不同的作用域管理着各自的独立的名册,一个作用域内的名字所指称的是唯一的对象,而在不同作用域内的对象则可以重名。修改上述例子:x = 1y = 1def foo(): y = 2 x = 2 print(“inside foo : x = " + str(x) + “, y = " + str(y))foo()print(“outside foo : x = " + str(x) + “, y = " + str(y))在全局作用域与局部作用域中命名了相同的变量,那么,打印的结果是什么呢?inside foo : x = 2, y = 2outside foo : x = 1, y = 1可见,同一个名字可以出现在不同的作用域内,互不干扰。那么,如何判断一个变量在哪个作用域内?对于嵌套作用域,以及变量名存在跨域分布的情况,要采用何种查找策略呢? Python 设计了命名空间(namespace) 机制,一个命名空间在本质上是一个字典、一个名册,登记了所有变量的名字以及对应的值。 按照记录内容的不同,可分为四类:局部命名空间(local namespace),记录了函数的变量,包括函数的参数和局部定义的变量。可通过内置函数 locals() 查看。在函数被调用时创建,在函数退出时删除。全局命名空间(global namespace),记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。可通过内置函数 globals() 查看。在模块加载时创建,一直存在。内置命名空间(build-in namespace),记录了所有模块共用的变量,包括一些内置的函数和异常。在解释器启动时创建,一直存在。命名空间包(namespace packages),包级别的命名空间,进行跨包的模块分组与管理。命名空间总是存在于具体的作用域内,而作用域存在着优先级,查找变量的顺序是:局部/本地作用域 –> 全局/模块/包作用域 –> 内置作用域。 命名空间扮演了变量与作用域之间的桥梁角色,承担了管理命名、记录名值对与检索变量的任务。无怪乎《Python之禅》(The Zen of Python)在最后一句中说:Namespaces are one honking great idea – let’s do more of those!——译:命名空间是个牛bi哄哄的主意,应该多加运用!3、看不见的客人名字(变量)是身份问题,空间(作用域)是边界问题,命名空间兼而有之。这两个问题恰恰是困扰着所有生灵的最核心的问题之二。它们的特点是:无处不在、层出不断、像一个超级大的被扯乱了的毛线球。 Python 是一种人工造物,它继承了人类的这些麻烦(这是不可避免的),所幸的是,这种简化版的麻烦能够得到解决。(现在当然是可解决的啦,但若人工智能高度发展以后呢?我看不一定吧。喵,好像想起了一个痛苦的梦。打住。)这里就有几个问题(注:每个例子相互独立):# 例1:x = x + 1# 例2:x = 1def foo(): x = x + 1foo()# 例3:x = 1def foo(): print(x) x = 2foo()# 例4:def foo(): if False: x = 3 print(x)foo()# 例5:if False: x = 3print(x)下面给出几个选项,请读者们思考一下,给每个例子选一个答案:1、没有报错2、报错:name ‘x’ is not defined3、报错:local variable ‘x’ referenced before assignment下面公布答案了:全部例子都报错,其中例 1 和例 5 是第一类报错,即变量未经定义不可使用,而其它例子都是第二类报错,即已定义却未赋值的变量不可使用。为什么会报错?为什么报错会不同?下面逐一解释。例 1 是一个定义变量的过程,本身未完成定义,而等号右侧就想使用变量 x,因此报变量未定义。例 2 和例 3 中,已经定义了全局变量 x,如果只在 foo 函数中引用全局变量 x 或者只是定义新的局部变量 x 的话,都不会报错,但现在既有引用又有重名定义,这引发了一个新的问题。请看下例的解释。例 4 中,if 语句判断失效,因此不会执行到 “x=3” 这句,照理来说 x 是未被定义。这时候,在 locals() 局部命名空间中也是没有内容的(读者可以试一下)。但是 print 方法却报找到了一个未赋值的变量 x ,这是为什么呢?使用 dis 模块查看 foo 函数的字节码:LOAD_FAST 说明它在局部作用域中找到了变量名 x,结果 0 说明未找到变量 x 所指向的值。既然此时在 locals() 局部命名空间中没有内容,那局部作用域中找到的 x 是来自哪里的呢?实际上,Python 虽然是所谓的解释型语言,但它也有编译的过程 (跟 Java 等语言的编译过程不同)。在例 2-4 中,编译器先将 foo 方法解析成一个抽象语法树(abstract syntax tree),然后扫描树上的名字(name)节点,接着,所有被扫描出来的变量名,都会作为局部作用域的变量名存入内存(栈?)中。 在编译期之后,局部作用域内的变量名已经确定了,只是没有赋值。在随后的解释期(即代码执行期),如果有赋值过程,则变量名与值才会被存入局部命名空间中,可通过 locals() 查看。只有存入了命名空间,变量才算真正地完成了定义(声明+赋值)。而上述 3 个例子之所以会报错,原因就是变量名已经被解析成局部变量,但是却未曾被赋值。可以推论:在局部作用域中查找变量,实际上是分查内存与查命名空间两步的。另外,若想在局部作用域内修改全局变量,需要在作用域中写上 “global x”。例 5 是作为例 4 的比对,也是对它的原理的补充。它们的区别是,一个不在函数内,一个在函数内,但是报错完全不同。前面分析了例 4 的背后原理是编译过程和抽象语法树,如果这个原理对例 5 也生效,那两者的报错应该是一样的。现在出现了差异,为什么呢?我得承认,这触及了我的知识盲区。我们可以推测,说例 5 的编译过程不同,它没有解析抽象语法树的步骤,但是,继续追问下去,为什么不同,为什么没有解析语法树的步骤呢?如果说是出于对解析函数与解析模块的代价考虑,或者其它考虑,那么新的问题是,编译与解析的底层原理是什么,如果有其它考虑,会是什么?这些问题真不可爱,一个都答不上。但是,自己一步一步地思考探寻到这一层,又能怪谁呢?回到前面说过的话,命名空间是身份与边界的集成问题,它跟作用域密切相关。如今看来,编译器还会掺和一脚,把这些问题搅拌得更加复杂。本来是在探问 Python 中的边界问题,到头来,却触碰到了自己的知识边界。真是反讽啊。(这一趟探知一个人工造物的身份问题之旅,最终是否会像走迷宫一般,进入到自己身份的困境之中?)4、边界内外的边界暂时把那些不可爱的问题抛开吧,继续说修身齐家治国平天下。想要把国治理好,就不得不面对更多的国内问题与国际问题。先看一个大家与小家的问题:def make_averager(): count = 0 total = 0 def averager(new_value): nonlocal count, total count += 1 total += new_value return total / count return averageraverager = make_averager()print(averager(10))print(averager(11))### 输出结果:10.010.5这里出现了嵌套函数,即函数内还包含其它函数。外部–内部函数的关系,就类似于模块–外部函数的关系,同样地,它们的作用域关系也相似:外部函数作用域–内部函数作用域,以及模块全局作用域–外部函数作用域。在内层作用域中,可以访问外层作用域的变量,但是不能直接修改,除非使用 nonlocal 作转化。Python 3 中引入了 nonlocal 关键字来标识外部函数的作用域,它处于全局作用域与局部作用域之间,即 global–nonlocal–local 。也就是说,国–大家–小家。上例中,nonlocal 关键字使得小家(内部函数)可以修改大家(外部函数)的变量,但是该变量并不是创建于小家,当小家函数执行完毕时,它并无权限清理这些变量。nonlocal 只带来了修改权限,并不带来回收清理的权限 ,这导致外部函数的变量突破了原有的生命周期,成为自由变量。上例是一个求平均值的函数,由于自由变量的存在,每次调用时,新传入的参数会跟自由变量一起计算。在计算机科学中,引用了自由变量的函数被称为闭包(Closure)。 在本质上,闭包就是一个突破了局部边界,所谓“跳出三界外,不在五行中”的法外之物。每次调用闭包函数时,它可以继续使用上次调用的成果,这不就好比是一个转世轮回的人(按照某种宗教的说法),仍携带着前世的记忆与技能么?打破边界,必然带来新的身份问题,此是明证。然而,人类并不打算 fix 它,因为他们发现了这种身份异化的特性可以在很多场合发挥作用,例如装饰器与函数式编程。适应身份异化,并从中获得好处,这可是地球人类的天赋。讲完了这个分家的话题,让我们放开视野,看看天下事。计算机语言中的包(package)实际是一种目录结构,以文件夹的形式进行封装与组织,内容可涵括各种模块(py 文件)、配置文件、静态资源文件等。与包相关的话题可不少,例如内置包、第三方包、包仓库、如何打包、如何用包、虚拟环境,等等。这是可理解的,更大的边界,意味着更多的关系,更大的边界,也意味着更多的知识与未知。在这里,我想聊聊 Python 3.3 引入的命名空间包 ,因为它是对前面谈论的所有话题的延续。然而,关于它的背景、实现手段与使用细节,都不重要,我那敏感而发散的思维突然捕捉到了一种相似结构,似乎这才更值得说。运用命名空间包的设计,不同包中的相同的命名空间可以联合起来使用,由此,不同目录的代码就被归纳到了一个共同的命名空间。也就是说,多个本来是相对独立的包,借由同名的命名空间,竟然实现了超远距离的瞬间联通,简直奇妙。我想到了空间折叠,一种无法深说,但却实实在在地辅助了我从喵星穿越到地球的技术。两个包,两个天下,两个宇宙,它们的距离与边界被穿透的方式何其相似!我着迷于这种相似结构。在不同的事物中,相似性的出现意味着一种更高维的法则的存在,而在不同的法则中,新的相似性就意味着更抽象的法则。学习了 Python 之后,我想通过对它的考察,来回答关乎自身的相似问题……啊喵,不知不觉竟然写了这么久,该死的皮囊又在咕咕叫了——地球上的食物可真抠门,也不知道你们人类是怎么忍受得住这几百万年的驯化过程的……就此搁笔,觅食去了。亲爱的读者们,后会有期~~~Python猫往期作品 :有了Python,我能叫出所有猫的名字Python对象的身份迷思:从全体公民到万物皆数Python对象的空间边界:独善其身与开放包容附录: 局部变量的编译原理:https://dwz.cn/ipj6FluJ命名空间包:https://www.tuicool.com/artic…公众号【Python猫】, 专注Python技术、数据科学和深度学习,力图创造一个有趣又有用的学习分享平台。本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、优质英文推荐与翻译等等,欢迎关注哦。PS:后台回复“爱学习”,免费获得一份学习大礼包。 ...

February 24, 2019 · 2 min · jiezi

个人博客四|注册登录退出功能后台开发

声明:本博客的注册登录退出功能将使用django-allauth,参考资源如下:django-allauth文档django-allauth教程1、安装django-allauthpip install django-allauth2、配置信息安装后设置blog/settings.py,将allauth相关APP加入到INSTALLED_APP里去。INSTALLED_APPS = [ ‘django.contrib.admin’, ‘django.contrib.auth’, ‘django.contrib.contenttypes’, ‘django.contrib.sessions’, ‘django.contrib.messages’, ‘django.contrib.staticfiles’, # <添加storm相关应用> ‘storm’, # <storm–end—> # <添加allauth相关应用> ‘django.contrib.sites’, ‘allauth’, ‘allauth.account’, ‘allauth.socialaccount’, ‘allauth.socialaccount.providers.github’, # <allauth–end—>]注意:allauth对于站点设置django.contrib.sites有依赖,你必需也把它加入进去,同时设置SITE_IDSITE_ID没必要深入了解,目前不涉及多站点。目前能涉及到的是当出现"SocialApp matching query does not exist"这种报错的时需要更换SITE_ID值3、allauth 基本设置# 多站点框架:# 位于django.contrib.sites的site。# SITE_ID指定与特定配置文件相关联的site对象之数据库的ID。# 当出现"SocialApp matching query does not exist",这种报错的时候就需要更换这个IDSITE_ID = 1# 设置登录和注册成功后重定向的页面,默认是/accounts/profile/LOGIN_REDIRECT_URL = “/”# Email setting# 禁用注册邮箱验证ACCOUNT_EMAIL_VERIFICATION = ’none’# 登录方式,选择用户名或者邮箱登录ACCOUNT_AUTHENTICATION_METHOD = “username_email”# 设置用户注册的时候必须填写邮箱地址ACCOUNT_EMAIL_REQUIRED = True# 登出直接退出,不用确认ACCOUNT_LOGOUT_ON_GET = True4、django-allauth常见设置选项你也可以添加其它设置选项来实现你所想要的功能, 比如设置邮件确认过期时间,限制用户使用错误密码登录的持续时间。# 指定要使用的登录方法(用户名、电子邮件地址或两者之一)ACCOUNT_AUTHENTICATION_METHOD (=“username” | “email” | “username_email”)# 邮件确认邮件的截止日期(天数)ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS (=3)# 注册中邮件验证方法:“强制(mandatory)”,“可选(optional)”或“否(none)”之一ACCOUNT_EMAIL_VERIFICATION (=“optional”)# 邮件发送后的冷却时间(以秒为单位)ACCOUNT_EMAIL_CONFIRMATION_COOLDOWN (=180)# 登录尝试失败的次数ACCOUNT_LOGIN_ATTEMPTS_LIMIT (=5)# 从上次失败的登录尝试,用户被禁止尝试登录的持续时间ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT (=300)# 更改为True,用户一旦确认他们的电子邮件地址,就会自动登录ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION (=False)# 更改或设置密码后是否自动退出ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE (=False)# 更改为True,用户将在重置密码后自动登录ACCOUNT_LOGIN_ON_PASSWORD_RESET (=False)# 控制会话的生命周期,可选项还有:False,TrueACCOUNT_SESSION_REMEMBER (=None)# 用户注册时是否需要输入邮箱两遍ACCOUNT_SIGNUP_EMAIL_ENTER_TWICE (=False)# 用户注册时是否需要用户输入两遍密码ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE (=True)# 用户不能使用的用户名列表ACCOUNT_USERNAME_BLACKLIST (=[])# 加强电子邮件地址的唯一性ACCOUNT_UNIQUE_EMAIL (=True)# 用户名允许的最小长度的整数ACCOUNT_USERNAME_MIN_LENGTH (=1)# 使用从社会帐户提供者检索的字段(如用户名、邮件)来绕过注册表单SOCIALACCOUNT_AUTO_SIGNUP (=True)# 设置登录后跳转链接LOGIN_REDIRECT_URL (="/") # 设置退出登录后跳转链接ACCOUNT_LOGOUT_REDIRECT_URL (="/") 5、配置allauth路由urlpatterns = [ url(r’^admin/’, admin.site.urls), # allauth url(r’^accounts/’, include(‘allauth.urls’)), # storm url(’’, include(‘storm.urls’, namespace=‘blog’)), # blog]6、运行效果可以访问哪个路由,取决于,blog/settings.py中allauth设置信息注册http://127.0.0.1:8080/accounts/signup/登录http://127.0.0.1:8080/accounts/login/7、django-allauth全部路由下面是django_allauth所有内置的URLs,均可以访问的。可以去allauth/account/urls.py查看# 登录/accounts/login/# 注册/accounts/signup/# 重置密码/accounts/password/reset/# 退出登录/accounts/logout/# 设置密码 /accounts/password/set/# 改变密码(需登录)/accounts/password/change/# 用户可以添加和移除email,并验证/accounts/email/# 管理第三方账户/accounts/social/connections/用户详细信息是没有的/accounts/profile/如果我希望用户在注册时提供更多信息(比如公司名、电话、住址等)如果用户在注册后需要修改个人信息怎么办?由于每个开发者对用户所需提供的额外信息需求是不一样的,所以没有提供这个视图和URL。因此django-allauth并没有提供用户详情应用用户详情请参考:[个人博客五|用户个人资料Profile扩展] ...

February 22, 2019 · 1 min · jiezi

django开发-d'jango和tornado的不同

python中常用的几个web框架有django, tornado, flask等,今天来总结一下django和tornado的不同。工作中django和tornado都用过,使用django相对更多一些。个人感觉django虽然好用,有搭建项目快、自带ORM、自动生成路由、自带管理后台等优势;但若实际工作中选择,我还是会偏向于使用tornado框架,因为torndo使用更加灵活,并且支持websocket,tcp等通信协议,最重要的是tornado是异步非阻塞的web框架;而在django中要实现websocket、异步非阻塞等功能则需要引入dwebsocket、celery等第三方模块。本文使用的环境是python3.6, django2.0, tornado5.1。下面主要从以下几个方面介绍一下这两个框架的不同:1.创建项目的方式2.数据库连接3.异步非阻塞请求4.websocket的使用1.项目创建方式1)djangodjango主要是通过下面两个命令创建项目:django-admin startproject Test # 创建项目,名称为Testdjango-admin startpapp Test01 # 创建app,名称为Test01执行完成后,会生成如下的目录结构:D:.│ manage.py│ test.txt│ ├─.idea│ │ misc.xml│ │ modules.xml│ │ Test.iml│ │ workspace.xml│ │ │ └─inspectionProfiles│ profiles_settings.xml│ ├─Test│ settings.py│ urls.py│ wsgi.py│ init.py│ └─Test01 │ admin.py │ apps.py │ models.py │ tests.py │ views.py │ init.py │ └─migrations init.py主要是manage.py,Test,Test01这几个文件和文件夹,manage.py是管理项目的文件,通过它运行django的一些内置命令,如模型迁移、启动项目等;Test/settings.py是配置文件,项目配置存放在这里Test/urls.py是路由文件,负责分发http请求Test01/models.py是模型文件,Test01下创建的模型就放在这里,模型负责将表结构映射到数据库中Test01/views.py是视图文件,django中的视图在这里定义Test01/migrations目录中存放迁移后生成的迁移文件。django项目的基本结构就是这样。2)tornadotornado项目的创建比较灵活,没有什么项目名称和app的概念,全靠自己组织项目,就是创建一个个python文件和python package。可以像下面一样来组织tornado项目:├── App│ ├── init.py│ ├── Shop│ │ ├── init.py│ │ └── views.py│ └── User│ ├── init.py│ └── views.py├── application.py├── Config│ ├── config_base.py│ ├── config_db.conf│ ├── config_db_get.py│ ├── config_engine.py│ ├── init.py├── Models│ ├── init.py│ ├── Shop│ │ └── init.py│ └── User│ ├── BaseClass.py│ ├── init.py│ └── UserModel.py├── server.py├── static│ └── init.py├── templates│ └── init.py├── test.py└── Urls ├── init.py ├── Shop.py └── User.py这里有几个主要文件App, Config, Models, Urls, static, templates, application.py, server.py。项目的app可以集中放在App目录中,与数据库对应的模型文件可以放在Models中,http路由可以放在Urls中,项目配置信息可以放在Config目录中,静态文件和模板分别放在static和templates中。application.py文件可以加载路由信息和项目配置信息,server.py文件负责启动项目。项目的基本配置信息可以放在Config/config_base.py中,如下:# coding=utf-8import osBASE_DIR = os.path.dirname(file)# 参数options = { “port”: 8001,}# 基本配置信息settings = { “debug”: True, “static_path”: os.path.join(BASE_DIR, “static”), “template_path”: os.path.join(BASE_DIR, “templates”)}路由信息可以放在Urls/User.py中,如下:# coding=utf-8from App.UserInfo import viewsuser_urls = [ (r’/user/’, views.IndexHandler),]application.py中加载路由信息和配置信息:# coding=utf-8from tornado import ioloop, httpserverfrom application import Applicationfrom Config import config_baseif name == ‘main’: app = Application() http_server = httpserver.HTTPServer(app) http_server.listen(config_base.options.get(“port”)) ioloop.IOLoop.current().start()2.数据库连接1)djangodjango中使用数据库时,首先要在settings.py中配置数据库信息:DATABASES = { ‘default’: { ‘ENGINE’: ‘django.db.backends.mysql’, # 数据库引擎 ‘NAME’: ‘django_test’, # 你要存储数据的库名,事先要创建之 ‘USER’: ‘root’, # 数据库用户名 ‘PASSWORD’: ’test’, # 密码 ‘HOST’: ’localhost’, # 主机 ‘PORT’: ‘3306’, # 数据库使用的端口 }}然后在每个app下编写完models.py后,执行以下两个命令后,就可以使用数据库了:python manage.py makemigrationspython manage.py migrate可以调用模型管理器对象objects的相应方法,执行增删改查等操作。2)tornado这里说一下在tornado中使用sqlalchemy连接数据库,需要安装sqlalchemy和pymysql。2.2.1)首先在Config/config_db.conf中配置数据库信息:[db_user]name = db_tornado03port = 3306user = roothost = 127.0.0.1pass = testpool_size = 32.2.2)然后在Config/config_engine.py中配置engine:# coding=utf-8from sqlalchemy import create_enginefrom Config.config_db_get import ConfigDBUser# 数据库配置信息 可以配置多个engine, 每个数据库对应一个enginedb_user = ConfigDBUser(“db_user”)engine_user = create_engine( “mysql+pymysql://%s:%s@%s:%d/%s” % ( db_user.get_db_user(), db_user.get_db_pass(), db_user.get_db_host(), db_user.get_db_port(), db_user.get_db_database() ), encoding=‘utf-8’, echo=True, pool_size=20, pool_recycle=100, connect_args={“charset”: ‘utf8mb4’})create_engine用来初始化数据库连接。2.2.3)在Models/UserInfo/BaseClass.py中配置连接数据库的session信息:# coding=utf-8from sqlalchemy.orm import scoped_session, sessionmakerfrom Config.config_engine import engine_userclass BaseClass: def init(self): # 创建session对象,并且用scoped_session维护session对象 # 数据库的增删改查通过session对象来完成 self.engine_user = scoped_session( sessionmaker( bind=engine_user, autocommit=False, autoflush=True, expire_on_commit=False ) )2.2.4)在Models/UserInfo/UserModel.py中配置模型信息,用于映射到数据库中对应的表:# coding=utf-8from sqlalchemy import Table, MetaDatafrom sqlalchemy.ext.declarative import declarative_basefrom Config.config_engine import engine_userBaseModel = declarative_base()def user_model(table_name): class UserModel(BaseModel): tablename = table_name metadata = MetaData(engine_user) Table(tablename, metadata, autoload=True) return UserModel配置模型信息前,需要在数据库中把表创建好,这是就需要写sql语句创建表了。对于熟练sql的同学,写sql语句应该不算什么;对应不熟悉sql的同学,可能更习惯于django中那种创建表的方式。2.2.5)以上都配置好以后,就可以在视图中使用了App/UserInfo/views.py:# coding=utf-8from tornado import webfrom Models.UserInfo.BaseClass import BaseClassfrom Models.UserInfo.UserModel import user_modelclass UserInfoHandler(web.RequestHandler, BaseClass): def get(self): """ 获取用户信息 :return: """ # user_model中的参数对应数据库中的表名 user_info = user_model(“user_info”) # 获取参数 user_id = self.get_query_argument(“id”) # self.engine_user其实就是一个session对象;query()方法会返回一个query.Query对象,通过这个对象查询数据库 user_info_obj = self.engine_user.query(user_info).filter(user_info.id==user_id).first() self.write(user_info_obj.name) self.finish()2.2.6)最后配置好url:Urls/UserInfo.py:# coding=utf-8from App.UserInfo import viewsuser_urls = [ (r’/userinfo’, views.UserInfoHandler),]application.py:# coding=utf-8from tornado import webfrom Config.config_base import settingsfrom Urls.UserInfo import user_urlsfrom Urls.Shop import shop_urls"““路由配置”““class Application(web.Application): def init(self): urls = user_urls + shop_urls super(Application, self).init(urls, **settings)启动服务后,就可以访问了。3.异步非阻塞请求1)djangodjango中可以通过celery来实现异步任务,也可以使用asyncio和aiohttp实现异步。下面讲一下celery的使用:3.1.1)首先需要安装 celery和 django-celery,使用pip安装就行了;3.1.2)然后在zsettings.py中进行如下配置:在INSTALLED_APPS中加入djcelery。import djcelery# Celery便会去查看INSTALLD_APPS下包含的所有app目录中的tasks.py文件,找到标记为task的方法,将它们注册为celery taskdjcelery.setup_loader()BROKER_URL = ‘redis://127.0.0.1:6379/2’CELERY_RESULT_BACKEND = ‘redis://127.0.0.1:6379/3’ # 或者使用rabbitmq: BROKER_URL = ‘amqp://test:test@192.168.173.1:5672/testhost’CELERY_RESULT_BACKEND = ‘amqp://test:test@192.168.173.1:5672/testhost'3.1.3)在需要使用异步的app中创建tasks.py文件,然后编辑该文件:# coding=utf-8import timefrom celery import task@taskdef test(data): "”” 预处理 :param data: :return: "”" time.sleep(3) return data耗时的任务就可以放在使用@task修饰的函数中3.1.4)在views.py中调用tasks.py中的函数from rest_framework.response import Responsefrom .tasks import testclass CeleryTrainView(APIView): def get(self, request): try: for i in range(0, 5): ret = test.delay(str(i)) print(“ret:”, ret) except Exception as e: return Response(dict(msg=str(e), code=10001)) return Response(dict(msg=“OK”, code=10000))上面的结果ret是一个AsyncResult对象,可以通过这个对象拿到保存在CELERY_RESULT_BACKEND中的结果。如果想立即得到结果,可以直接调用get()方法,但是这样就会阻塞其他请求,直到结果返回:from rest_framework.response import Responsefrom .tasks import testclass CeleryTrainView(APIView): def get(self, request): try: for i in range(0, 5): ret = test.delay(str(i)) print(“ret:”, ret.get()) except Exception as e: return Response(dict(msg=str(e), code=10001)) return Response(dict(msg=“OK”, code=10000))3.1.5)启动celery#先启动服务器python manage.py runserver#再启动worker python manage.py celery worker2)tornadotornado中实现异步有回调和协程这两种方式,这里只举一个协程实现异步的例子:from tornado import webfrom tornado import genfrom tornado.httpclient import AsyncHTTPClientclass AsyncHandler(web.RequestHandler): @gen.coroutine def get(self, *args, **kwargs): client = AsyncHTTPClient() url = ‘http://ip.taobao.com/service/getIpInfo.php?ip=14.130.112.24' # 根据ip地址获取相关信息 resp = yield client.fetch(url) data = str(resp.body, encoding=“utf-8”) print(“data:”, data) self.write(data) self.finish()或者像下面这样,把获取ip信息的部分封装成一个函数:from tornado import webfrom tornado import genfrom tornado.httpclient import AsyncHTTPClientclass AsyncHandler(web.RequestHandler): @gen.coroutine def get(self, *args, **kwargs): ip_info = yield self.get_ip_info() self.write(ip_info) self.finish() @gen.coroutine def get_ip_info(self): client = AsyncHTTPClient() url = ‘http://ip.taobao.com/service/getIpInfo.php?ip=14.130.112.24' resp = yield client.fetch(url) data = str(resp.body, encoding=“utf-8”) return data也可以同时发起多个异步请求:from tornado import webfrom tornado import genfrom tornado.httpclient import AsyncHTTPClientclass AsyncHandler(web.RequestHandler): @gen.coroutine def get(self, *args, **kwargs): ips = [ “14.130.112.24”, “14.130.112.23”, “14.130.112.22” ] info1, info2, info3 = yield [self.get_ip_info(ips[0]), self.get_ip_info(ips[1]), self.get_ip_info(ips[2])] self.write(info1) self.write(info2) self.write(info3) self.finish() @gen.coroutine def get_ip_info(self, ip): client = AsyncHTTPClient() url = ‘http://ip.taobao.com/service/getIpInfo.php?ip=' + ip resp = yield client.fetch(url) data = str(resp.body, encoding=“utf-8”) return dataAsyncHTTPClient的fetch()方法有两种调用方式,一种是像上面那样只传入一个url的字符串,另一种是接收一个HTTPRequest对象作为参数,像下面这样: @gen.coroutine def get_ip_info(self, ip): client = AsyncHTTPClient() url = ‘http://ip.taobao.com/service/getIpInfo.php?ip=' + ip header = {‘Accept’: ‘application/json;charset=utf-8’, ‘Content-Type’: ‘application/x-www-form-urlencoded;charset=utf-8’} param1 = ’test’ http_request = HTTPRequest(url=url, method=‘POST’, headers=header, body=urlencode({‘param1’: param1})) resp = yield client.fetch(http_request) data = str(resp.body, encoding=“utf-8”) return data4.websocket的使用1)djangodjango中使用websocket需要安装第三方包dwebsocket。2)tornadotornado中实现websocket功能需要用到tornado.websocket模块,主要有以下几个方法:open(), write_message(), on_message(), on_close()open(): 当websocket客户端连接时所做的操作write_message(): 使用这个方法向客户端发送消息on_message(): 接收并处理客户端的消息on_close(): websocket关闭连接时所作的操作下面看一个例子:views.py:from tornado import websocketclass IndexHandler(web.RequestHandler): def get(self, *args, **kwargs): self.render(“chat.html”)class ChatHandler(websocket.WebSocketHandler): clients = set() def open(self, *args, **kwargs): self.clients.add(self) for client in self.clients: client.write_message("%s上线了" % self.request.remote_ip) def on_message(self, message): for client in self.clients: client.write_message("%s: %s" % (self.request.remote_ip, message)) def on_close(self): self.clients.remove(self) for client in self.clients: client.write_message("%s下线了" % self.request.remote_ip) def check_origin(self, origin): """ 用于处理跨域问题 :param origin: :return: """ return True路由:# coding=utf-8from App.UserInfo import viewsuser_urls = [ (r’/index’, views.IndexHandler), (r’/chat’, views.ChatHandler),]chat.html:<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>聊天室</title></head><body> <div id=“content” style=“height: 500px;overflow: auto;"></div> <div> <textarea id=“msg”></textarea> <a href=“javascript:;” onclick=“sendMsg()">发送</a> </div> <script src=”{{ static_url(‘js/jquery.min.js’) }}"></script> <script type=“text/javascript”> var ws = new WebSocket(“ws://192.168.1.104:8001/chat”); ws.onmessage = function (data) { $("#content”).append("<p>"+ data.data +"</p>") }; function sendMsg() { var msg = $("#msg").val(); if (msg) { ws.send(msg); } } </script></body></html>上面一个例子通过websocket实现了简单的聊天室功能。以上就简单的比较了django和tornado几个方面的不同,它们各有优缺点,实际工作中可以根据不同的需求选择不同的框架进行开发。如果想了解如何在tornado中使用tcpserver,可以看一下这篇博客:tornado中tcpserver和tcpclient的使用 ...

February 22, 2019 · 4 min · jiezi

个人博客-首页后台开发(一)

声明:本渣渣部分代码参考自TendCode其实有很多代码是不需要自己一行行码出来,生产力是第一位。只有研究型人才需要生产代码,作为一名渣渣拿来用是最高效的做法。程序员都有一个开源的精神,码出来的代码本身是希望更多的人用到,应用到生产中。参考编写博客的 Model 与首页面整合资源也应该是一个码农的必备能力,在实际生产中可以更高效的工作项目环境windows10python==3.6.6django==1.11.1首先本渣渣想对django项目结构做一下梳理,如果有的读者对django不太熟悉,可能阅读有障碍。对django基础做一下简单了解。推荐阅读:Django1.10 中文文档大牛翻译的django基础部分简单了解通用视图开始 Django 开发之前,需要掌握以下知识:• 创建 Django 工程,了解 Django 默认的工程目录结构• 创建 Django APP• 理解 Django 的MTV 模式,学会编写 Model、View、Template• Django 如何处理静态文件,即各种 CSS,JS,以及图片文件等推荐阅读:个人博客-创建项目Django中MVC与MVT设计模式Django的工作流程1、 用户通过浏览器输入相应的 URL 发起 HTTP 请求(一般是 GET/POST)用户在网站前台做的每次操作都是一次HTTP请求的触发,通过触发的URL,和后台进行交互2:Django后台接收到请求,检测 urls.py 文件,找到和用户请求的URL 相匹配的项,然后调用该 URL 对应的视图函数(view),在视图内进行逻辑呈现,然后渲染数据到模板展示给用户。推荐阅读:静态路由和动态路由视图函数被调用后,可能会访问数据库(Model)去查询用户想要请求的数据,并加载模板文件(Template),渲染完数据后打包成 HttpResponse 返回给浏览器(Http协议)从流程可以看出,开发者需要做的事情如下:• 编写相应的 url• 编写数据库(Model)• 编写处理 Http 请求的视图函数(View)• 编写需要渲染数据的模板(Template)下面遵循这样的开发流程继续进行我们个人博客的开发:虽然博客站点的django项目已经烂大街了,但是很多人还没意识到个人博客的价值,有时间我们可以谈谈这个问题。第一版选择使用Django通用视图开发,后续升级前后端分离开发(正在学习中)推荐阅读:Django通用视图ListView 和 DetailView在接触到django到现在看到的教程大都没有采用django的通用视图,但是为了更好的认识django,在这里本渣渣,想尝试尽量在开发博客主体时使用通用视图为什么选择使用通用视图1、 目前在看到的django教程中大部分都未使用通用视图,顶多是继承通用类。这样你就没办法真正了解django框架,django独特之处就在于构建Web应用程序的常用功能已经在框架内,而不是单独的库。通用视图是Django为解决建站过程中的常见的呈现模式而建立的通用视图使用原则代码越少越好永远不要重复代码view应当只呈现逻辑, 不应包含业务逻辑保持view逻辑清晰简单2、 锻炼快速高效开发一类网站的能力了解了通用视图你可以快速开发一类网站比如博客、论坛都有相似的需求3、 组合方便这里本渣渣的做法是扒取崔庆才个人博客前端源码,然后通过django实现网站后端开发,使用django 通用视图,可以缩短开发周期,而且能够具备使用django快速模仿一个网站的能力下面不废话开干按流程来,规范的流程少出错,严谨应该是一个码农的素养目前项目文件结构1、前端把抓取崔庆才个人博客网站前端源码一文中抓取的崔庆才博客前端源码放入个人博客-创建项目一文中创建的项目中https://www.jianshu.com/p/246…2、配置基本信息设置blog/settings.py添加 apps 目录 sys.path.insert(0, os.path.join(BASE_DIR, ‘apps’))添加app# Application definitionINSTALLED_APPS = [ ‘django.contrib.admin’, ‘django.contrib.auth’, ‘django.contrib.contenttypes’, ‘django.contrib.sessions’, ‘django.contrib.messages’, ‘django.contrib.staticfiles’, ‘storm’,]添加模板ROOT_URLCONF = ‘blog.urls’TEMPLATES = [ { ‘BACKEND’: ‘django.template.backends.django.DjangoTemplates’, ‘DIRS’: [os.path.join(BASE_DIR, ’templates’)], # 设置视图 ‘APP_DIRS’: True, ‘OPTIONS’: { ‘context_processors’: [ ‘django.template.context_processors.debug’, ‘django.template.context_processors.request’, ‘django.contrib.auth.context_processors.auth’, ‘django.contrib.messages.context_processors.messages’, ], }, },]连接数据库,首先要在本地数据库创建名为blog的数据库这里需要用到数据库连接包,请参考安装python包到虚拟环境安装包Name: mysqlclientVersion: 1.4.2.post1DATABASES = { ‘default’: { ‘ENGINE’: ‘django.db.backends.mysql’, ‘HOST’: ’localhost’, ‘PORT’: ‘3306’, ‘USER’: ‘root’, ‘PASSWORD’: ‘root’, ‘NAME’: ‘blog’, # 如果创建的数据库是uft-8编码,映射数据库时出现警告,添加此行解决问题 ‘OPTIONS’: { ‘init_command’: “SET sql_mode=‘STRICT_TRANS_TABLES’”, ‘charset’: ‘utf8mb4’, }, }}配置静态文件路径STATIC_URL = ‘/static/‘STATICFILES_DIRS = [ os.path.join(BASE_DIR, ‘static’)]model.py编写针对blog开发大体有这几个通用的models类文章、幻灯片、文章标签、文章分类、友情链接这里为了后期能够更好的给本站做SEO优化,这里添加了一个,文章关键词,死链文章、幻灯片、文章标签、文章分类、友情链接、关键词、死链由此可见,设计数据库结构就是编写 models,数据库中每一个实体对应的表在 django 中对用着 models.py 中的一个类,类的属性对应着数据库表的属性列。# 文章关键词,用来作为SEO中keywordsclass Keyword(models.Model): name = models.CharField(‘文章关键词’, max_length=20) class Meta: verbose_name = ‘关键词’ verbose_name_plural = verbose_name ordering = [’name’] def str(self): return self.name# 文章标签class Tag(models.Model): name = models.CharField(‘文章标签’, max_length=20) slug = models.SlugField(unique=True) description = models.TextField(‘描述’, max_length=240, default=settings.SITE_DESCRIPTION, help_text=‘用来作为SEO中description,长度参考SEO标准’) class Meta: verbose_name = ‘标签’ verbose_name_plural = verbose_name ordering = [‘id’] def str(self): return self.name def get_absolute_url(self): return reverse(‘blog:tag’, kwargs={‘slug’: self.slug}) def get_article_list(self): ‘‘‘返回当前标签下所有发表的文章列表’’’ return Article.objects.filter(tags=self)# 文章分类class Category(models.Model): name = models.CharField(‘文章分类’, max_length=20) slug = models.SlugField(unique=True) description = models.TextField(‘描述’, max_length=240, default=settings.SITE_DESCRIPTION, help_text=‘用来作为SEO中description,长度参考SEO标准’) class Meta: verbose_name = ‘分类’ verbose_name_plural = verbose_name ordering = [’name’] def str(self): return self.name def get_absolute_url(self): return reverse(‘blog:category’, kwargs={‘slug’: self.slug}) def get_article_list(self): return Article.objects.filter(category=self)# 文章class Article(models.Model): # 文章缩略图 IMG_LINK = ‘/static/img/summary.png’ author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=‘作者’) # 文章标题,CharField 表示对应数据库中数据表的列的数据类型,‘标题’是一个位置参数 #(verbose_name),主要用于 django 的后台系统。max_length 表示能存储的字符串 # 的最大长度 title = models.CharField(max_length=150, verbose_name=‘文章标题’) # 这里填的内容有助于后期SEO优化 summary = models.TextField(‘文章摘要’, max_length=230, default=‘文章摘要等同于网页description内容,请务必填写…’) # 文章正文,TextField 用来存储大文本字符 body = models.TextField(verbose_name=‘文章内容’) # 文章缩略图地址 img_link = models.CharField(‘图片地址’, default=IMG_LINK, max_length=255) # 文章创建时间,DateTimeField用于存储时间,设定auto_now_add参数为真,则在文章被创建时会自动添加创建时间 create_date = models.DateTimeField(verbose_name=‘创建时间’, auto_now_add=True) # 文章最后一次编辑时间,auto_now=True表示每次修改文章时自动添加修改的时间 update_date = models.DateTimeField(verbose_name=‘修改时间’, auto_now=True) # 阅览量,PositiveIntegerField存储非负整数 # views = models.PositiveIntegerField(‘阅览量’, default=0) views = models.IntegerField(‘阅览量’, default=0) slug = models.SlugField(unique=True)# 文章的分类,ForeignKey即数据库中的外键。外键的定义是:如果数据库中某个表的列的值是另外一个表的主键。外键定义了一个一对多的关系,这里即一篇文章对应一个分类,而一个分类下可能有多篇文章。详情参考django官方文档关于ForeinKey的说明,on_delete=models.SET_NULL表示删除某个分类(category)后该分类下所有的Article的外键设为null(空) category = models.ForeignKey(Category, verbose_name=‘文章分类’,on_delete=models.SET_NULL) tags = models.ManyToManyField(Tag, verbose_name=‘标签’) keywords = models.ManyToManyField(Keyword, verbose_name=‘文章关键词’, help_text=‘文章关键词,用来作为SEO中keywords,最好使用长尾词,3-4个足够’) def str(self): # 主要用于交互解释器显示表示该类的字符串 return self.title class Meta: verbose_name = ‘文章’ verbose_name_plural = verbose_name ordering = [’-create_date’]# 幻灯片class Carousel(models.Model): number = models.IntegerField(‘编号’, help_text=‘编号决定图片播放的顺序,图片不要多于5张’) title = models.CharField(‘标题’, max_length=20, blank=True, null=True, help_text=‘标题可以为空’) content = models.CharField(‘描述’, max_length=80) img_url = models.CharField(‘图片地址’, max_length=200) url = models.CharField(‘跳转链接’, max_length=200, default=’#’, help_text=‘图片跳转的超链接,默认#表示不跳转’) class Meta: verbose_name = ‘图片轮播’ verbose_name_plural = verbose_name # 编号越小越靠前,添加的时间越晚越靠前 ordering = [’number’, ‘-id’] def str(self): return self.content[:25]# 死链class Silian(models.Model): badurl = models.CharField(‘死链地址’, max_length=200, help_text=‘注意:地址是以http开头的完整链接格式’) remark = models.CharField(‘死链说明’, max_length=50, blank=True, null=True) add_date = models.DateTimeField(‘提交日期’, auto_now_add=True) class Meta: verbose_name = ‘死链’ verbose_name_plural = verbose_name ordering = [’-add_date’] def str(self): return self.badurlclass FriendLink(models.Model): name = models.CharField(‘网站名称’, max_length=50) description = models.CharField(‘网站描述’, max_length=100, blank=True) link = models.URLField(‘友链地址’, help_text=‘请填写http或https开头的完整形式地址’) logo = models.URLField(‘网站LOGO’, help_text=‘请填写http或https开头的完整形式地址’, blank=True) create_date = models.DateTimeField(‘创建时间’, auto_now_add=True) is_active = models.BooleanField(‘是否有效’, default=True) is_show = models.BooleanField(‘是否首页展示’, default=False) class Meta: verbose_name = ‘友情链接’ verbose_name_plural = verbose_name ordering = [‘create_date’] def str(self): return self.name def get_home_url(self): ‘‘‘提取友链的主页’’’ u = re.findall(r’(http|https://.?)/.?’, self.link) home_url = u[0] if u else self.link return home_url def active_to_false(self): self.is_active = False self.save(update_fields=[‘is_active’]) def show_to_false(self): self.is_show = True self.save(update_fields=[‘is_show’])model 定义完毕后,运行以下命令即可生成相应的数据库表:python manage.py makemigrationspython manage.py migrate针对models分别给出以下参考资料供学习:Models:官方文档Django1.10 中文文档大牛翻译推荐学习:自强学堂Django的ORM映射机制与数据库实战数据库模型(Models)对象映射表ORMfield(字段选项):官方文档个人技术博文ForeinKey(一对多)、ManyToMany(多对多)、OneToOne(一对一):官方文档Django的ORM映射机制与数据库实战View编写上面已经介绍了 django 应用的工作流程,数据库建立完毕后需要编写视图函数(view)来处理 Http 请求。同样先来看 django 的 view 代码是如何写的,这里数据库内没有文章先不做文章展示逻辑编写,待后续一一进行,这里只写一个视图让首页可以被访问。视图代码from django.views import genericfrom django.conf import settingsfrom .models import Article, Tag, Category, Timeline, Silian# Create your views here.class IndexView(generic.ListView): """ 首页视图,继承自ListVIew,用于展示从数据库中获取的文章列表 """ # 获取数据库中的文章列表 model = Article # template_name属性用于指定使用哪个模板进行渲染 template_name = ‘index.html’ # context_object_name属性用于给上下文变量取名(在模板中使用该名字) context_object_name = ‘articles’配置路由blog/urls.pyfrom django.conf.urls import url, includefrom django.contrib import adminurlpatterns = [ url(r’^admin/’, admin.site.urls), # allauth url(r’^accounts/’, include(‘allauth.urls’)), # storm url(’’, include(‘storm.urls’, namespace=‘blog’)), # blog]apps/storm/urls.py# —————————author = ‘stormsha’date = ‘2019/2/20 23:27’# —————————from django.conf.urls import url# from .views import goviewfrom .views import IndexViewurlpatterns = [ url(r’^$’, IndexView.as_view(), name=‘index’), # 主页,自然排序]运行效果现在首页流程基本走通之,差视图内的逻辑、和数据渲染的问题了,静待后续。总结本渣渣也是在繁忙的工作之余,抽出点时间想自己搞一下博客,但是不想草草了事,想从这个项目中实践如下知识抓取网站前端页面源码、Django通用视图、django rest framework、vue.js、redis数据缓存、docker容器部署、git、SEO技术钱途:想法——实现——部署——运营——维护——盈利有什么问题欢迎留言,或者关注微信公众号「stormsha」进行深度技术、思维碰撞交流。 ...

February 22, 2019 · 3 min · jiezi

安装python包到指定虚拟环境

首先需要阅读《创建Python虚拟环境——下》《创建Python虚拟环境——下》创建虚拟环境1、 打开虚拟环境workon venv(自己创建的虚拟环境名称)2、 在这里使用pip安装python包直接安装pip install 包名使用国内镜像安装python包国内镜像:清华:https://pypi.tuna.tsinghua.ed…阿里云:http://mirrors.aliyun.com/pyp…中国科技大学 https://pypi.mirrors.ustc.edu…华中理工大学:http://pypi.hustunique.com/山东理工大学:http://pypi.sdutlinux.org/豆瓣:http://pypi.douban.com/simple/使用演示pip install -i https://pypi.tuna.tsinghua.ed… 包名如果pip安装出错时可以选择离线安装python离线包:http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml打开离线包网站Ctrl+f 在浏览器的搜索框输入要安装的包名,找到他下载他安装 pip install 下载的离线包位置使用演示pip install D:chrome下载tensorflow-1.9.0-cp36-cp36m-win_amd64.whl完毕

February 21, 2019 · 1 min · jiezi

django数据库自动重连

简介Django数据库连接超过wait_timeout导致连接丢失时自动重新连接数据库https://github.com/zhanghaofe…安装pip install django_db_reconnect注意仅支持pymysql,使django使用pymysql需要先安装包并在settings.py所在目录的__init__.py增加如下代码:import pymysqlpymysql.install_as_MySQLdb()使用添加django_db_reconnect到settings.py的INSTALLED_APPSINSTALLED_APPS = ( # 省略其他配置 ‘django_db_reconnect’,)其他问题事务或者其他autocommit=False非自动提交情况下将不会自动重连,否则可能导致连接丢失前的写入没有commit被丢弃

February 20, 2019 · 1 min · jiezi

个人博客-创建项目

Django是一种基于Python的开源Web框架,采用了MVC的框架模式。工作区 D:Userswork1、创建虚拟环境mkvirtualenv stormsha2、安装django==1.11.12(本项目计划使用此版本开发)pip install django==1.11.123、创建django项目django-admin startproject blog4、启动项目cd blog 进入下项目文件默认(127.0.0.1:8000)启动python manage.py runserver指定8080端口启动python manage.py runserver 127.0.0.1:80805、访问 http://127.0.0.1:8080/ 6、创建第一个app7、完整的项目文件8、在项目文件夹下创建一个apps文件把storm文件放入apps,之后所有app将放于次文件夹便于管理9、扯淡最近考研结果陆续已经公布,身边很多同学都参见了二战,都在讨论怎么调剂的问题。在这里想说一下本渣渣去年目标北邮差10+分落榜失败的感受。首先我考研的目的是明确的,就是为了提升自的圈层,想在人生第一阶段再赌一把,没考上北邮直接毕业奔上海找工作。如果在考研的你们如果真正搞清自己考研的目的,就不会出现二战、怎么调剂的困惑。首先在考研前就应该想好自己是否适合考研,这个别人给不了建议,自己对自己的评估要诚实。其次要确认自己考研的目的,不要为了上学而考研,这是在逃避现实。虽然本渣渣考研失败了,也要说如果考上研还不能认清自己,读研也是浪费时间。考上研就应该对自己进行第二次评估,是否适合搞科研,要对自己诚实。如果要搞科研,最多给自己一年的时间在研一做出成绩,如果没有成绩,就不要再为了逃避现实而自欺欺人的走下去。有时间可以去看一下崔庆才的个人博客,观摩一下一个对自己有清晰规划的研究生都在干些什么。

February 20, 2019 · 1 min · jiezi

抓取崔庆才个人博客网站前端源码

1、准备工具:仿站小工具+V9.0工具获取方式一: 关注微信公众号微信公众号『stormsha』,后台回复『仿站工具』获取工具工具获取方式二: 仿站小工具官网https://smalltool.github.io/崔庆才博客https://cuiqingcai.com/从网站源码来看此博客应该是使用的wordpress框架,原站用什么写的不重要,重要的是快速使用python实现全栈开发2、开扒下载工具后,解压直接打开exe程序,按如下步骤抓取即可3、扒取结果4、整理新建一个templates文件把扒下的除static文件之外,其它文件中的html文件都放入templates文件中,把static文件和templates放于同级目录,结果5、发现什么了吗?django项目文件: static、templates把index.html打开看一下效果,完美运行6、总结:学习python只有两个方向是最好找工作的,python web、python爬虫,之前我也在《让每行代码产生价值》一文中提到,如果你是因为数据科学的兴起才有了学习python的想法,那你错了,你应该去学习数据科学。对于数据科学而言python只是一个工具。如果你没有一个好的学历背景,学习python数据科学仍然是没有什么竞争力的。学习python web和python爬虫你可以从事数据科学相关工作,比如数据可视化、数据分析、数据抓取等。学习python web只有具备全栈开发能力才有竞争力。只会python你就是一块砖,搬到哪里都一样,会全栈开发你就是一个设计师,其实也不需要全栈,会点python web 加 爬虫就可以搞很多有意思的事情了。如果想快速入门python全栈那基本是不现实。按我的思路来,快速学习找到一份工作问题不大。扒取网站前端的源码应该是一个学习python web开发人员必备的技能,具备了此能力你可以快速搭建起一个网站。学习python就应该从实际需求出发去学习。接下来我会把崔大佬的个人博客网站使用django实现。最终部署上线,一个人的精力有限,计划每天在工作之余花费四个小时左右一点点把这个项目完善,后期我会把项目放在github上,每天进步一点点。今天看到的一段鸡汤文:如何评估一个人的职业化程度?①、时间感强、时间颗粒度小,最大限度地提升自己的时间价值。②、能够分清事实与观点,只用实力佐证事实,而不用情绪陈述观点。关注公众号结识更多python伙伴

February 18, 2019 · 1 min · jiezi

Django-Signals信号量

信号量最为Django的一个核心知识点,在项目中很少有使用到,所以很多人都不了解或者没听过过(包括我)。简单来说就是在进行一些操作的前后我们可以发出一个信号来获得特定的操作,这些操作包括(信息来自:https://yiyibooks.cn/xx/Djang…:django.db.models.signals.pre_save&django.db.models.signals.post_save在模型 save()方法调用之前或之后发送。django.db.models.signals.pre_delete&django.db.models.signals.post_delete在模型delete()方法或查询集的delete() 方法调用之前或之后发送。django.db.models.signals.m2m_changed模型上的 ManyToManyField 修改时发送。django.core.signals.request_started&django.core.signals.request_finishedDjango开始或完成HTTP请求时发送。其他细致的知识点,大家可以点链接查看,直接通过一个例子解释:在自定义用户模型类的时候,在后台添加用户数据因为使用了自定义模型类的create所以密码会以明文保存,接下来使用信号量方式在保存后马上修改密码解决。(网上一个项目的例子)users/signals.pyfrom django.db.models.signals import post_savefrom django.dispatch import receiverfrom django.contrib.auth import get_user_modelUser = get_user_model()# post_save:上面七大方法之一:在模型保存之后的操作# sender: 发出信号的model@receiver(post_save, sender=User)def create_user(sender, instance=None, created=False, **kwargs): """ sender:模型类。 instance:保存的实际实例。 created:如果创建了新记录True。 update_fields:Model.save()要更新的字段集,如果没有传递则为None """ if created: password = instance.password # instance相当于user instance.set_password(password) instance.save()users/apps.pyfrom django.apps import AppConfigclass UsersConfig(AppConfig): name = ‘users’ verbose_name = ‘用户管理’ def ready(self): “““使用ready加载,否则不生效””” import users.signals

February 18, 2019 · 1 min · jiezi

让敲过的每行代码产生价值

勤学似春起之苗,不见其增,而日有所长如果你一直想学python,但是不知道从何入手,那么就不要犹豫了。这篇文章就是我的python新实战计划可能适合你。学习 Python ,不要追求系统,更不要舍本逐末死学知识。采用“快速掌握核心知识+解决实际问题中补充”的方式。去看那些入门到精通的课程必然是入门即放弃。人和人的差别是和惰性对抗的程度,如果你认为自己有极强的自律能力,那就请绕道而行,这里会浪费你的时间。python最近的火是大数据、人工智能带起来的。如果你不去看清实质,盲目的去学习python的大数据和人工智能那你不应该学习python,应该去学习数据科学和算法,也请绕道而行,推荐去看[玉树芝兰]的知乎专栏。这里是一群想通过学习python网站开发,python爬虫得到一份offer的小伙伴。如你有一个大厂梦,那也请绕道而行,你应该去好好学习考个优秀的211或者985本科或者研究生。Hello、兄弟。如果你还在,偷偷的告诉你通过技术也有可能实现大厂梦,只要你肯坚持努力。嘿嘿、咱们算的上志同道合,想用python搞点事情的小伙伴,欢迎结伴同行如今的python在2017年底伙同比特币一起飞上了天。随着python的火爆想必很多企业也就开始了解关注python,由于python开发项目周期短成本较低,如果公司内部需要一个管理系统使用python开发那简直不要太好,而且很多互联网公司应该对数据或者自动化测试都有需求,那么python爬虫以及selenium简单又好用。针对一些公司可能存在的需求,本渣渣将着手两个方面的进阶学习,希望实现大厂梦:django、爬虫针对这两块如果需要实战项目学习资源请阅读《python自学之路》,不建议没有python、django基础的直接实战,这样会遇到很多问题,一直处在找bug的路上,很难进步,最终导致放弃。还是需要先看一下视频教程跟着做一下,再来看别人的实战项目自己来实战,最好是自己根据需求来实战。让每一行代码都能给自己带来价值,才是每个码农应该有的思维,而不是成为一个职业码农。一、个人博客搭建1、首先在网上找到一个自己心仪的个人博客网站,扒下网站前端源码目标已经锁定崔大才子的博客门户,项目上线时希望能认识一下崔大才子,让我在他的博客中发一篇文章,庆祝项目完工。2、使用django搭建网站后台3、给博客扩展一些功能(需求推动、成果实践)4、python爬虫(推荐学习催才子的笔记)5、创建公众号聚集志同道合的python开发者二、部署上线vue.js 前后端分离redis 页面数据缓存docker 容器部署上线坚持后期根据进度把每个过程写成文章发布于微信公众号『stormsha』和 segmentfault大家接触过django的应该都知道,网上有很多写个人博客的教程,可能有的人认为很low,本渣渣不这么认为。做网站需要关注的是django框架的学习、技术的应用,做什么不重要。不过做个人博客有一个重要的价值便是在你做成之后可以部署上线让它成为自己在互联网的基地。也可以在后续的python学习中给自己的博客扩展功能,比如学习爬虫时可以把自己爬到的有价值的东西做成可视化展示在自己的博客上,或者学习文件操作时可以在自己的网站上加入个文件格式转换的功能,每一块的学习都有实际的产值才是不断学习前进的源泉。如果你再了解一点SEO知识你的网站可能还会给你带来一定的收益可以看一下这个网站[https://tool.lu/], 这些工具完全可以在学习python的路上把它实现了,如果你学习相应的知识时,又用django把它给放在互联网上,这不就有可能让你敲过的每行代码给你带来收益。不废话,加油干赢取白富美希望大家一起进步,催促我坚持,如果发现本渣渣偷懒托更请炮轰评论区。

February 17, 2019 · 1 min · jiezi

为什么要坚持写技术博文

图片描述写作能力,就是一个人的隐形财富,我最喜欢的一个文化类节目《圆桌派》,更是让我明白了,一个人只要还有一只笔在就不会是最穷的。自己特别喜欢听这些老腊肉聊天。昨天,我忙碌了将近四个小时写的一篇《python自学之路》,发布后八个小时就因为内容侵权违规(主要是分享了一些网课资源)被举报,已经被删除。刚开始通过写作记录自己的学习、分享知识,就给了当头一棒。好了言归正传。本渣渣经历过昨天删文的事件后,想谈谈自己《第一个错失》《为什么要写文章》、《如何坚持写下去》一、为什么要写文章1、第一个错失——通过上学改变命运在高二时,我的一个本姓堂侄大学毕业(北京对外经贸大学韩语专业),但是经人推荐直接进入中信建投工作,现在已经是一个带领10人团队的股权并购主管、业务基本都是对接公司高管。一个语言专业毕业的学生,从事了金融行业,而且从事的还是一级市场。他让我真切的意识到一个学历的重要性。不要相信有些人说能力是一个人立业的根本。我这个本姓堂侄的成功引发了我的一些思考。什么是能力?我认为能力分为两块:学习能力、专业能力学历主要证明你的学习能力、在某个领域的成功主要证明你的专业能力。①、学习能力上学是一个底层人改变命运的第一个平台也是最重要的平台,只要能坚持千万不要放弃。就像我这个本姓堂侄,他如果不是有名牌大学的背景,一个学习语言专业的能进入权贵聚集的国企中信建投,可以说没有学历证明你的学习能力那是不可能的。经过人生的第一个平台你就已经被划分社会层次。当初本渣渣在高二时就意识到了圈层的重要性,我处在高考大省河南,高一时浑浑噩噩,高二经过自己的努力,根据高二一年的综合成绩高三进入了加强班,进入加强班基本保一本冲211、985。其实我当时清楚自己的水平,由于自己存在偏科,语文和英语极差,高二全年的策略就是放养这两个,数学和物理一般般,主要抓住自己的优势科目生物和化学,每次保证这两科大考进全校前100,班级前三,加强班的人选只看全年综合成绩不看单科,最终如愿进入加强班,进入加强班后又通过单科领跑第一次大考获得了优先选位权。我进入加强班的目地是改变圈层,拿到选位权尤为重要。既然进入了加强班,那么就要改变一下自己的高考目标。最初本渣渣的高考目标是根据自己的成绩选择一个计算机学院(不考虑专业),现在由于学习环境的改变圈层的变化,可以改变一下自己的选择范围。如果能报考211就选择金融专业、如果能报考985选择经济学院、如果211、985都没戏就选择任何一个可选择的计算机学院,最终本渣渣还是只能选择计算机学院。虽然没有成功走进好的学府,但是这种通过进入一定的圈层改变自己的思维是没错的,也算是一种社会生存能力(以结果为唯一导向的努力方式)。②、专业能力本渣渣认为,能力是广泛的,能力没有大小只有差别,这个差别是由圈层决定的,你第一个平台获得的成绩决定了你的圈层。不要扯什么马云,茫茫人海中你可能成为哪一个人,但是要始终告诉自己哪个人不是你。就像我这个本姓堂侄,一个语言专业懂金融吗?他压根没金融专业背景,这就是学历的重要性,只要有了高学历未来的选择会更加广泛,选择了行业后专业能力是可以从实践中获得的。本渣渣学习编程也是需求推动学习,这样就不会被一门编程语言的庞杂基础知识给堵在门外,这种方法在python学习中更为有效,python有强大的第三方开源库,短时间是学不完的。学习初期只需要深耕一个方向即可,其它都可以需求推动学习。为什么要写文章前边也提到了本渣渣的写作能力极差,高考语文89分考出了一个物理应该有的成绩。但是在互联网行业有两个需要思考的问题:技术、产品怎么检测自己适合技术,还是产品。如果一门编程语言你能坚持学习基础知识两个月不失热情,那么恭喜你大概率能成为技术大牛。如果你学习任何一门语言都是需求推动,对用户需求敏感、对生活有深入思考。如果再有一个做出一款改变人们生活方式的产品梦想,那么恭喜,你大概率和本渣渣有同样的梦想,一起努力成为一个懂技术的产品人。由于对自己的定位清晰,不得不在学习python的同时关注新媒体现在写文章主要是记录自己每天的进步,分享python知识,认识更多努力进步的人提升自己的圈层,促进自己进步。如何坚持下去这是个头疼的问题,但是我又仔细思考了一下。为什么并不是每个人都热爱工作,但是每个人都在坚持工作呢?主要的原因是为了生存,不过现在的我,通过工作也并不愁吃喝。不过定神一想目前本人单身,如果赚的钱只够吃喝什么时候能娶上媳妇呢?所以我要把我的文章当作赚取彩礼钱的工作来做,不想做也得咬牙做。图片描述哈哈,开个玩笑,其实我写文章的终极目的还是认识更高圈层的人,因为我现在的圈层给我带来不了任何养分,年前我和几个学习python的同学说了一个我思考已久的项目,想合作开发但是没有一个人有想法,这让我明白了,有的时候当一个人的思维已经超出自己能力范围时,很难和周围的人交流。所以之后我要坚持每天在这里记录我的学习与进步让编程能跟上自己的思维,同时希望能遇到一些不安于现状的小伙伴们一起前进交流亦或是合作。微信公众号『stormsha』

February 17, 2019 · 1 min · jiezi

[译]PEP 380--子生成器的语法

导语: PEP(Python增强提案)几乎是 Python 社区中最重要的文档,它们提供了公告信息、指导流程、新功能的设计及使用说明等内容。对于学习者来说,PEP 是非常值得一读的第一手材料,学习中遇到的大部分难题,都能在 PEP 中找到答案或者解决思路。我翻译了几篇 PEP,这么做的目的一方面是为了加强学习,另一方面也是为了锻炼自己的英文水平。Python 与 English,都是如此重要。翻译能将两者巧妙地结合起来,真是一举两得。本文介绍了子生成器的语法,即 yield from 语法。其它与生成器相关的 PEP 有 3 篇,翻译的结果附在了本文末尾。若有对翻译感兴趣的同学,可在 Github 上关注下我创建的项目 peps-cn 。PEP原文 : https://www.python.org/dev/pe...PEP标题: Syntax for Delegating to a SubgeneratorPEP作者: Gregory Ewing创建日期: 2009-02-13合入版本: 3.3译者 :豌豆花下猫(Python猫 公众号作者)目录摘要PEP接受动机提议StopIteration 的增强形式语义基本原理重构原则结束方式作为线程的生成器语法优化使用StopIteration来返回值被拒绝的建议批评可选的提案附加材料参考资料版权摘要为生成器提出了一种新的语法,用于将部分的操作委派给其它的生成器。这使得一部分包含“yield”的代码段,可以被分离并放置到其它生成器中。与此同时,子生成器会返回一个值,交给委派生成器(delegating generator)使用。当一个生成器再次 yield 被另一个生成器生成的值时,该语法还创造了一些优化的可能。PEP接受Guido 于 2011 年 6 月 26 日正式接受本 PEP。动机Python 的生成器是一种协程,但有一个限制,它只能返回值给直接的调用者。这意味着包含了 yield 的代码段不能像其它代码段一样,被拆分并放入到单独的函数中。如果做了这样的分解,就会导致被调用的函数本身成为一个生成器,并且必须显式地迭代这个生成器,以便重新 yield 它产生的所有值。如果只关心生成值的过程,那么可以不费劲地使用如下的循环:for v in g: yield v但是,如果在调用send(),throw()和close()的情况下,要使子生成器与调用者正确地交互,就相当困难。如后面所说,必要的代码非常复杂,因此想要正确地处理所有特殊情况,将会非常棘手。一种新的语法被提出来解决此问题。在最简单的用例中,它等同于上面的 for-循环,并且可以处理生成器的所有的行为,同时还能用简单而直接的方式进行重构。提议以下的新的生成器语法将被允许在生成器的内部使用:yield from <expr>其中 <expr> 表达式作用于可迭代对象,从迭代器中提取元素。该迭代器会遍历到耗尽,在此期间,它直接向包含 yield from 表达式的调用者生成器(即“委托生成器”)生成和接收值。此外,当该迭代器是一个生成器时,则此生成器可以执行 return 语句返回一个值,而该值将成为 yield from 表达式的值。yield from 表达式的完整语义可通过生成器协议来描述如下:迭代器返回的任何值都直接传给调用者。使用 send() 发送给委托生成器的任何值都直接传给迭代器。如果发送的值是 None,则调用迭代器的 next() 方法。如果发送的值不是 None,则调用迭代器的 send() 方法。如果调用引发了 StopIteration,则恢复委托生成器。任何其它异常都会传递给委托生成器。除 GeneratorExit 以外,任何传给委托生成器的异常都会传给迭代器的 throw() 方法。如果调用引发 StopIteration,则恢复委托生成器。任何其它异常都会传递给委托生成器。如果传给委托生成器的是 GeneratorExit 异常,或者调用委托生成器的 close() 方法,则迭代器的 close() 方法会被调用(如果有)。如果调用时出现异常,则会传给委托生成器。否则的话,在委托生成器中抛出 GeneratorExit。yield from 表达式的值是迭代器终止时引发的 StopIteration 异常的第一个参数。生成器里的 return expr 导致从生成器退出时引发 StopIteration(expr)。StopIteration的增强功能为方便起见,StopIteration 异常被赋予了一个 value 属性,来保存它的第一个参数,若无参数,则为 None。正式的语义本节使用 Python 3语法。1、RESULT = yield from EXPR 语句等同于以下语句:_i = iter(EXPR)try: _y = next(_i)except StopIteration as _e: _r = _e.valueelse: while 1: try: _s = yield _y except GeneratorExit as _e: try: _m = _i.close except AttributeError: pass else: _m() raise _e except BaseException as _e: _x = sys.exc_info() try: _m = _i.throw except AttributeError: raise _e else: try: _y = _m(*_x) except StopIteration as _e: _r = _e.value break else: try: if _s is None: _y = next(_i) else: _y = _i.send(_s) except StopIteration as _e: _r = _e.value breakRESULT = _r2、在生成器中,return value 语句在语义上等同于 raise StopIteration(value) ,除了一点,当前返回的生成器中的 except 子句无法捕获该异常。3、 StopIteration 异常的行为就像这样定义:class StopIteration(Exception): def init(self, *args): if len(args) > 0: self.value = args[0] else: self.value = None Exception.init(self, *args)基本原理重构原则上面提到的大多数语义,其背后的基本原理源于一种对生成器代码进行重构的愿望。即希望可以将包含一个或多个 yield 表达式的代码段,分离进一个单独的函数中(使用常规手段来处理作用域范围内的变量引用,等等),并通过 yield from 表达式来调用该函数。在合理可行的情况下,这种复合而成的生成器的行为应该跟原始的非分离的生成器完全相同,包括调用 __next () 、send()、throw() 和 close() 。子迭代器(而非生成器)的语义被选择成为生成器案例的合理泛化(generalization)。所提出的语义在重构方面具有如下限制:一个捕获了 GenetatorExit 却不重新抛出的代码块,不能在完全保留相同行为的情况下被分离出去。如果将 StopIteration 异常抛进了委托生成器中,则分离的生成器的行为跟原始代码的行为可能会不同。由于这些用例几乎不存在,因此不值得为支持它们而考虑额外的复杂性。结束方式当在 yield from 处挂起时,并且使用 close() 方法显式地终止委托生成器时,关于是否要一并终止子迭代器,存在一些争议。一个反对的论据是,如果在别处存在对子迭代器的引用,这样做会导致过早结束它。对非引用计数型的 Python 实现的考虑,导致了应该显式地结束的结论,以便在所有类型的 Python 实现上,显式地结束子迭代器与非重构的迭代器,能具有相同的效果。这里做的假设是,在大多数用例中,子迭代器不会被共享。在子迭代器被共享的稀有情况下,可通过一个阻塞调用 throw() 和 close() 的装饰器来实现,或者使用除 yield from 以外的方法来调用子迭代器。作为线程的生成器使生成器能够 return 值的动机,还考虑到使用生成器来实现轻量级的线程。当以这种方式使用生成器时,将轻量级线程的计算扩散到许多函数上就会是合理的。人们希望能够像调用普通函数一样调用子生成器,传递给它参数并接收返回值。使用提议的语法,像以下的表达式y = f(x)其中 f 是一个普通的函数,就可以被转化成一个委托调用y = yield from g(x)其中 g 是生成器。通过把 g 想象成一个普通的能被 yield 语句挂起的函数,人们可以推断出结果代码的行为。当以这种方式把生成器作为线程使用时,通常人们不会对 yield 所传入或传出的值感兴趣。但是,也有一些例子,线程可以作为 item 的生产者或消费者。yield from 表达式允许线程的逻辑被扩散到所需的尽可能多的函数中,item 的生产与消费发生在任意的子函数中,并且这些 item 会自动路由到/去它们的最终来源/目的地。对于 throw() 与 close() ,可以合理地预期,如果从外部向线程内抛入了一个异常,那么首先应该在线程挂起处的最内部的生成器中引发,再从那里向外传递;而如果线程是从外部调用 close() 来终结的,那也应该从最内部往外地终止处于活动态的生成器链。语法所提出的特定语法被选中,像它的含义所暗示,并没有引入任何新的关键词,且清晰地突出了它与普通 yield 的不同。优化当存在一长串生成器时,使用专门的语法就为优化提供了可能性。这种生成器链可能存在,例如,当递归遍历树结构时。在链上传递 next() 的调用与 yield 返回值,可能造成 O(n) 开销,最坏情况下会是 O(n**2)。可能的策略是向生成器对象添加一个槽(slot)来保存委派给它的生成器。当在生成器上调用 next() 或 send() 时,首先检查该槽,如果非空,则它引用的生成器将会被激活。如果引发了 StopIteration,该槽会被清空,并且主生成器会被激活。这将减少一系列 C 函数调用的委托开销,并不涉及 Python 代码的执行。一种可能的增强方法是在循环中遍历整个生成器链,并直接激活最后一个生成器,尽管 StopIteration 的处理会比较复杂。使用StopIteration来返回值有多种方法可以将生成器的返回值传回。也有一些替代的方法,例如将其存储为生成器-迭代器对象的属性,或将其作为子生成器的 close() 方法的调用值返回。然而,本 PEP 提议的机制很有吸引力,有如下理由:使用泛化的 StopIteration 异常,可以使其它类型的迭代器轻松地加入协议,而不必增加额外的属性或 close() 方法。它简化了实现,因为子生成器的返回值变得可用的点与引发异常的点相同。延迟到任意时间都需要在某处存储返回值。被拒绝的建议一些想法被讨论并且拒绝了。建议:应该有一些方法可以避免对__next() 的调用,或者用带有指定值的 send() 调用来替换它,目的是支持对生成器作装饰,以便可以自动地执行初始的 next() 。决议:超出本提案的范围。这种生成器不该与 yield from 一起使用。建议:如果关闭一个子迭代器时,引发了带返回值的 StopIteration 异常,则将该值从 close() 调用中返回给委托生成器。此功能的动机是为了通过关闭生成器,传信号给传入生成器的最后的值。被关闭的生成器会捕获 GeneratorExit ,完成其计算并返回一个结果,该结果最终成为 close() 调用的返回值。决议:close() 与 GeneratorExit 的这种用法,将与当前的退出(bail-out)与清理机制的角色不兼容。这要求在关闭子生成器后、关闭一个委托生成器时,该委托生成器可以被恢复,而不是重新引发 GeneratorExit。但这是不可接受的,因为调用 close() 进行清理的意图,无法保证委托生成器能正确地终止。通过其它方式,可以更好地处理向消费者告知(signal)最后的值的问题,例如发送一个哨兵值(sentinel value)或者抛入一个被生产者与消费者都认可的异常。然后,消费者可以检查该哨兵或异常,通过完成其计算并正常地返回,来作响应。这种方案在存在委托的情况下表现正确。建议:如果 close() 不返回值,如果出现 StopIteration 中带有非 None 的值,则抛出一个异常。决议:没有明确的理由如此做。忽略返回值在 Python 中的任何其它地方,都不会被视为错误。批评根据本提案,yield from 表达式的值将以跟普通 yield 表达式非常不同的方式得出。这意味着其它不包含 yield 表达式的语法可能会更合适,但到目前为止,还没有提出可接受的替代方案。被拒绝的替代品包括 call、delegate 和 gcall。有人提议,应该使用子生成器中除 return 以外的某些机制,来处理 yield from 表达式的返回值。但是,这会干扰将子生成器视为可挂起函数的目的,因为它不能像其它函数一样 return 值。有人批评,说使用异常来传递返回值是“滥用异常”,却没有任何具体的理由来证明它。无论如何,这只是一种实现的建议;其它机制可以在不丢失本提案的任何关键特性的情况下使用。有人建议,使用与 StopIteration 不同的异常来返回值,例如 GeneratorReturn。但是,还没有令人信服的实际理由被提出,并且向 StopIteration 添加 value 属性减轻了从异常(该异常可能存在也可能不存在)中提取返回值的所有困难。此外,使用不同的异常意味着,与普通函数不同,生成器中不带值的 return,将不等同于 return None 。可选的提案之前已经提到了类似的提议,有些语法使用 yield 而不是 yield from。虽然 yield 更简洁,但是有争议的是,它看起来与普通的 yield 太相似了,可能在阅读代码时会忽视了其中的差异。据作者所知,之前的提案只关注于 yield 产生值,因此遭受到了批评,即他们所替代的两行 for 循环并没有足够令人厌烦,不足以让人为新的语法辩护。通过处理完整的生成器协议,本提案提供了更多的好处。附加材料本提案的语法的一些用例已经被提供出来,并且基于上面概括的第一个优化的原型也已实现。Examples and Implementation可以从跟踪器问题的 issue 11682 中获得针对 Python 3.3 实现的升级版本。参考资料[1] https://mail.python.org/pipermail/python-dev/2011-June/112010.html [2] http://www.cosc.canterbury.ac.nz/greg.ewing/python/yield-from/ [3] http://bugs.python.org/issue11682版权本文档已经放置在公共领域。源文档:https://github.com/python/pep…————-(译文完)————-相关链接: PEP背景知识 :学习Python,怎能不懂点PEP呢?PEP翻译计划 :https://github.com/chinesehua…[[译] PEP 255–简单的生成器](https://mp.weixin.qq.com/s/vj...[[译] PEP 342–增强型生成器:协程](https://mp.weixin.qq.com/s/M7...[[译] PEP 525–异步生成器](https://mp.weixin.qq.com/s/fy…公众号【Python猫】, 专注Python技术、数据科学和深度学习,力图创造一个有趣又有用的学习分享平台。本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、优质英文推荐与翻译等等,欢迎关注哦。PS:后台回复“爱学习”,免费获得一份学习大礼包。 ...

February 16, 2019 · 2 min · jiezi

Python自学之路

图片描述一、回顾自学初期状态Python学习也将近一年,自学初期读了很多自学指导,学习路线图,搜集发现的所有python学习资源。最终发现,读过的指导还是别人的,看过的学习路线走进了起点却不知道在哪里已经迷失,搜集的学习资源仍然像买过的书籍只是看到了个目录,更像是收藏的书签从未再次打开过。为什么会有这么多资源,并不是本渣渣学习多么的努力,只因网络资源太多且太杂乱,总认为会找到更适合自己且高质量的学习资源。自学初期由于迷茫总是不断的寻找资源,回过头看其实大多资源都大差不差。这种状态的主要原因是python虽然入门简单,但是知识冗杂。在不知道自己学习python的真正需求时,看到茫茫的python基础知识点、第三方库总感觉学海无涯,希望找到一份失传的速成武功秘籍。在迷茫的时候,本渣渣还是冷静思考了一下《为什么要学习python》,思考后针对自己学习python的需求,对资源进行了一下整理,又有针对性地去寻找资源。适合本渣渣的:首先是完全契合本渣渣需求的__bobby老师的全套资源 http://www.imooc.com/t/2255006__bobby老师的两个课程已经完全满足我学习python的目的课程一:课程地址:https://coding.imooc.com/clas…课程名字:强力django+杀手级xadmin课程二:地址:https://coding.imooc.com/clas…课程名字:聚焦Python分布式爬虫必学框架Scrapy 打造搜索引擎建议:如果想在线学习直接www.imooc.com即可,其它平台python课程并不太好。二、资源整理1、基础知识图片描述任何一门编程语言的基础知识都是庞杂的,如果只是为了短期学习某个编程语言找份糊口的工作,大可不必再以学生的状态学习,要根据需求学习,有时间再聊《如何快速学习一门编程语言》。2、Python系列资源五此资源相当于自学python的百科字典,相当的基础全面,讲的又细致但是没必要一节一节的看,09-012是python基础知识,如果有C或者java编程基础的可以只了解基础知识即可,其它选看3、电子书图片描述由于python有很多基础库,和第三方依赖包,偶尔使用手机阅读一下电子书,大致了解一下知识点,编程速成主要是了解知识点,遇到需求知道那块知识可以解决,现学现用比较实际。了解知识点:Python标准库中文版Python参考手册(第4版:含有库)Python3程序开发指南第二版这个纯属对黑客的神往,偶尔看一看,说不定还可以了解点技术留待以后吹牛皮用[互联网服务器攻防秘笈].陈彬.扫描版[ED2000.COM]4、网站制作基础web前端的基础知识,做了解即可,同样现学现用最实际5、完整django + scrapy学习 图片描述此乃硬核,可以说__bobby老师的两套教程不仅让我拿到优秀毕设、还给我找到第一份工作打下坚实的基础,再配合“网站制作基础“ 好好学习,让你找到一份web开发或者爬虫类的工作完全不是问题:__bobby老师https://www.imooc.com/t/2255006他人学习__bobby老师课程得而笔记https://mtianyan.gitee.io/cat…https://github.com/mtianyan麦子学院django课程:https://www.bilibili.com/vide…django课程第九章左右好像有点残缺但是问题不大6、开发工具图片描述ssr翻墙软件,4.0即为PC版,翻墙是学习编程的必备技术,大家都懂的百度的尿性。免费ssr代理分享需要翻墙才能访问:https://www.freefq.com/d/file…轻便的文本编辑器,好不好用谁用谁知道Notepad_v7.5.4.rar破解版mysql可视化管理软件NavicatForMysql.zip Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型Redis-x64-3.2.100.zipredis数据可视化管理应用redis-desktop-manager-0.8.8.384.exePython比较好用IDE,打开 资源.txt 使用服务器激活使用,可以百度解决怎么激活pycharm-professional-2017.3.2.exe7、PotPlayerSetup_1.7.1150.0.exe看视频必备轻量级播放器8、全部资源图片描述学习资源不必贪多,想好自己为什么要学习python,要用python干什么,再去选择针对性的学习资源去认真学习。9、敲过的代码图片描述两部分:基础知识学习、项目实战这些代码主要是大四实习阶段,通过关系在一个培训机构免费旁听了两个月基础知识主要是:列表、字典、函数、类、scrapy、re、request、selenium等项目实战①、blog_project博客项目参考课程:麦子学院胡明星django:https://www.bilibili.com/vide…为什么博客项目在学习python web的实战中那么常见,想必大家可能都会认为个人博客简单,当初本渣渣在学习时也是这么认为的。自从毕业后只身奔往上海找到第一份工作,才发现它的另一个价值。本渣渣第一份工作就是纯靠__bobby老师的两个硬核课程,找到了一份SEO相关的工作,三个月的时间一个人撑起一个技术团队给公司开发了两个项目:网站集群管理系统、针对某个大型网站的自动化脚本(mysql+redis +selenium)。图片描述仔细想一下,互联网什么最值钱,非流量(就是每一个网民)莫属,只要你有足够的流量就可以变现。个人博客的好处就在于,不仅在你初学python时快速学习web技术,在这里用SEO的技术告诉你只要你把你的第一个django项目真正部署上线,稍加运营便可获取流量。如果有此想法却不了解SEO知识可以留言或者私信『sxc123654』 备注『SEO』和本渣渣交流。②、novelshttps://www.bilibili.com/vide…此项目的目的纯属为了学习怎么快速扒取别人网站,都懂的要想快速借鉴已有是不二捷径,尤其是学习后端开发的不了解一下扒取别人全栈、bootstrap等技术路也是很窄的,了解了这些技术可以快速成为伪python全栈高手③、django10此项目是自己写的,scrapy抓取豆瓣数据、django实现仿豆瓣,算是个半成品,由于当时要开始自己的毕业设计,就把此项目放下了。抓取豆瓣的scrapy源码也找不到了,也有可能就在基础知识学习文件中。10、这些资源已经整理好,后台回复『1024』即可获取。以上的自学资源全部来源于网络资源,其中也含自己付费买的的资源。现在全部免费奉上,每个看到此篇文章的想必都是像本渣渣一样C、C++、JAVA混不下去才打算通过无所不能的python谋条出路。希望大家共同进步都成为技术大牛,通过python实现大厂梦。文章来源: 微信公众号『stormsha』 微信号『sxc123654』 QQ『1414749109』资源|教程|交流

February 16, 2019 · 1 min · jiezi

drf实现常用数据缓存

在以往的后台数据访问时,我们往往都会进行数据库查询,基本的流程是这样的:图中发生了三次请求,则很正常向数据库查询了三次。但是现在有这样一个场景:我们有1000个人在一个十分钟内向一个我们网站都看了同一个文章,那么我们有没有哪些可以优化我们的后端代码,因为这只是一篇文章在短时间内就被访问了1000次,当然我们网站的文章是海量的,那我们该怎么办?那么我们的数据缓存就派上用场了,基本的流程是这样的:流程在第一次请求的时候查看缓存中(redis)是否有数据,有数据则直接返回响应若redis中没有数据,则查询数据库查询数据库并将数据保存到redis中,返回响应这就是我们为什么只查询了一次数据库,若有1000次,我们在缓存时间内也只需要查询一次数据库,这里向redis中获取数据也需要耗时,但是由于redis数据存储在内存中,数据获取性能较数据库高了不止一点半点。那么在drf项目中如何去实现呢?只需要简单的三步安装pip install drf-extensions配置(可以省略)# DRF扩展REST_FRAMEWORK_EXTENSIONS = { # 缓存时间 ‘DEFAULT_CACHE_RESPONSE_TIMEOUT’: 60 * 60,}使用使用cache_response装饰器from rest_framework.response import Responsefrom rest_framework import viewsfrom rest_framework_extensions.cache.decorators import ( cache_response)from myapp.models import Cityclass CityView(views.APIView): @cache_response() def get(self, request, *args, **kwargs): cities = City.objects.all().values_list(’name’, flat=True) return Response(cities)注意,cache_response装饰器既可以装饰在类视图中的get方法上,也可以装饰在REST framework扩展类提供的list或retrieve方法上。使用cache_response装饰器无需使用method_decorator进行转换。使用扩展类(使用了视图集ViewSet)ListCacheResponseMixin:用于缓存返回列表数据的视图,与ListModelMixin扩展类配合使用,实际是为list方法添加了cache_response装饰器RetrieveCacheResponseMixin:用于缓存返回单一数据的视图,与RetrieveModelMixin扩展类配合使用,实际是为retrieve方法添加了cache_response装饰器CacheResponseMixin:为视图集同时补充List和Retrieve两种缓存,与ListModelMixin和RetrieveModelMixin一起配合使用。from myapps.serializers import UserSerializerfrom rest_framework_extensions.cache.mixins import CacheResponseMixinclass UserViewSet(CacheResponseMixin, viewsets.ModelViewSet):#继承顺序一定在ViewSet前,其实必须在对应的mixin前 serializer_class = UserSerializerdef-extensions官方文档:http://chibisov.github.io/drf…

February 15, 2019 · 1 min · jiezi

Python之父重回决策层,社区未来如何发展?

春节假期结束了,大家陆续地重回到原来的生活轨道上。假期是一个很好的休息与调节的机会,同时,春节还有辞旧迎新的本意,它是新的轮回的开端。在 Python 社区里,刚发生了一件大事,同样有开启新纪元的意义:在"Python 之父" Guido van Rossum 宣布卸任 BDFL(终身仁慈独裁者)后,Python 核心开发者们历经半年多的时间,终于为新的治理方案选出了第一届的“执政成员”。2 月 4 日,经过为期 2 周的投票,Python 社区选出了第一届的指导委员会的 5 名成员:Barry Warsaw、Brett Cannon、Carol Willing、Guido van Rossum、Nick Coghlan。前段时间,我曾回顾了 Python 之父的退位风波、翻译了各种治理提案的汇总介绍、也分析了核心开发者的投票意向(PS:可通过文末链接进行查看)。本文是对此事件的跟踪报道,也是一个阶段性的句号。随着第一届指导委员会成员的确定,Python 社区将迎来一个新的安稳的过渡期。本文的意义,就是向各位 Python 开发者/学习者/爱好者宣告这个好消息。核心开发者的自治模式迎来如此重大的转变,这本就是一件值得关注的大事。Python 社区的未来走向与此息息相关,而这种治理模式的成败,也会为其它技术社区提供极好的参照系。1、指导委员会是什么?关于指导委员会(Steering Council),它是 7 种治理方案中最晚被提出,但却最被广泛接收的一个,最终经过投票成为了社区里新的治理方案。该治理方案以 5 人组成的指导委员会作为最高决策层,并允许在必要的时候,将决策权委派给其它团队或开发者代表。指导委员会拥有至高的权力,但它的行事原则是:boring、simple、comprehensive、flexible and light-weight,具体而言则是,通过设定一系列的基础性的、清晰的、灵活的、轻量的规则及流程,来“指导”社区的治理工作。指导委员会可以直接行使某些权力,例如批准或驳回 PEP、更新项目的行为守则、跟软件基金会一同管理项目资产等等,然而,过分行驶权力的方式并不受鼓励。指导委员会与其它治理提案的关键区别就在于,它将扮演规则制定者的角色,指导、引导以及协调社区工作,只有在关键时候,才会行使最终的裁决权。指导委员会的职能是:Maintain the quality and stability of the Python language and CPython interpreter,维护 Python 语言及 CPython 解释器的质量与稳定性Make contributing as accessible, inclusive, and sustainable as possible,尽可能使做贡献是便利的、包容的与可持续的Formalize and maintain the relationship between the core team and the PSF,巩固核心团队与 Python 软件基金会的关系Establish appropriate decision-making processes for PEPs,为 PEP 建立恰当的决策流程Seek consensus among contributors and the core team before acting in a formal capacity,为贡献者与核心团队寻求共识Act as a “court of final appeal” for decisions where all other methods have failed,当其它所有方法都失败时扮演“最终裁决法庭”的角色这个治理模式是借鉴自 Django 项目,详细内容参见 PEP-13。2、指导委员会的成员?指导委员会的固定成员是 5 人,且最多允许两人来自同一家企业。换届频率是每个 Python 发行版本。成员可连任。支持不信任投票(即弹劾)。现在来看看第一届当选的成员:Barry Warsaw:自1995年起成为核心开发者之一,荣获 2014 年的弗兰克·威利森纪念奖。目前供职于 LinkedIn(已被微软收购,也即供职于微软),业余爱好是音乐和太极。Brett Cannon:自2003年起成为核心开发者之一,荣获 2016 年的弗兰克·威利森纪念奖。曾担任 Python 软件基金会的执行副主席。目前供职于微软,负责 VSCode 的 Python 插件项目。Carol Willing:Python 核心开发者,Jupyter 核心开发者及 Jupyter 的指导委员会成员。自由职业,兴趣在于科研及教育项目。Guido van Rossum:Python 的创始人,被称为“Python 之父”,长期领导 Python 社区的发展,直到此次的退位风波。目前供职于 Dropbox。Nick Coghlan:自2005年起成为核心开发者之一。目前供职于 Tritium。注:弗兰克·威利森纪念奖,即 Frank Willison Memorial Award,该奖由 O’Reilly 出版集团设立,颁布给为 Python 社区做了突出贡献的个人。设立于2002年,每年颁布一次。这些成员都是多年的资深核心开发者,为 Python 发展做出过长足的贡献。最值得一提的当然是 Guido van Rossum,他并没有离开决策层。事实上,Guido 是自荐成为候选人的,并且是 17 名候选人中最早自荐或被提名的几个人之一。在当选之后,其他人都在 Twitter 上转发了好消息,而 Guido 不置一词。这留下了一个悬念:Guido 出于什么考虑而决定重回决策层呢,又将会扮演怎样的角色呢?3、开源技术项目的发展?要发起一个开源的技术项目,似乎并不难,然而,要使它推广到广大的技术群体,打造出完整的技术生态,并且持续健康地运作下去,这就太难了。今天,看到一则新闻:Bootstrap 5 将彻底移除对 jQuery 的依赖。我不由地想起半年前,Github 也宣布了完全放弃 jQuery。jQuery 是著名的前端开源项目,几年前一统江湖盛极一时,然而随着 MVVM 框架的崛起,目前已到了穷途末路的境地。这揭示了技术项目发展的第一大难题:保持技术的领先性。近几年,Python 凭借着在人工智能和科学计算领域的赫赫战功,成为了众多开发者追捧的对象,对我等追随者来说,真是喜闻乐见。乐观地想,Python 至少还不会因为技术原因而没落。去年,技术社区里还发生了一件大事:Linux 之父 Linus Torvalds 宣布要无限期休假。这个新闻跟 Python 之父的退位相比,所引起的轰动效应可要大得多了。这两件事有很大的相似性,引发了我的好奇心:开源技术项目所重度依赖的灵魂人物离开了,它们如何才能继续健康地发展运作?这个话题对我等小小的边缘码农而言,实在是超出能力范围而无法回答。所幸的是,他们又回归了。不过对于核心开发者们来说,这个话题迟早要面对,现在的风波就是一个预警。Python 社区贡献出来的指导委员会治理方案,会带来什么样的变化,会引领社区走向何方呢?拭目以待。相关链接:这件正在发生的事,关乎所有的Python开发者……https://www.python.org/dev/pe…https://www.python.org/dev/pe...-----------------本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

February 14, 2019 · 1 min · jiezi

Django搭建个人博客:文章标签功能

“标签”是作者从文章中提取的核心词汇,其他用户可以通过标签快速了解文章的关注点。每一篇文章的标签可能都不一样,并且还可能拥有多个标签,这是与栏目功能不同的。好在标签功能也有优秀的三方库:Django-taggit,省得自己动手设计了。快速开发就是这样,能“借用”就不要自己重复劳动。安装及设置首先在虚拟环境中安装Django-taggit:pip install django-taggit安装成功后,修改项目设置以添加库:my_blog/settings.py…INSTALLED_APPS = [ … ’taggit’,]…修改文章模型标签是文章Model的属性,因此需要修改文章模型。需要注意的是标签引用的不是内置字段,而是库中的TaggableManager,它是处理多对多关系的管理器:article/models.py…# Django-taggitfrom taggit.managers import TaggableManager…class ArticlePost(models.Model): … # 文章标签 tags = TaggableManager(blank=True) …然后记得数据迁移。带标签文章的发表修改文章的表单类,让其能够提交标签字段:article/forms.py…class ArticlePostForm(forms.ModelForm): class Meta: … fields = (’title’, ‘body’, ’tags’)然后修改发表文章的视图,保存POST中的标签:article/views.py…def article_create(request): # 已有代码 if request.method == “POST”: article_post_form = ArticlePostForm(data=request.POST) if article_post_form.is_valid(): new_article = article_post_form.save(commit=False) … new_article.save() # 新增代码,保存 tags 的多对多关系 article_post_form.save_m2m() …需要注意的是,如果提交的表单使用了commit=False选项,则必须调用save_m2m()才能正确的保存标签,就像普通的多对多关系一样。最后就是在发表文章的模板中添加标签的表单项了:templates/article/create.html…<!– 提交文章的表单 –><form method=“post” action="."> … <!– 文章标签 –> <div class=“form-group”> <label for=“tags”>标签</label> <input type=“text” class=“form-control col-3” id=“tags” name=“tags” > </div> …</form>…运行服务器,就可以在发表页面看到效果了:多个标签最好用英文逗号进行分隔。中文逗号有的版本会报错,干脆就不要去使用了。列表中显示标签虽然保存标签的功能已经实现了,还得把它显示出来才行。显示标签最常用的位置是在文章列表中,方便用户筛选感兴趣的文章。修改文章列表的模板,将标签显示出来:templates/article/list.html…<!– 栏目 –>…<!– 标签 –><span> {% for tag in article.tags.all %} <a href="#" class=“badge badge-secondary” > {{ tag }} </a> {% endfor %}</span>…链接中的class中是Bootstrap定义的徽章样式。插入位置紧靠在栏目按钮的后面。当然你想放到其他位置也是完全可以的。刷新列表页面看看效果:标签过滤有时候用户想搜索带有某一个标签的所有文章,现在就来做这个功能。与搜索功能一样,只需要调取数据时用filter()方法过滤结果就可以了。修改<a>标签中的href,使其带有tag参数返回到View中:templates/article/list.html…<!– 标签 –><span> {% for tag in article.tags.all %} <a href="{% url ‘article:article_list’ %}?tag={{ tag }}" class=“badge badge-secondary” > {{ tag }} </a> {% endfor %}</span>…然后在View中取得tag的值,并进行搜索。下面的代码将article_list()函数完整写出来了(包括上一章末尾没讲的栏目查询),方便读者比对。article/views.py…def article_list(request): # 从 url 中提取查询参数 search = request.GET.get(‘search’) order = request.GET.get(‘order’) column = request.GET.get(‘column’) tag = request.GET.get(’tag’) # 初始化查询集 article_list = ArticlePost.objects.all() # 搜索查询集 if search: article_list = article_list.filter( Q(title__icontains=search) | Q(body__icontains=search) ) else: search = ’’ # 栏目查询集 if column is not None and column.isdigit(): article_list = article_list.filter(column=column) # 标签查询集 if tag and tag != ‘None’: article_list = article_list.filter(tags__name__in=[tag]) # 查询集排序 if order == ’total_views’: article_list = article_list.order_by(’-total_views’) paginator = Paginator(article_list, 3) page = request.GET.get(‘page’) articles = paginator.get_page(page) # 需要传递给模板(templates)的对象 context = { ‘articles’: articles, ‘order’: order, ‘search’: search, ‘column’: column, ’tag’: tag, } return render(request, ‘article/list.html’, context)…注意Django-taggit中标签过滤的写法:filter(tags__name__in=[tag]),意思是在tags字段中过滤name为tag的数据条目。赋值的字符串tag用方括号包起来。之所以这样写是因为Django-taggit还支持多标签的联合查询,比如:Model.objects.filter(tags__name__in=[“tag1”, “tag2”])为了实现带参数的交叉查询,还要将翻页等位置的href修改一下:templates/article/list.html…<!– 所有类似地方加上 tag 参数,如排序、翻页等 –><a href="{% url ‘article:article_list’ %}?search={{ search }}&column={{ column }}&tag={{ tag }}"> 最新</a>…<a href="{% url ‘article:article_list’ %}?order=total_views&search={{ search }}&column={{ column }}&tag={{ tag }}"> 最热</a>…<a href="?page=1&order={{ order }}&search={{ search }}&column={{ column }}&tag={{ tag }}" class=“btn btn-success”> &laquo; 1</a><!– 上面3条是举例,其他类似地方也请补充进去 –>…标签过滤功能就完成了。Django-taggit更多的用法请阅读官方文档:Django-taggit总结本章学习了使用Django-taggit来完成标签功能。在学习阶段,你可以不借助他人的轮子,自己实现功能:瞎折腾对掌握基础有很大帮助。实际开发时,又分为两种情况:浅层需求某项通用功能,开发完成后改动不大:此类功能建议尽量使用轮子,加快开发效率。人生苦短,能节约的时间,一秒钟都不要浪费。需要大量定制化的功能,开发完成后需要频繁改动:此类功能因为经常对底层代码进行改动,与其在别人的代码上修修补补,还不如自己从头写了。自己的代码不仅熟悉,而且都是为定制化而生的。到底如何选择,就根据你的喜欢进行斟酌了。有疑问请在杜赛的个人网站留言,我会尽快回复。或Email私信我:dusaiphoto@foxmail.com项目完整代码:Django_blog_tutorial ...

February 12, 2019 · 2 min · jiezi

vue+django+mysql实现移动端二手交易应用

这是一个用vue+django+mysql实现的移动端二手交易应用。效果展示相关仓库onehome:项目前端部分onehomeServer:项目后端部分onehomeDoc:项目的一些文档已完成功能[x] 登录[x] 注册[x] 首页展示[x] 发布商品信息(支持多图片上传)[x] 收藏/取消收藏[x] 私聊[x] 搜索[x] 消息提醒[x] 上传图像[x] 我的收藏[x] 我的发布[x] 修改密码[x] 退出登录

February 4, 2019 · 1 min · jiezi

四个月技术写作,我写了些什么?

从去年国庆节开始,我连续更新了 4 个月公众号,累计发布原创文章 40 篇。按照大多数个人订阅号的优良传统,号主应该在跨年的前后作年终总结。然而,一来我反应比较迟钝,没跟上节奏,二来当时我正在写比较重要的系列,没时间分心,所以还是慢了半拍。现在,创作出现了空档期,而身体也出现一种魔幻性的跨移——从几千里外的城市回到分别了几百天的农村。这仿佛就在营造一种仪式感,逼使我要把这未完成的任务做个了结。因此,现在我就来梳理梳理写出来的东西,说说我的想法吧。1、Python猫的故事这是我的主打系列,故事的主角是一只来自外太星(喵星)的猫。它外貌长什么样,我还没想好,你可以叠加所有猫的形象上去,这就是它的样子。然而,它绝不是一种固定形态的物种。编程语言(Python为主)、人类文化(文学+哲学)、人工智能、前沿科学(生物+量子物理)和幻想相交合,这些东西都会是我的灵感,也会是塑造这只猫的原力。我们认识周遭世界的过程是一种逐步扩大的过程,从点到线,到理得清的网,再到真正的网。一只作为讲述者的猫,在思考,在探知并试图融入陌生的星球的时候,会发生些什么认知层面上的结果呢?我有很多朦胧的念头。想要完全落实它们,简直不可能。有些东西就是说不清。不过,有了开端,有了大致的方向,就总是有了提起“笔”写下去的动机。有了Python,我能叫出所有猫的名字Python对象的身份迷思:从全体公民到万物皆数Python对象的空间边界:独善其身与开放包容2、Python进阶之路我接触 Python 的时间并不长,在工作中用到它的时间就更短了。因为清楚地意识到自己的基础并不扎实,所以,几个月以来,我花了不少时间系统性地学习了一些内容。写作前,搜集资料,查漏补缺;写作中,发散思考,融会贯通;发布后,聆听反馈,修正错误。时间过得真快,现在能拿得出手的也就仅仅是字符串系列、切片系列和迭代器系列了。我计划继续花些时间,把重要的知识梳理一遍。通过这个系列的写作,我想驱动自己走出一条 Python 进阶之路。然后以它为基础,再进行其它领域(爬虫、数据分析、深度学习、?)的转向。在准备生成器系列的时候,我一时起了翻译 PEP 的念头,就开启了翻译 PEP 的系列。现在试水了两篇生成器相关的,年后还会翻译一篇。关于翻译,我有一些想法,今后再细说。这个系列的一些内容,其实是在给 Python 猫系列打基础做铺垫。今后,我会避免两个系列的内容重叠,也不让它们失衡,因此会想办法给 Python猫 系列留下足够的写作余地。超强汇总:学习Python列表,只需这篇文章就够了学习Python操作JSON,网络数据交换不用愁给Python学习者的文件读写指南(含基础与进阶,建议收藏)再谈文件读写:判断文件的几种方法及其优劣对比Python中的“特权种族”是什么?详解Python拼接字符串的七种方式你真的知道Python的字符串是什么吗?你真的知道Python的字符串怎么用吗?Python是否支持复制字符串呢?join()方法的神奇用处与Intern机制的软肋Python进阶:切片的误区与高级用法Python进阶:迭代器与迭代器切片Python进阶:全面解读高级特性之切片!Python进阶:设计模式之迭代器模式为什么range不是迭代器?range到底是什么类型?[[译] PEP 255–简单的生成器](https://mp.weixin.qq.com/s/vj...[[译]PEP 342–增强型生成器:协程](https://mp.weixin.qq.com/s/M7...3、荐书系列关于荐书系列,我是受到了经常阅读的一些电影公众号的启发。如果有一部好电影,大家就会花很长篇幅去推介它,去评论它,去宣传它。对于一些非技术类的书籍,也很可能有此待遇。可是,我们却不怎么见到技术类书籍是这样的吧?除去出版社、作者和利益相关机构,你几乎看不到有人为一本技术书籍写书评(写笔记、画思维导图的倒是挺多)。于是,我决定来尝试一下。有几篇,我特意提到了豆瓣评分和评论,现在看来模仿的痕迹太重,这类玩意对技术类书籍来说,真不合适。纯探索阶段,希望今后能拿出更好的作品。这个系列,主要还有一个考虑:促使我自己去阅读,逼着自己学会总结归纳,多产生一些积累。《编写高质量代码改善 Python 程序的 91 个建议》《Python最佳实践指南》《黑客与画家》《Python源码剖析》《Python高性能编程 》4、杂七杂八这部分内容也是跟技术息息相关的,例如 Python社区动态、技术写作、编程思想、技术翻译等等。其中,关于社区治理模式投票的几篇文章,我最初以为是个热点,但后来意识到,真的没有多少人关心。(我该怀疑自己的关注点呢,还是怀疑别人?)值得庆幸的是,有篇文章被两位圈内大佬转载了!我乐了好久。这里就不提名字了,总之他们是我初学 Python 时就很佩服的人,因为看了他们的一些文章,我才动了写技术文章的念头。关于技术写作和翻译,我初见门道,今后还会多作总结分享。来自Kenneth Reitz大神的建议:避免不必要的面向对象编程学习Python,怎能不懂点PEP呢?再聊聊Python中文社区的翻译Python之父退位后,最高决策权花落谁家?这件正在发生的事,关乎所有的Python开发者……最新进展|关于Python治理模式的投票Python决策权的投票结果诞生了,“指导委员会”模式拔得头筹聊聊技术写作的个人体会大名鼎鼎的Requests库用了什么编码风格?5、写在最后我承认自己是一个不擅运营的人,虽然为了提升公众号的订阅数与阅读数,也做过一些运营的尝试,但是,跟圈内的很多号主相比,差得可不止一丝半点。四个月以来,我结识了很多技术写作的号主,他们有些人创号不久,但不仅技术扎实,而且抓选题、抓热点和写作风格都极其出色,很快就成为了“当红炸子鸡”;还有的大佬,持续耕耘了几年,坐拥数十万粉丝,立品牌、出书、出课程、开知识付费、开公司,替技术人走出了一条名利双收的榜样之路。他们令我羡慕。他们皆有值得我学习取经的优点。不过我也知道,坚持自己的原则、发展自己的特色更为重要。做人也好,写公众号也好,循着自己的本心与节奏,才不至于走歪了路。所以,在以上几个系列的写作方向上,我仍会继续坚持,沉下心来学习,思考和分享。这个阶段性的小结,既是一个交代,也是新的开端。—————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。

February 3, 2019 · 1 min · jiezi

Django搭建个人博客:设置文章的栏目

博客的文章类型通常不止一种:有时候你会写高深莫测的技术文章,有时候又纯粹只记录一下当天的心情。因此对文章的分类就显得相当的重要了,既方便博主对文章进行分类归档,也方便用户有针对性的阅读。而文章分类一个重要的途径就是设置栏目。栏目的模型实现文章栏目功能的方法有多种。你可以只是简单的在文章的Model中增加CharField()字段,以字符串的形式将栏目名称保存起来(实际上这种实现更像是“标签”,以后会讲到)。这样做的优点是比较简单;缺点也很明显,就是时间长了你可能会记混栏目的名字,并且也不方便对栏目的其他属性进行扩展。因此对文章栏目可以独立为一个Model,用外键与文章的Model关联起来。修改article/modles.py文件:article/models.py…class ArticleColumn(models.Model): """ 栏目的 Model """ # 栏目标题 title = models.CharField(max_length=100, blank=True) # 创建时间 created = models.DateTimeField(default=timezone.now) def str(self): return self.titleclass ArticlePost(models.Model): … # 文章栏目的 “一对多” 外键 column = models.ForeignKey( ArticleColumn, null=True, blank=True, on_delete=models.CASCADE, related_name=‘article’ ) …栏目的Model有两个字段,“名称”和“创建日期”。一篇文章只有一个栏目,而一个栏目可以对应多篇文章,因此建立“一对多”的关系。写好模型后记得用makemigrations和migrate指令迁移数据。列表中显示栏目添加测试数据模型写好之后就需要几条栏目的数据来进行测试。由于还没有写视图,因此需要善加利用Django自带的后台。首先把栏目模型注册到后台:article/admin.py…from .models import ArticleColumn# 注册文章栏目admin.site.register(ArticleColumn)然后就可以在后台中添加栏目的数据条目了:先随便添加了“HTML”、“Java”、“Django”这3条。在后台中随便打开一篇文章,“栏目”字段已经静静的在等你了:随机找几篇文章设置不同的栏目用于测试。重写文章列表之前我们用卡片类型的UI界面展示文章列表。卡片的好处是简洁大方,但是随着数据的增多,卡片小小的版面已经不堪重负了。因此这里重写list.html模板的列表循环:article/list.html…<!– 列表循环 –><div class=“row mt-2”> {% for article in articles %} <!– 文章内容 –> <div class=“col-12”> <!– 栏目 –> {% if article.column %} <button type=“button” class=“btn btn-sm mb-2 {% if article.column.title == ‘Django’ %} btn-success {% elif article.column.title == ‘Java’ %} btn-danger {% elif article.column.title == ‘HTML’ %} btn-warning {% endif %} " > {{ article.column }} </button> {% endif %} <!– 标题 –> <h4> <b> <a href=”{% url ‘article:article_detail’ article.id %}" style=“color: black;” > {{ article.title }} </a> </b> </h4> <!– 摘要 –> <div> <p style=“color: gray;"> {{ article.body|slice:‘100’ }}… </p> </div> <!– 注脚 –> <p> <!– 附加信息 –> <span style=“color: green;"> {{ article.total_views }} 浏览&nbsp;&nbsp;&nbsp; </span> <span style=“color: blue;"> {{ article.created|date:‘Y-m-d’ }} 发布&nbsp;&nbsp;&nbsp; </span> <span style=“color: darkred;"> {{ article.updated|date:‘Y-m-d’ }} 更新 </span> </p> <hr> </div> {% endfor %}</div>…最主要的改动就是新增了展现“栏目”的按钮。我们甚至还为不同的栏目设置了不同的按钮颜色。在附加信息中顺便还把之前没有用到的日期信息也添加上去了。来看看效果:感觉还不错!修改写文章功能展示已经没问题了,但是发表新文章时还不能选择栏目。修改写文章的模板,在表单中新增下面的内容:templates/article/create.html…<!– 提交文章的表单 –><form method=“post” action=”."> {% csrf_token %} <!– 文章标题 –> … <!– 文章栏目 –> <div class=“form-group”> <label for=“column”>栏目</label> <select class=“form-control” id=“column” name=“column” > <option value=“none”>请选择栏目..</option> {% for column in columns %} <option value=”{{ column.id }}">{{ column }}</option> {% endfor %} </select> </div> <!– 文章正文 –> … <!– 提交按钮 –> …</form><select>是表单的下拉框选择组件。在这个组件中循环列出所有的栏目数据,并且设置value属性,指定表单提交栏目的id值。刷新页面,效果像下面这样:跟之前一样,能够展示了,但是还没有处理表单的视图逻辑。修改已有的写文章视图article_create(),让其能够处理表单上传的栏目数据:article/views.py# 引入栏目Modelfrom .models import ArticleColumn…# 写文章的视图…def article_create(request): if request.method == “POST”: … if article_post_form.is_valid(): … # 新增的代码 if request.POST[‘column’] != ’none’: new_article.column = ArticleColumn.objects.get(id=request.POST[‘column’]) # 已有代码 new_article.save() … else: … # 新增及修改的代码 columns = ArticleColumn.objects.all() context = { ‘article_post_form’: article_post_form, ‘columns’: columns } …新增代码涉及get和post两部分:POST:主要考虑某些文章是可以没有栏目的。因此用if语句判断该文章是否有栏目,如果有,则根据表单提交的value值,关联对应的栏目。GET:增加栏目的上下文,以便模板使用。测试一下,写文章的栏目功能应该可以正常工作了。修改更新视图更新文章的视图同样也需要升级一下。还是先更改模板:templates/article/update.html…<!– 提交文章的表单 –><form method=“post” action=”."> {% csrf_token %} <!– 文章标题 –> … <!– 文章栏目 –> <div class=“form-group”> <label for=“column”>栏目</label> <select class=“form-control” id=“column” name=“column” > <option value=“none”>请选择栏目..</option> {% for column in columns %} <option value=”{{ column.id }}" {% if column.id == article.column.id %} selected {% endif %} > {{ column }} </option> {% endfor %} </select> </div> <!– 文章正文 –> … <!– 提交按钮 –> …</form>…与前面稍有不同的是,表单中判断了column.id与article.column.id是否相等,如果相等则将其设置为默认值。然后修改视图函数:article/views.py# 更新文章…def article_update(request, id): … # 判断用户是否为 POST 提交表单数据 if request.method == “POST”: … if article_post_form.is_valid(): … # 新增的代码 if request.POST[‘column’] != ’none’: article.column = ArticleColumn.objects.get(id=request.POST[‘column’]) else: article.column = None … else: … # 新增及修改的代码 columns = ArticleColumn.objects.all() context = { ‘article’: article, ‘article_post_form’: article_post_form, ‘columns’: columns, } …代码逻辑与前面很类似。修改文章的栏目功能,也就完成了。总结本章实现了简单的栏目功能,可以舒舒服服对文章进行分类了,强迫症福音啊。还有些可以完善的工作,比如:单击栏目按钮显示所有相同栏目的文章。这个功能与之前学过的最热文章排序以及搜索文章非常的类似。还记得filter()方法吗?栏目Model的增删改查。对个人博客来说,栏目数据的变动通常是很少的。如果你不想写增删改查,用后台修改数据是完全可以的。以上内容就不再赘述了。留给读者去尝试实现。有疑问请在杜赛的个人网站留言,我会尽快回复。或Email私信我:dusaiphoto@foxmail.com项目完整代码:Django_blog_tutorial ...

January 30, 2019 · 2 min · jiezi

[译]PEP 342--增强型生成器:协程

PEP原文 : https://www.python.org/dev/pe...PEP标题: Coroutines via Enhanced GeneratorsPEP作者: Guido van Rossum, Phillip J. Eby创建日期: 2005-05-10合入版本: 2.5译者 :豌豆花下猫(Python猫 公众号作者)目录简介动机规格摘要规格:将值发送到生成器新的生成器方法:send(value)新的语法:yield 表达式规格:异常和清理新语法:yield 允许在try-finally中新的生成器方法:throw(type,value = None,traceback = None)新的标准异常:GeneratorExit新的生成器方法:close()新的生成器方法:del()可选的扩展扩展的 continue 表达式未决问题示例参考实现致谢参考文献版权简介这个 PEP 在生成器的 API 和语法方面,提出了一些增强功能,使得它们可以作为简单的协程使用。这基本上是将下述两个 PEP 的想法结合起来,如果它被采纳,那它们就是多余的了:PEP-288,关于生成器的属性特征与异常(Attributes and Exceptions)。当前 PEP 沿用了它的下半部分,即生成器的异常(事实上,throw() 的方法名就取自 PEP-288)。PEP-342 用 yield 表达式(这个概念来自 PEP-288 的早期版本)来替换了生成器的属性特征。PEP-325,生成器支持释放资源。PEP-342 收紧了 PEP-325 中的一些松散的规范,使其更适用于实际的实现。(译注:PEP-288 和 PEP-325 都没有被采纳通过,它们的核心内容被集成到了 PEP-342里。)动机协程是表达许多算法的自然方式,例如模拟/仿真、游戏、异步 I/O、以及其它事件驱动编程或协同的多任务处理。Python 的生成器函数几乎就是协程——但不完全是——因为它们允许暂停来生成值,但又不允许在程序恢复时传入值或异常。它们也不允许在 try-finally 结构的 try 部分作暂停,因此很难令一个异常退出的(aborted)协程来清理自己。同样地,当其它函数在执行时,生成器不能提供控制,除非这些函数本身是生成器,并且外部生成器之所以写了去 yield,是要为了响应内部生成器所 yield 的值。这使得即使是相对简单的实现(如异步通信)也变得复杂,因为调用任意函数,要么需要生成器变堵塞(block,即无法提供控制),要么必须在每个要调用的函数的周围,添加一大堆引用循环代码(a lot of boilerplate looping code)。但是,如果有可能在生成器挂起的点上传递进来值或者异常,那么,一个简单的协程调度器或蹦床函数(trampoline function)就能使协程相互调用且不用阻塞——对异步应用程序有巨大好处。这些应用程序可以编写协程来运行非阻塞的 socket I/O,通过给 I/O 调度器提供控制,直到数据被发送或变为可用。同时,执行 I/O 的代码只需像如下方式操作,就能暂停执行,直到 nonblocking_read() 继续产生一个值:data = (yield nonblocking_read(my_socket, nbytes))换句话说, 通过给语言和生成器类型增加一些相对较小的增强,Python 不需要为整个程序编写一系列回调,就能支持异步操作,并且对于本该需要数百上千个协作式的多任务伪线程的(co-operatively multitasking pseudothreads)程序,也可以不需要使用资源密集型线程。因此,这些增强功能将给标准 Python 带来 Stackless Python 的许多优点,又无需对 CPython 核心及其 API 进行任何重大的修改。此外,这些增强在任何已经支持生成器的 Python 实现(例如 Jython)上都是可落实的。规格摘要通过给生成器类型增加一些简单的方法,以及两个微小的语法调整,Python 开发者就能够使用生成器函数来实现协程与其它的协作式多任务。这些方法和调整是:重定义 yield 为表达式(expression),而不是语句(statement)。当前的 yield 语句将变成一个 yield 表达式,其值将被丢弃。每当通过正常的 next() 调用来恢复生成器时,yield 表达式的返回值是 None。为生成器(generator-iterator)添加一个新的 send() 方法,它会恢复生成器,并且 send 一个值作为当前表达式的结果。send() 方法返回的是生成器产生的 next 值,若生成器没有产生值就退出的话,则抛出 StopIteration 。为生成器(generator-iterator)添加一个新的 throw() 方法,它在生成器暂停处抛出异常,并返回生成器产生的下一个值,若生成器没有产生值就退出的话,则抛出 StopIteration (如果生成器没有捕获传入的异常,或者它引发了其它异常,则该异常会传递给调用者。)为生成器(generator-iterator)添加一个新的 close() 方法,它在生成器暂停处引发 GeneratorExit 。如果生成器在之后引发 StopIteration (通过正常退出,或者已经被关闭)或 GeneratorExit (通过不捕获异常),则 close() 返回给其调用者。如果生成器产生一个值,则抛出 RuntimeError。如果生成器引发任何其它异常,也会传递给调用者。如果生成器已经退出(异常退出或正常退出),则 close() 不执行任何操作。增加了支持,确保即使在生成器被垃圾回收时,也会调用 close()。允许 yield 在 try-finally 块中使用,因为现在允许在 finally 语句中执行垃圾回收或显式地调用 close() 。实现了所有这些变更的原型补丁已经可用了,可作为当前 Python CVS HEAD 的 SourceForge 补丁。# 1223381设计规格:将值发送进生成器新的生成器方法:send(value)为生成器提出了一种新的方法,即 send() 。它只接收一个参数,并将它发送给生成器。调用 send(None) 完全等同于调用生成器的 next() 方法。使用其它参数调用 send() 也有同样的效果,不同的是,当前生成器表达式产生的值会不一样。因为生成器在生成器函数体的头部执行,所以在刚刚创建生成器时不会有 yield 表达式来接收值,因此,当生成器刚启动时,禁止使用非 None 参数来调用 send() ,如果调用了,就会抛出 TypeError (可能是由于某种逻辑错误)。所以,在与协程通信前,必须先调用 next() 或 send(None) ,来将程序推进到第一个 yield 表达式。与 next() 方法一样,send() 方法也返回生成器产生的下一个值,或者抛出 StopIteration 异常(当生成器正常退出,或早已退出时)。如果生成器出现未捕获的异常,则它会传给调用者。新语法:yield 表达式yield 语句(yield-statement)可以被用在赋值表达式的右侧;在这种情况下,它就是 yield 表达式(yield-expression)。除非使用非 None 参数调用 send() ,否则 yield 表达式的值就是 None。见下文。yield 表达式必须始终用括号括起来,除非它是作为顶级表达式而出现在赋值表达式的右侧。所以,下面例子都是合法的:x = yield 42x = yieldx = 12 + (yield 42)x = 12 + (yield)foo(yield 42)foo(yield)而下面的例子则是非法的(举了一些特例的原因是,当前的 yield 12,42 是合法的):x = 12 + yield 42x = 12 + yieldfoo(yield 42, 12)foo(yield, 12)请注意,如今没有表达式的 yield-语句 和 yield-表达式是合法的。这意味着:当 next() 调用中的信息流被反转时,应该可以在不传递显式的值的情况下 yield (yield 当然就等同于 yield None)。当调用 send(value) 时,它恢复的 yield 表达式将返回传入的值。当调用 next() 时,它恢复的 yield 表达式将返回 None。如果 yield-表达式(yield-expression)是一个 yield-语句(yield-statement),其返回值会被忽略,就类似于忽略用作语句的函数的返回值。实际上,yield 表达式就像一个反函数调用(inverted function);它所 yield 的值实际上是当前函数返回(生成)的,而它 return 的值则是通过 send() 传入的参数。提示:这样的拓展语法,使得它非常地接近于 Ruby。这是故意的。请注意,Python 在阻塞时,通过使用 send(EXPR) 而不是 return EXPR 来传值给生成器,并且在生成器与阻塞之间传递控制权的底层机制完全不同。Python 中的阻塞不会被编译成 thunk,相反,yield 暂停生成器的执行进度。有一些不是这样的特例,在 Python 中,你不能保存阻塞以供后续调用,并且你无法测试是否存在着阻塞。(XXX - 关于阻塞的这些东西似乎不合适,或许 Guido 会编辑下,做澄清。)设计规格:异常和清理让生成器对象成为通过调用生成器函数而生成的迭代器。本节中的 g 指的都是生成器对象。新语法:yield 允许在 try-finally 里生成器函数的语法被拓展了,允许在 try-finally 语句中使用 yield 语句。新的生成器方法:throw(type,value = None,traceback = None)g.throw(type, value, traceback) 会使生成器在挂起的点处抛出指定的异常(即在 yield 语句中,或在其函数体的头部、且还未调用 next() 时)。如果生成器捕获了异常,并生成了新的值,则它就是 g.throw() 的返回值。如果生成器没有捕获异常,那 throw() 也会抛出同样的异常(它溜走了)。如果生成器抛出其它异常(包括返回时产生的 StopIteration),那该异常会被 throw() 抛出。总之,throw() 的行为类似于 next() 或 send(),除了它是在挂起点处抛出异常。如果生成器已经处于关闭状态,throw() 只会抛出经过它的异常,而不去执行生成器的任何代码。抛出异常的效果完全像它所声明的那样:raise type, value, traceback会在暂停点执行。type 参数不能是 None,且 type 与 value 的类型必须得兼容。如果 value 不是 type 的实例(instance),则按照 raise 语句创建异常实例的规则,用 value 来生成新的异常实例。如果提供了 traceback 参数,则它必须是有效的 Python 堆栈(traceback)对象,否则会抛出 TypeError 。注释:选择 throw() 这个名称,有几个原因。Raise 是一个关键字,因此不能作为方法的名称。与 raise 不同(它在执行点处即时地抛出异常),throw() 首先恢复生成器,然后才抛出异常。单词 throw 意味着将异常抛在别处,并且跟其它语言相关联。考虑了几个替代的方法名:resolve(), signal(), genraise(), raiseinto() 和 flush() 。没有一个像 throw() 那般合适。新的标准异常:GeneratorExit定义了一个新的标准异常 GeneratorExit,继承自 Exception。生成器应该继续抛出它(或者就不捕获它),或者通过抛出 StopIteration 来处理这个问题。新的生成器方法:close()g.close() 由以下伪代码定义:def close(self): try: self.throw(GeneratorExit) except (GeneratorExit, StopIteration): pass else: raise RuntimeError(“generator ignored GeneratorExit”) # Other exceptions are not caught新的生成器方法:del()g.__ del __() 是 g.close() 的装饰器。当生成器对象被作垃圾回收时,会调用它(在 CPython 中,则是它的引用计数变为零时)。如果 close() 引发异常, 异常的堆栈信息(traceback)会被打印到 sys.stderr 并被忽略掉;它不会退回到触发垃圾回收的地方。这与类实例在处理 del()的异常时的方法一样。如果生成器对象被循环引用,则可能不会调用 g.del() 。这是当前 CPython 的垃圾收集器的表现。做此限制的原因是,GC 代码需要在一个任意点打破循环,以便回收它,在此之后,不允许 Python 代码“看到”形成循环的对象,因为它们可能处于无效的状态。被用于解开(hanging off)循环的对象不受此限制。尽管实际上不太可能看到生成器被循环引用。但是,若将生成器对象存储在全局变量中,则会通过生成器框架的 f_globals 指针创建一个循环。另外,若在数据结构中存储对生成器对象的引用,且该数据结构被作为参数传递给生成器,这也会创造一个循环引用(例如,如果一个对象具有一个作为生成器的方法,并持有由该方法创建的运行中的迭代器的引用)。鉴于生成器的典型用法,这些情况都不太可能。此外,CPython 在实现当前 PEP 时,每当由于错误或正常退出而终止执行时,会释放被生成器使用的框架对象(frame object)。这保证了那些无法被恢复的生成器不会成为无法回收的循环引用的部分。这就允许了其它代码在 try-finally 或 with 语句中使用 close() (参考 PEP-343),确保了给定的生成器会正确地完结。可选扩展扩展的 continue 语句本 PEP 的早期草案提出了一种新的 continue EXPR 语法,用于 for 循环(继承自 PEP-340),将 EXPR 的值传给被遍历的迭代器。此功能暂时被撤销了,因为本 PEP 的范围已经缩小,只关注将值传给生成器迭代器(generator-iterator),而非其它类型的迭代器。Python-Dev 邮件列表中的一些人也觉得为这个特定功能添加新语法是为时过早(would be premature at best)。未决问题Python-Dev 邮件的讨论提出了一些未决的问题。我罗列于此,附上我推荐的解决方案与它的动机。目前编写的 PEP 也反映了这种喜好的解决方案。当生成器产生另一个值作为对“GeneratorExit”异常的响应时,close()应该引发什么异常?我最初选择了 TypeError ,因为它表示生成器函数发生了严重的错误行为,应该通过修改代码来修复。但是 PEP-343 中的 with_template 装饰器类使用了 RuntimeError 来进行类似处理。可以说它们都应该使用相同的异常。我宁愿不为此目的引入新的异常类,因为它不是我希望人们捕获的异常:我希望它变成一个 traceback 给程序员看到,然后进行修复。所以我觉得它们都应该抛出 RuntimeError 。有一些先例:在检测到无限递归的情况下,或者检测到未初始化的对象(由于各种各样的原因),核心 Python 代码会抛出该异常。Oren Tirosh 建议将 send() 方法重命名为 feed() ,以便能跟 consumer 接口兼容(规范参见:http://effbot.org/zone/consumer.htm)。然而,仔细观察 consumer 接口,似乎 feed() 所需的语义与 send() 不同,因为后者不能在刚启动的生成器上作有意义的调用。此外,当前定义的 consumer 接口不包含对 StopIteration 的处理。因此,创建一个贴合 consumer 接口的简单的装饰器,来装饰生成器函数,似乎会更有用。举个例子,它可以用初始的 next() 调用给生成器预热(warm up),追踪 StopIteration,甚至可以通过重新调用生成器来提供 reset() 用途。示例一个简单的 consumer 装饰器,它使生成器函数在最初调用时,就自动地前进到第一个 yield 点:def consumer(func): def wrapper(args,**kw): gen = func(args, **kw) gen.next() return gen wrapper.name = func.name wrapper.dict = func.dict wrapper.doc = func.doc return wrapper一个使用 consumer 装饰器创建反向生成器(reverse generator)的示例,该生成器接收图像并创建缩略图,再发送给其它 consumer。像这样的函数可以链接在一起,形成 consumer 间的高效处理流水线,且每个流水线都可以具有复杂的内部状态:@consumerdef thumbnail_pager(pagesize, thumbsize, destination): while True: page = new_image(pagesize) rows, columns = pagesize / thumbsize pending = False try: for row in xrange(rows): for column in xrange(columns): thumb = create_thumbnail((yield), thumbsize) page.write( thumb, colthumbsize.x, rowthumbsize.y ) pending = True except GeneratorExit: # close() was called, so flush any pending output if pending: destination.send(page) # then close the downstream consumer, and exit destination.close() return else: # we finished a page full of thumbnails, so send it # downstream and keep on looping destination.send(page)@consumerdef jpeg_writer(dirname): fileno = 1 while True: filename = os.path.join(dirname,“page%04d.jpg” % fileno) write_jpeg((yield), filename) fileno += 1# Put them together to make a function that makes thumbnail# pages from a list of images and other parameters.#def write_thumbnails(pagesize, thumbsize, images, output_dir): pipeline = thumbnail_pager( pagesize, thumbsize, jpeg_writer(output_dir) ) for image in images: pipeline.send(image) pipeline.close()一个简单的协程调度器或蹦床(trampoline),它允许协程通过 yield 其它协程,来调用后者。被调用的协程所产生的非生成器的值,会被返回给调用方的协程。类似地,如果被调用的协程抛出异常,该异常也会传导给调用者。实际上,只要你用 yield 表达式来调用协程(否则会阻塞),这个例子就模拟了 Stackless Python 中使用的简单的子任务(tasklet)。这只是一个非常简单的例子,但也可以使用更复杂的调度程序。(例如,现有的 GTasklet 框架 (http://www.gnome.org/~gjc/gtasklet/gtasklets.html) 和 peak.events 框架 (http://peak.telecommunity.com/) 已经实现类似的调度功能,但大多数因为无法将值或异常传给生成器,而必须使用很尴尬的解决方法。)import collectionsclass Trampoline: “““Manage communications between coroutines””” running = False def init(self): self.queue = collections.deque() def add(self, coroutine): “““Request that a coroutine be executed””” self.schedule(coroutine) def run(self): result = None self.running = True try: while self.running and self.queue: func = self.queue.popleft() result = func() return result finally: self.running = False def stop(self): self.running = False def schedule(self, coroutine, stack=(), val=None, *exc): def resume(): value = val try: if exc: value = coroutine.throw(value,*exc) else: value = coroutine.send(value) except: if stack: # send the error back to the “caller” self.schedule( stack[0], stack[1], *sys.exc_info() ) else: # Nothing left in this pseudothread to # handle it, let it propagate to the # run loop raise if isinstance(value, types.GeneratorType): # Yielded to a specific coroutine, push the # current one on the stack, and call the new # one with no args self.schedule(value, (coroutine,stack)) elif stack: # Yielded a result, pop the stack and send the # value to the caller self.schedule(stack[0], stack[1], value) # else: this pseudothread has ended self.queue.append(resume)一个简单的 echo 服务器以及用蹦床原理实现的运行代码(假设存在 nonblocking_read 、nonblocking_write 和其它 I/O 协程,该例子在连接关闭时抛出 ConnectionLost ):# coroutine function that echos data back on a connected# socket#def echo_handler(sock): while True: try: data = yield nonblocking_read(sock) yield nonblocking_write(sock, data) except ConnectionLost: pass # exit normally if connection lost# coroutine function that listens for connections on a# socket, and then launches a service “handler” coroutine# to service the connection#def listen_on(trampoline, sock, handler): while True: # get the next incoming connection connected_socket = yield nonblocking_accept(sock) # start another coroutine to handle the connection trampoline.add( handler(connected_socket) )# Create a scheduler to manage all our coroutinest = Trampoline()# Create a coroutine instance to run the echo_handler on# incoming connections#server = listen_on( t, listening_socket(“localhost”,“echo”), echo_handler)# Add the coroutine to the schedulert.add(server)# loop forever, accepting connections and servicing them# “in parallel”#t.run()参考实现实现了本 PEP 中描述的所有功能的原型补丁已经可用,参见 SourceForge 补丁 1223381 (https://bugs.python.org/issue1223381)。该补丁已提交到 CVS,2005年8月 01-02。致谢Raymond Hettinger (PEP 288) 与 Samuele Pedroni (PEP 325) 第一个正式地提出将值或异常传递给生成器的想法,以及关闭生成器的能力。Timothy Delaney 建议了本 PEP 的标题,还有 Steven Bethard 帮忙编辑了早期的版本。另见 PEP-340 的致谢部分。参考文献TBD.版权本文档已经放置在公共领域。源文档:https://github.com/python/peps/blob/master/pep-0342.txt—————-(译文完)——————–相关链接: PEP背景知识 :学习Python,怎能不懂点PEP呢?PEP翻译计划 :https://github.com/chinesehua…[[译] PEP 255–简单的生成器](https://mp.weixin.qq.com/s/vj...[[译]PEP 525–异步生成器](https://mp.weixin.qq.com/s/fy…花下猫语: 唠叨几句吧,年前这几周事情太多了,挤着时间好歹是又翻译出一篇 PEP。与生成器密切相关的 PEP 已经完成 3/4,年后再译最后一篇(PEP-380)。当初翻译第一篇,完全是一时兴起,直觉这是一件有意义的事,现在呢,这个念头开始有点膨胀——我竟然在 Github 上建了个翻译项目。我深知,自己水平实在有限,因此不求得到多少认同吧。但行好事,莫问前程。不过,若有人帮着吆喝一声,也是极好的。 —————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

January 27, 2019 · 5 min · jiezi

JWT验证

JWT(Json Web Token):是目前最流行的跨域身份验证解决方案。此前我们使用的身份验证方式都是基于Session:这种方式并没有什么不妥,但其实这里有三个缺点:Session一般存储在redis中,而redis数据保存在内存中,随着用户的增多,内存消耗太大。扩展性不好,用户每次验证都需要请求session服务器,增大了负载均衡能力,应用扩展受限。因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。所以,我们需要一种既能实现相同要求并且还要比session存储更有效的身份验证方式。JWT通过一种加密的方式,将加密后的数据保存返回给用户本地进行保存,我们称为token数据。其数据由三部分组成:1、header声明类型和加密的算法:{ ’typ’: ‘JWT’, #固定值 ‘alg’: ‘HS256’ #加密算法}2、payload负载这是有效信息的存放地方,其分为三部分:标准中注册的声明、公共声明、私有声明(用户信息)标准中的注册声明(有需要在使用,不强制使用):iss: jwt签发者sub: jwt所面向的用户aud: 接收jwt的一方exp: jwt的过期时间,这个过期时间必须要大于签发时间nbf: 定义在什么时间之前,该jwt都是不可用的.iat: jwt的签发时间jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。公共声明:公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.私有声明:{ “name”: “jim”, “id”: “111111”, “admin”: true}3、signature签名需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。由于base64是对称加密算法,所以可以轻松解密:因此我们在负载部分不要将私密信息放置在里面,只需要把能验证唯一的标识信息添加就可以了。有关base64,请参考:https://www.liaoxuefeng.com/w…由于目前在学习DRF,所以我介绍一下怎样在DRF项目中使用JWT进行身份验证:安装djangorestframework-jwt:pip install djangorestframework-jwt添加jwt认证类:REST_FRAMEWORK = { ‘DEFAULT_PERMISSION_CLASSES’: ( ‘rest_framework.permissions.IsAuthenticated’, ), ‘DEFAULT_AUTHENTICATION_CLASSES’: ( ‘rest_framework_jwt.authentication.JSONWebTokenAuthentication’, ‘rest_framework.authentication.SessionAuthentication’, ‘rest_framework.authentication.BasicAuthentication’, ),}添加jwt路由用于生成token:from rest_framework_jwt.views import obtain_jwt_tokenurlpatterns = [ url(r’^自己的路由/’, obtain_jwt_token),]然后我们就可以通过自己添加的路由并通过post添加username和password来获取到token了,在进行访问页面的时候我们只需要在请求头中添加一个:Authorization: JWT <your_token>,就可以得到验证了。本文参考:https://lion1ou.win/2017/01/18/

January 27, 2019 · 1 min · jiezi

DRF跨域后端解决之django-cors-headers

在使用django-rest-framework开发项目的时候我们总是避免不了跨域的问题,因为现在大多数的项目都是前后端分离,前后端项目部署在不同的web服务器上,因为我们是后端程序员,因此我要通过后端的程序实现跨域。当然如果前端框架是Vue的话,则可以代理服务实现跨域,我也就知道一点点,如果有兴趣,大家可以自行搜索哦。DRF后端实现跨域第三方扩展———djangocorsheaders,在介绍之前,我先介绍两个概念:同源策略、跨域同源策略同源策略/SOP(Same origin policy)是一种约定,是浏览器的一种安全机制。这里同源需要"协议+域名+端口"三者都相同,否则不能进行Ajax访问。跨域不同源之间的网站通信就是跨域。安装pip install django-cors-headers注册INSTALLED_APPS = ( ‘corsheaders’,)添加中间件MIDDLEWARE = [ ‘corsheaders.middleware.CorsMiddleware’, #最好添加至第一行]配置白名单#单个配置CORS_ORIGIN_WHITELIST =( ’ 域名’,)#正则配置:CORS_ORIGIN_REGEX_WHITELIST =(r’^(https?://)?(\w+.)?jim.com $’,)或者直接允许所有主机跨域CORS_ORIGIN_ALLOW_ALL = True 默认为False一般情况下,我们配置这些就足够,当然最为一个出名的扩展,肯定做的很完美,更多的配置,请访问:https://github.com/ottoyiu/dj…

January 25, 2019 · 1 min · jiezi

我为什么建议你学Python?献上Python学习大礼包,拿走别客气!

编程语言首推Python,为什么这么说呢?Python在2017年世界脚本语言排行榜中排名第1,也是多领域首选语言,作为一种高级程序语言,其核心设计哲学是代码可读性和语法,能够让程序员用很少的代码来表达自己的想法。打个比方,同样一项工作C语言可能要1000行,java要100行,python可能只要10行。像Google,facebook,Yahoo,YouTube,还有美国宇航局NASA,著名的开源云计算平台openstack,还有国内的豆瓣都是用python写的。目前Python工程师正处于需求量大,人才供不应求的阶段,薪资一路也是水涨船高。北京Python工程师的薪资平均为18880每月,即使是刚刚毕业的应届毕业生,做Python在人工智能领域的薪资也在12500元每月。数据显示,2017年在雇主发布的职位说明中,Python技能需求增速达到174%,居于首位。未来十年将是大数据、人工智能爆发的时代,到时将会有大量的数据需要处理,而Python最大的优势,就是对数据的处理,有着得天独厚的优势。其实无论你的工作是什么,对每个人来说,学习如何编程都是很重要的一件事,编程不仅有助于丰富你的计算思维,还能提高决策性,让你在招聘中脱颖而出。学长为了大家能跟上互联网时代发展的趋势,特意为大家准备了一份Python大礼包供大家学习。这份大礼包有什么?如何获取呢?关注,转发,加QQ群:700341555即可免费领取,希望对你们有帮助!

January 25, 2019 · 1 min · jiezi

PyCasbin: 支持 ACL、RBAC、ABAC 多种模型的 Python 权限管理框架

PyCasbin 是一个用 Python 语言打造的轻量级开源访问控制框架( https://github.com/casbin/pyc… ),目前在 GitHub 开源。PyCasbin 采用了元模型的设计思想,支持多种经典的访问控制方案,如基于角色的访问控制 RBAC、基于属性的访问控制 ABAC 等。PyCasbin 的主要特性包括1.支持自定义请求的格式,默认的请求格式为{subject, object, action};2.具有访问控制模型 model 和策略 policy 两个核心概念;3.支持 RBAC 中的多层角色继承,不止主体可以有角色,资源也可以具有角色;4.支持超级用户,如 root 或 Administrator,超级用户可以不受授权策略的约束访问任意资源;5.支持多种内置的操作符,如 keyMatch,方便对路径式的资源进行管理,如 /foo/bar 可以映射到 /foo*PyCasbin 不做的事情:1.身份认证 authentication (即验证用户的用户名、密码),PyCasbin 只负责访问控制。应该有其他专门的组件负责身份认证,然后由 PyCasbin 进行访问控制,二者是相互配合的关系;2.管理用户列表或角色列表。PyCasbin 认为由项目自身来管理用户、角色列表更为合适,PyCasbin 假设所有策略和请求中出现的用户、角色、资源都是合法有效的。安装pip install casbinHelloWorld 例子1.初始化一个 enforcer,传入两个参数:模型文件路径和策略文件路径;import casbine = casbin.Enforcer(“path/to/model.conf”, “path/to/policy.csv”)2.在你的代码需要进行访问控制的位置,加入如下钩子;sub = “alice” # the user that wants to access a resource.obj = “data1” # the resource that is going to be accessed.act = “read” # the operation that the user performs on the resource.if e.enforce(sub, obj, act): # permit alice to read data1 passelse: # deny the request, show an error pass3.采用管理 API 进行权限的管理,如获取一个用户所有的角色;roles = e.get_roles(“alice”)社区进展PyCasbin 目前正在积极向社区进行推送,并且可以通过插件的方式已经支持与 Django 等 Web 框架进行集成,将来会推广到更多 Web 框架以及社区。Casbin 已经有 Golang 版本、Java 版本、PHP 版本、Node.js 版本、Pytho n版本 等主流语言版本。有跨语言需求的开发者可以只用 Casbin 这一套框架就实现多个不同语言的项目的权限管理任务。PyCasbin (Python): https://github.com/casbin/pyc...Casbin (Go): https://github.com/casbin/casbinjCasbin (Java): https://github.com/casbin/jca...PHP-Casbin (PHP): https://github.com/php-casbin...Node-Casbin (Node.js): https://github.com/casbin/nod…协议PyCasbin 采用 Apache 2.0 开源协议发布。联系作者有问题请提交 Issues: https://github.com/casbin/pyc…或者加入 QQ 群:546057381( Casbin 访问控制讨论群) ...

January 25, 2019 · 1 min · jiezi

一篇文章搞懂Jinja2 Template Engine 模版引擎

Flask和Django,以及其它很多Python框架,都默认使用Jinja2来作为模版引擎。在Python中,什么是模版?就是在一个静态HTML加入一些类似变量的标签,然后引擎在渲染这个HTML时候会动态的把变量填入内容,生成一个最终的HTML。什么是模版引擎?其实就是一种能解析类似Python语言的标记语言的解释器。比如我们在HTML模版中输入一个<p> {{ post.title }} </p>,显然这不是真正的HTML语法。但是当Jinja2解释器读取到{{ …}}后知道里面是一个变量,那么就把这个变量替换为真正的值,最后翻译出来就变成了<p> 大标题 </p>这样的HTML内容。Jinja2是一个模版语言,只是类似Python,比较符合Python语法,但不完全相同!所有的模版引擎,实际上都差不多,不管是基于VBS语言的ASP模版,还是基于PHP语言的PHP模版,都不是与原本语言一摸一样,而只是做到尽量一样而已。Jinja2语言基础注意:Jinja2模版语言,是不区分缩进的,和纯python不同。实际上所有模版语言都不区分缩紧。常用标记:注释:{# 这是注释 #}变量:{{ post.title }},或字典元素{{your_dict[‘key’]}},或列表{{your_list[0]}}多行代码块:{% 开始 %} HTML标签 {% 结束 %}示例:{% if user %} {{ user }}{% else %} hello! {% for index in indexs %} {{ index }} {% endfor %}Jinja2 Filter 过滤器 (即函数)一个filter过滤器的本质就是一个function函数。使用格式为:变量名 | 函数。它做到的就是,把变量传给函数,然后再把函数返回值作为这个代码块的值。如:<!– 带参数的 –>{{变量 | 函数名(*args)}}<!– 不带参数可以省略括号 –>{{变量 | 函数名}}链式调用(管道式):和命令行的pipline管道一样,可以一次调用多个函数(过滤器),如:{{ “hello world” | reverse | upper }}文本块调用(将中间的所有文字都作为变量内容传入到过滤器中):{% filter upper %} 一大堆文字{% endfilter %}Jinja2常用内置函数(过滤器)字符串操作:safe:禁用转义<p>{{ ‘<em>hello</em>’ | safe }}</p>capitalize:把变量值的首字母转成大写,其余字母转小写<p>{{ ‘hello’ | capitalize }}</p>lower:把值转成小写<p>{{ ‘HELLO’ | lower }}</p>upper:把值转成大写<p>{{ ‘hello’ | upper }}</p>title:把值中的每个单词的首字母都转成大写<p>{{ ‘hello’ | title }}</p>reverse:字符串反转<p>{{ ‘olleh’ | reverse }}</p>format:格式化输出<p>{{ ‘%s is %d’ | format(’name’,17) }}</p>striptags:渲染之前把值中所有的HTML标签都删掉<p>{{ ‘<em>hello</em>’ | striptags }}</p>truncate: 字符串截断<p>{{ ‘hello every one’ | truncate(9)}}</p>列表操作:first:取第一个元素<p>{{ [1,2,3,4,5,6] | first }}</p>last:取最后一个元素<p>{{ [1,2,3,4,5,6] | last }}</p>length:获取列表长度<p>{{ [1,2,3,4,5,6] | length }}</p>sum:列表求和<p>{{ [1,2,3,4,5,6] | sum }}</p>sort:列表排序<p>{{ [6,2,3,1,5,4] | sort }}</p>Jinja2 Macro 宏 (自定义函数)Jinja2是允许自定义函数的,这样在模版中可以重复利用这个自定义函数。Jinja2称之为Macro宏。定义方法:{% macro 函数名(参数) %} 具体的HTML内容{% endmacro %}<!– 使用 –>{{ 函数名(参数) }}<!– 或作为过滤器 –>{{ 变量 | 函数名(参数) }}关于Jinja2自定义函数的context上下文和环境变量的问题:Jinja2的自定义函数“宏”,本身是没法像filter过滤器函数一样使用上下文和环境变量的。不过可以加上@contextfilter装饰器达到同样的效果。导入另一个文件的自定义函数“宏”:假设在macro.html文件中我们定义了一个函数func()。那么现在我们可以在另一个文件reference.html中像python导入模块一样导入它:{% import ‘macro.html’ as module %}{{ module.func() }}Include 模版引用Include是我们常用的操作,即定义一个框架模版(父模版),然后一个一个指定性的把子模版引入进来。框架模版frame.html如下:{% include ‘header.html’ %}{% include ‘body.html’ %}{% include ‘footer.html’ %}Extend 模版继承我们可以在一个父模版中定义一个block代码块,然后在另一个子模版中“继承”这个父模版,并重写这个block代码块。不过一般模版中的父模版,都只是留出一个block空位,里面不写东西,特意等子模版来实现。假设现在有一个父模版parent.html:{% block HEADER %} 页头部分的HTML内容。{% endblock HEADER %}{% block BODY %} 正文部分的HTML内容。{% endblock BODY %}{% block FOOTER %} 页脚部分的HTML内容。{% endblock FOOTER %}其中定义了三个block,页头、正文和页脚。然后我们就可以定义一个模版child.html来继承父模版,并且只重写BODY部分:{% extends ‘parent.html’ %}{% block BODY %} 由子页面重写改写的的HTML内容,替换父页面的BODY。。。{% endblock BODY %}扩展完成后,我们最终得到的结果是:{% block HEADER %} 页头部分的HTML内容。{% endblock HEADER %}{% block BODY %} 由子页面重写改写的的HTML内容,替换父页面的BODY。。。{% endblock BODY %}{% block FOOTER %} 页脚部分的HTML内容。{% endblock FOOTER %}Jinja2模版引用Flask路由中的内容在Flask应用Jinja2模版时,在模版中可以直接调用Flask app中的一些公用变量和方法。引用Flask的request对象:<p> {{ request.url }} </p><p> {{ request.form.get(’name’) }} </p>引用Flask的url_for(…)方法:<!– 它会返回我们定义的路由app.route('/index')所对应的URL –><p> {{ url_for(‘index’) }} </p><!– 它会返回我们定义的路由app.route('/post/{post_id}')所对应的URL –><p> {{ url_for(‘post’, post_id=‘127’) }} </p>在模版中,我们可以引用get_flashed_messages()方法,获取Flask路由传来的闪现信息:{% for msg in get_flashed_messages() %} <p> {{ msg }} </p>{% endfor %}这种闪现信息是从Flask路由中传来的,只要在路由中发一个flash(‘hello’)信息,相当于弹了一个alert()。然后我们可以在Jinja2的模版中用get_flashed_messages()获得flash过来的信息列表。 ...

January 24, 2019 · 2 min · jiezi

大名鼎鼎的Requests库用了什么编码风格?

原文:https://www.kennethreitz.org/…作者:Kenneth Reitz原题:Kenneth Reitz’s Code Style™Requests 的代码库使用 PEP-8 编码风格。除了 PEP-8 中列出的标准外,我们还有一些指导原则:如果方便的话,行长(Line-length)可超过 79 个字符,达到 100 个字符。如果换行会导致严重的不方便,则行长可以超过 100 个字符。除非在字符串中出现单引号,否则始终使用单引号字符串(例如,’#flatearth’)。此外,PEP-8 推荐的用于连续行的编码风格毫无一点品味,绝不允许在 Requests 代码库使用:# 与开局定界符对齐foo = long_function_name(var_one, var_two, var_three, var_four)No。千万别。请。文档字符串(docstrings)应遵循以下语法:def the_earth_is_flat(): “““NASA divided up the seas into thirty-three degrees.””” passdef fibonacci_spiral_tool(): “““With my feet upon the ground I lose myself / between the sounds and open wide to suck it in. / I feel it move across my skin. / I’m reaching up and reaching out. / I’m reaching for the random or whatever will bewilder me. / Whatever will bewilder me. / And following our will and wind we may just go where no one’s been. / We’ll ride the spiral to the end and may just go where no one’s been. Spiral out. Keep going… "”” pass所有函数、方法和类都要求包含 docstrings 。除了对象数据模型方法(例如,repr),这些是此规则的例外。Thanks for helping to make the world a better place!资料来源(译注:即 Requests 的开发者指南):http://t.cn/E5VgNJF(译文完)K 神的这篇文章很短,实际上,这只是摘自 Requests 的开发者指南的一小部分。但是,关于灵活设定行长的部分,我举双手双脚赞同。如果你所在的公司有“清白盒”的优良传统(不仅指Python),那你极有可能遇到被迫换行的麻烦,而实际上才仅仅刚刚超出了几个字符。那时候,你就会明白,这 3 条灵活规则的好处了。另外,关于连续行的部分,PEP-8 相关内容在:http://t.cn/Rq4mxOoPEP-8 反对的是如下写法:# Arguments on first line forbidden when not using vertical alignment.# 不使用垂直对齐的参数禁止在第一行上foo = long_function_name(var_one, var_two, var_three, var_four)PEP-8 推荐的写法是垂直地将换行的参数对齐起始的参数:# 与开局定界符对齐foo = long_function_name(var_one, var_two, var_three, var_four)K 神反对了 PEP-8 推荐的写法。在我看来,任何有品味的人,都会反对以上的两种写法。即使一个方法的参数超级多,超出了 100 个字符,我本人也是极不情愿换行的。所以,K 神的说法深得我心。关于代码风格,没有绝对完全一致的标准。本文也不想引起争论。不过,我认同 K 神设定的规则,因为一种与主流不同的审美倾向,值得发现它的同类。—————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

January 20, 2019 · 1 min · jiezi

Django+vue跨域问题解决

跨域由于开发模式为前后端分离式开发,故而通常情况下,前端和后端可能运行不同的ip或者port下,导致出现跨域问题,故而单独说明什么是跨域跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。什么是同源策略?同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。同源策略限制以下几种行为:Cookie、LocalStorage 和 IndexDB 无法读取DOM 和 Js对象无法获得AJAX 请求不能发送跨域错误浏览器会在控制台中出现如下错误:报错信息如下:Access to XMLHttpRequest at ‘http://127.0.0.1:8000/api/test/’ from origin ‘http://127.0.0.1:3000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.翻译过来即是:从源地址 http://127.0.0.1:3000 发起的到http://127.0.0.1:8000/api/test/ 的XMLHttpRequest访问违反了同源策略:因为在请求头中没有Access-Control-Allow-Origin的值前端解决跨域参考博客既然跨域是因为不同源,那我同源不就完事儿了,但是后端请求地址不可能改变,所以可以在前端和后端的中间加一层代理,前端通过代理访问后端。在Vue-cli工具中已经提供了代理的功能,只需要配置即可。在根目录下的config/index.js文件中有如下配置项:proxyTable: { ‘/’: { target: ‘http://127.0.0.1:8000/’, changeOrigin: true, pathRewrite: { ‘^/api’: ’’ } } },’/‘表示以’/’(即所有的路径)开头的路径均需要代理,target:代理的目标服务器地址(即后端服务器地址)为 ‘http://127.0.0.1:8000/’,changeOrigin,为修改源:修改请求中的源地址pathReWrite:URL路径重写,对于以’/api’开头的路径将’/api’替换为’‘后端解决跨域参考博客后端出于安全考虑,也会对于跨域有限制,解决方法如下:安装django-cors-headers$ pip install django-cors-headers配置settings.py文件INSTALLED_APPS = [ … ‘corsheaders’, … ] MIDDLEWARE_CLASSES = ( … ‘corsheaders.middleware.CorsMiddleware’, ‘django.middleware.common.CommonMiddleware’, # 注意顺序 …)#跨域增加忽略CORS_ALLOW_CREDENTIALS = TrueCORS_ORIGIN_ALLOW_ALL = TrueCORS_ORIGIN_WHITELIST = ( ‘*’)CORS_ALLOW_METHODS = ( ‘DELETE’, ‘GET’, ‘OPTIONS’, ‘PATCH’, ‘POST’, ‘PUT’, ‘VIEW’,)CORS_ALLOW_HEADERS = ( ‘XMLHttpRequest’, ‘X_FILENAME’, ‘accept-encoding’, ‘authorization’, ‘content-type’, ‘dnt’, ‘origin’, ‘user-agent’, ‘x-csrftoken’, ‘x-requested-with’, ‘Pragma’,) ...

January 19, 2019 · 1 min · jiezi

[译] PEP 255--简单的生成器

我正打算写写 Python 的生成器,然而查资料时发现,引入生成器的 PEP 没人翻译过,因此就花了点时间翻译出来。如果在阅读时,你有读不懂的地方,不用怀疑,极有可能是我译得不到位。若出现这种情况,我建议你直接阅读原文,最好也能将错误处告知于我,以便做出修改。原文:https://www.python.org/dev/pe…创建日期:2001-05-18合入Python版本:2.2译者 :豌豆花下猫(Python猫 公众号作者)PEP背景知识 :学习Python,怎能不懂点PEP呢?摘要这个 PEP 想在 Python 中引入生成器的概念,以及一个新的表达式,即 yield 表达式。动机当一个生产者函数在处理某些艰难的任务时,它可能需要维持住生产完某个值时的状态,大多数编程语言都提供不了既舒服又高效的方案,除了往参数列表中添加回调函数,然后每生产一个值时就去调用一下。例如,标准库中的tokenize.py采用这种方法:调用者必须传一个 tokeneater 函数给 tokenize() ,当 tokenize() 找到下一个 token 时再调用。这使得 tokenize 能以自然的方式编码,但程序调用 tokenize 会变得极其复杂,因为它需要记住每次回调前最后出现的是哪个 token(s)。tabnanny.py中的 tokeneater 函数是处理得比较好的例子,它在全局变量中维护了一个状态机,用于记录已出现的 token 和预期会出现的 token 。这很难正确地工作,而且也挺难让人理解。不幸的是,它已经是最标准的解决方法了。有一个替代方案是一次性生成 Python 程序的全部解析,并存入超大列表中。这样 tokenize 客户端可以用自然的方式,即使用局部变量和局部控制流(例如循环和嵌套的 if 语句),来跟踪其状态。然而这并不实用:程序会变得臃肿,因此不能在实现整个解析所需的内存上放置先验限制;而有些 tokenize 客户端仅仅想要查看某个特定的东西是否曾出现(例如,future 声明,或者像 IDLE 做的那样,只是首个缩进的声明),因此解析整个程序就是严重地浪费时间。另一个替代方案是把 tokenize 变为一个迭代器【注释1】,每次调用它的 next() 方法时再传递下一个 token。这对调用者来说很便利,就像前一方案把结果存入大列表一样,同时没有内存与“想要早点退出怎么办”的缺点。然而,这个方案也把 tokenize 的负担转化成记住 next() 的调用状态,读者只要瞄一眼 tokenize.tokenize_loop() ,就会意识到这是一件多么可怕的苦差事。或者想象一下,用递归算法来生成普通树结构的节点:若把它投射成一个迭代器框架实现,就需要手动地移除递归状态并维护遍历的状态。第四种选择是在不同的线程中运行生产者和消费者。这允许两者以自然的方式维护其状态,所以都会很舒服。实际上,Python 源代码发行版中的 Demo/threads/Generator.py 就提供了一个可用的同步通信(synchronized-communication)类,来完成一般的任务。但是,这在没有线程的平台上无法运用,而且就算可用也会很慢(与不用线程可取得的成就相比)。最后一个选择是使用 Python 的变种 Stackless 【注释2-3】来实现,它支持轻量级的协程。它与前述的线程方案有相同的编程优势,效率还更高。然而,Stackless 在 Python 核心层存在争议,Jython 也可能不会实现相同的语义。这个 PEP 不是讨论这些问题的地方,但完全可以说生成器是 Stackless 相关功能的子集在当前 CPython 中的一种简单实现,而且可以说,其它 Python 实现起来也相对简单。以上分析完了已有的方案。其它一些高级语言也提供了不错的解决方案,特别是 Sather 的迭代器,它受到 CLU 的迭代器启发【注释4】;Icon 的生成器,一种新颖的语言,其中每个表达式都是生成器【注释5】。它们虽有差异,但基本的思路是一致的:提供一种函数,它可以返回中间结果(“下一个值”)给它的调用者,同时还保存了函数的局部状态,以便在停止的位置恢复(译注:resum,下文也译作激活)调用。一个非常简单的例子:def fib(): a, b = 0, 1 while 1: yield b a, b = b, a+b当 fib() 首次被调用时,它将 a 设为 0,将 b 设为 1,然后生成 b 给其调用者。调用者得到 1。当 fib 恢复时,从它的角度来看,yield 语句实际上跟 print 语句相同:fib 继续执行,且所有局部状态完好无损。然后,a 和 b 的值变为 1,并且 fib 再次循环到 yield,生成 1 给它的调用者。以此类推。 从 fib 的角度来看,它只是提供一系列结果,就像用了回调一样。但是从调用者的角度来看,fib 的调用就是一个可随时恢复的可迭代对象。跟线程一样,这允许两边以最自然的方式进行编码;但与线程方法不同,这可以在所有平台上高效完成。事实上,恢复生成器应该不比函数调用昂贵。同样的方法适用于许多生产者/消费者函数。例如,tokenize.py 可以生成下一个 token 而不是用它作为参数调用回调函数,而且 tokenize 客户端可以以自然的方式迭代 tokens:Python 生成器是一种迭代器,但是特别强大。设计规格:yield引入了一种新的表达式:yield_stmt:“yield”expression_listyield 是一个新的关键字,因此需要一个 future 声明【注释8】来进行引入:在早期版本中,若想使用生成器的模块,必须在接近头部处包含以下行(详见 PEP 236):from future import generators没有引入 future 模块就使用 yield 关键字,将会告警。 在后续的版本中,yield 将是一个语言关键字,不再需要 future 语句。yield 语句只能在函数内部使用。包含 yield 语句的函数被称为生成器函数。从各方面来看,生成器函数都只是个普通函数,但在它的代码对象的 co_flags 中设置了新的“CO_GENERATOR”标志。当调用生成器函数时,实际参数还是绑定到函数的局部变量空间,但不会执行代码。得到的是一个 generator-iterator 对象;这符合迭代器协议【注释6】,因此可用于 for 循环。注意,在上下文无歧义的情况下,非限定名称 “generator” 既可以指生成器函数,又可以指生成器-迭代器(generator-iterator)。每次调用 generator-iterator 的 next() 方法时,才会执行 generator-function 体中的代码,直至遇到 yield 或 return 语句(见下文),或者直接迭代到尽头。如果执行到 yield 语句,则函数的状态会被冻结,并将 expression_list 的值返回给 next() 的调用者。“冻结”是指挂起所有本地状态,包括局部变量、指令指针和内部堆栈:保存足够的信息,以便在下次调用 next() 时,函数可以继续执行,仿佛 yield 语句只是一次普通的外部调用。限制:yield 语句不能用于 try-finally 结构的 try 子句中。困难的是不能保证生成器会被再次激活(resum),因此无法保证 finally 语句块会被执行;这就太违背 finally 的用处了。限制:生成器在活跃状态时无法被再次激活:>>> def g():… i = me.next()… yield i>>> me = g()>>> me.next()Traceback (most recent call last): … File “<string>”, line 2, in gValueError: generator already executing设计规格:return生成器函数还可以包含以下形式的return语句:return注意,生成器主体中的 return 语句不允许使用 expression_list (然而当然,它们可以嵌套地使用在生成器里的非生成器函数中)。当执行到 return 语句时,程序会正常 return,继续执行恰当的 finally 子句(如果存在)。然后引发一个 StopIteration 异常,表明迭代器已经耗尽。如果程序没有显式 return 而执行到生成器的末尾,也会引发 StopIteration 异常。请注意,对于生成器函数和非生成器函数,return 意味着“我已经完成,并且没有任何有趣的东西可以返回”。注意,return 并不一定会引发 StopIteration :关键在于如何处理封闭的 try-except 结构。 例如:>>> def f1():… try:… return… except:… yield 1>>> print list(f1())[]因为,就像在任何函数中一样,return 只是退出,但是:>>> def f2():… try:… raise StopIteration… except:… yield 42>>> print list(f2())[42]因为 StopIteration 被一个简单的 except 捕获,就像任意异常一样。设计规格:生成器和异常传播如果一个未捕获的异常——包括但不限于 StopIteration——由生成器函数引发或传递,则异常会以通常的方式传递给调用者,若试图重新激活生成器函数的话,则会引发 StopIteration 。 换句话说,未捕获的异常终结了生成器的使用寿命。示例(不合语言习惯,仅作举例):>>> def f():… return 1/0>>> def g():… yield f() # the zero division exception propagates… yield 42 # and we’ll never get here>>> k = g()>>> k.next()Traceback (most recent call last): File “<stdin>”, line 1, in ? File “<stdin>”, line 2, in g File “<stdin>”, line 2, in fZeroDivisionError: integer division or modulo by zero>>> k.next() # and the generator cannot be resumedTraceback (most recent call last): File “<stdin>”, line 1, in ?StopIteration>>>设计规格:Try/Exception/Finally前面提过,yield 语句不能用于 try-finally 结构的 try 子句中。这带来的结果是生成器要非常谨慎地分配关键的资源。但是在其它地方,yield 语句并无限制,例如 finally 子句、except 子句、或者 try-except 结构的 try 子句:>>> def f():… try:… yield 1… try:… yield 2… 1/0… yield 3 # never get here… except ZeroDivisionError:… yield 4… yield 5… raise… except:… yield 6… yield 7 # the “raise” above stops this… except:… yield 8… yield 9… try:… x = 12… finally:… yield 10… yield 11>>> print list(f())[1, 2, 4, 5, 8, 9, 10, 11]>>>示例# 二叉树类class Tree: def init(self, label, left=None, right=None): self.label = label self.left = left self.right = right def repr(self, level=0, indent=" “): s = levelindent + self.label if self.left: s = s + “\n” + self.left.repr(level+1, indent) if self.right: s = s + “\n” + self.right.repr(level+1, indent) return s def iter(self): return inorder(self)# 从列表中创建 Treedef tree(list): n = len(list) if n == 0: return [] i = n / 2 return Tree(list[i], tree(list[:i]), tree(list[i+1:]))# 递归生成器,按顺序生成树标签def inorder(t): if t: for x in inorder(t.left): yield x yield t.label for x in inorder(t.right): yield x# 展示:创建一棵树t = tree(“ABCDEFGHIJKLMNOPQRSTUVWXYZ”)# 按顺序打印树的节点for x in t: print x,print# 非递归生成器def inorder(node): stack = [] while node: while node.left: stack.append(node) node = node.left yield node.label while not node.right: try: node = stack.pop() except IndexError: return yield node.label node = node.right# 练习非递归生成器for x in t: print x,printBoth output blocks display:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z问答为什么重用 def 而不用新的关键字?请参阅下面的 BDFL 声明部分。为什么用新的关键字yield而非内置函数?Python 中通过关键字能更好地表达控制流,即 yield 是一个控制结构。而且为了 Jython 的高效实现,编译器需要在编译时就确定潜在的挂起点,新的关键字会使这一点变得简单。CPython 的实现也大量利用它来检测哪些函数是生成器函数(尽管一个新的关键字替代 def 就能解决 CPython 的问题,但人们问“为什么要新的关键字”问题时,并不想要新的关键字)。为什么不是其它不带新关键字的特殊语法?例如,为何不用下面用法而用 yield 3:return 3 and continuereturn and continue 3return generating 3continue return 3return >> , 3from generator return 3return >> 3return << 3>> 3<< 3 3我没有错过一个“眼色”吧?在数百条消息中,我算了每种替代方案有三条建议,然后总结出上面这些。不需要用新的关键字会很好,但使用 yield 会更好——我个人认为,在一堆无意义的关键字或运算符序列中,yield 更具表现力。尽管如此,如果这引起足够的兴趣,支持者应该发起一个提案,交给 Guido 裁断。为什么允许用return,而不强制用StopIteration?“StopIteration”的机制是底层细节,就像 Python 2.1 中的“IndexError”的机制一样:实现时需要做一些预先定义好的东西,而 Python 为高级用户开放了这些机制。尽管不强制要求每个人都在这个层级工作。 “return”在任何一种函数中都意味着“我已经完成”,这很容易解读和使用。注意,return 并不总是等同于 try-except 结构中的 raise StopIteration(参见“设计规格:Return”部分)。那为什么不允许return一个表达式?也许有一天会允许。 在 Icon 中,return expr 意味着“我已经完成”和“但我还有最后一个有用的值可以返回,这就是它”。 在初始阶段,不强制使用return expr的情况下,使用 yield 仅仅传递值,这很简单明了。BDFL声明Issue引入另一个新的关键字(比如,gen 或 generator )来代替 def ,或以其它方式改变语法,以区分生成器函数和非生成器函数。Con实际上(你如何看待它们),生成器是函数,但它们具有可恢复性。使它们建立起来的机制是一个相对较小的技术问题,引入新的关键字无助于强调生成器是如何启动的机制(生成器生命中至关重要却很小的部分)。Pro实际上(你如何看待它们),生成器函数实际上是工厂函数,它们就像施了魔法一样地生产生成器-迭代器。 在这方面,它们与非生成器函数完全不同,更像是构造函数而不是函数,因此重用 def 无疑是令人困惑的。藏在内部的 yield 语句不足以警示它们的语义是如此不同。BDFLdef 留了下来。任何一方都没有任何争论是完全令人信服的,所以我咨询了我的语言设计师的直觉。它告诉我 PEP 中提出的语法是完全正确的——不是太热,也不是太冷。但是,就像希腊神话中的 Delphi(译注:特尔斐,希腊古都) 的甲骨文一样,它并没有告诉我原因,所以我没有对反对此 PEP 语法的论点进行反驳。 我能想出的最好的(除了已经同意做出的反驳)是“FUD”(译注:缩写自 fear、uncertainty 和 doubt)。 如果这从第一天开始就是语言的一部分,我非常怀疑这早已让安德鲁·库奇林(Andrew Kuchling)的“Python Warts”页面成为可能。(译注:wart 是疣,一种难看的皮肤病。这是一个 wiki 页面,列举了对 Python 吹毛求疵的建议)。参考实现当前的实现(译注:2001年),处于初步状态(没有文档,但经过充分测试,可靠),是Python 的 CVS 开发树【注释9】的一部分。 使用它需要您从源代码中构建 Python。这是衍生自 Neil Schemenauer【注释7】的早期补丁。脚注和参考文献[1] PEP-234, Iterators, Yee, Van Rossumhttp://www.python.org/dev/pep…[2] http://www.stackless.com/[3] PEP-219, Stackless Python, McMillanhttp://www.python.org/dev/pep…[4] “Iteration Abstraction in Sather” Murer, Omohundro, Stoutamire and Szyperski http://www.icsi.berkeley.edu/...[5] http://www.cs.arizona.edu/icon/[6] The concept of iterators is described in PEP 234. See [1] above.[7] http://python.ca/nas/python/g...[8] PEP 236, Back to the future, Petershttp://www.python.org/dev/pep…[9] To experiment with this implementation, check out Python from CVS according to the instructions at http://sf.net/cvs/?group_id=5470 ,Note that the std test Lib/test/test_generators.py contains many examples, including all those in this PEP.版权信息本文档已经放置在公共领域。源文档:https://github.com/python/peps/blob/master/pep-0255.txt(译文完)PS:官方 PEP 有将近500个,然而保守估计,被翻译成中文的不足20个(去重的情况下)。我好奇,感兴趣将一些重要的 PEP 翻译出来的人有多少呢?现抛此问题出来探探路,欢迎留言交流。—————–本文翻译并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

January 19, 2019 · 4 min · jiezi

xadmin之模型类美化修整

今天像大家介绍一下界面的的修整,在此之前为了观看,我们改一下界面的语言展示为中文。看一下结果:之后我们点击一下之前添加的Model(人)修改字段的显示名称修改默认字段显示添加搜索数据功能添加过滤器还有日期过滤器,只能添加日期字段:我没日期字段,就不掩饰了,和前面用法一样:属性为:date_hierarchy

January 17, 2019 · 1 min · jiezi

xadmin的安装与使用

xadmin是什么?Xadmin 采用Python语言编写,框架层基于最成熟的Web框架 Django,是一个基于Django的admin的一个后台模板框架。安装# 方式一pip install xadmin# 方式二# 下载xadmin源码包:https://github.com/sshwsfc/xadminpip install xadmin-master.zip在INSTALLED_APPS注册INSTALLED_APPS = [ ‘django.contrib.admin’, ‘django.contrib.auth’, ‘django.contrib.contenttypes’, ‘django.contrib.sessions’, ‘django.contrib.messages’, ‘django.contrib.staticfiles’, ‘xadmin’, ‘crispy_forms’,]修改路由# 这里我们替换掉admin路由import xadminurlpatterns = [ url(‘xadmin/’, xadmin.site.urls),]这样我们就成功用xadmin替换了admin,可以看一下页面对比(上:原始admin)注册Model到站点# 在app下新建一个adminx.py文件import xadminclass ModelAdmin(object): pass xadmin.site.register(Model类,ModelAdmin管理类)这样我们就成功将模型类添加至了后台管理站点,接下来我们通过一个列子让大家看到效果:其中adminx文件:# -- coding:utf-8 --“““Create by Jim on2019/01/16"““import xadminfrom users.models import Peopleclass PeopleAdmin(object): passxadmin.site.register(People, PeopleAdmin)接下来我们通过manage.py@xadmintest控制台进行数据迁移:manage.py@xadmintest > makemigrations"C:\PyCharm 2017.3.7\bin\runnerw.exe” G:\python\python.exe “C:\PyCharm 2017.3.7\helpers\pycharm\django_manage.py” makemigrations “G:/pycharm project/xadmintest"Migrations for ‘users’: users\migrations\0001_initial.py - Create model PeopleFollowing files were affected G:\pycharm project\xadmintest\users\migrations\0001_initial.pyProcess finished with exit code 0manage.py@xadmintest > migrate"C:\PyCharm 2017.3.7\bin\runnerw.exe” G:\python\python.exe “C:\PyCharm 2017.3.7\helpers\pycharm\django_manage.py” migrate “G:/pycharm project/xadmintest"Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, users, xadminRunning migrations: Applying contenttypes.0001_initial… OK Applying auth.0001_initial… OK Applying admin.0001_initial… OK Applying admin.0002_logentry_remove_auto_add… OK Applying contenttypes.0002_remove_content_type_name… OK Applying auth.0002_alter_permission_name_max_length… OK Applying auth.0003_alter_user_email_max_length… OK Applying auth.0004_alter_user_username_opts… OK Applying auth.0005_alter_user_last_login_null… OK Applying auth.0006_require_contenttypes_0002… OK Applying auth.0007_alter_validators_add_error_messages… OK Applying auth.0008_alter_user_username_max_length… OK Applying sessions.0001_initial… OK Applying users.0001_initial… OK Applying xadmin.0001_initial… OK Applying xadmin.0002_log… OK Applying xadmin.0003_auto_20160715_0100… OKFollowing files were affected G:\pycharm project\xadmintest\db.sqlite3然后就是创建管理员账号了:这里我设置用户名为admin,密码为adminjim,邮箱为空manage.py@xadmintest > createsuperuser"C:\PyCharm 2017.3.7\bin\runnerw.exe” G:\python\python.exe “C:\PyCharm 2017.3.7\helpers\pycharm\django_manage.py” createsuperuser “G:/pycharm project/xadmintest"Username (leave blank to use ‘administrator’): adminEmail address: Warning: Password input may be echoed.Password: adminjimWarning: Password input may be echoed.Password (again): adminjimSuperuser created successfully.登录账号:这里我们发现我们添加的model就显示出来了,接下来我们可以点击模型类,然后添加数据添加成功我们点击这个数据这里我们可以修改和删除,是不是很方便,当然我们的系统显示还有很多需要修改,如:主题,左上角的文字,页面底部文字,列表显示英文,搜索功能等等,我们都可以做添加修改,大家回想一下我们前面在注册的时候是不是还写了一个多余模型管理类呢,那么他就派上用场了,它就可以用来操作这些。由于东西比较多,这里我就不多说了,期待我的下一篇文章吧。 ...

January 16, 2019 · 1 min · jiezi

Django搭建个人博客:基于类的视图

说是完结,马上又开始写进阶篇了。本章不会为博客项目增加新功能,但是也同样重要,因为我们要学习高逼格的基于类的视图。什么是类视图前面章节中写的所有视图都是基于函数的,即def;而类视图是基于类的,即class。有编程基础的同学都知道,类是面向对象技术中非常重要的概念。具有复杂数据、功能的类,可以通过继承轻而易举的将自身特性传递给另一个类,从而实现代码的高效复用。相比以前的函数视图,类视图有以下优势:HTTP方法(GET,POST等)相关的代码,可以通过方法而不是条件分支来组织可以通过诸如mixins(多重继承)之类的面向对象技术将代码分解为可重用组件说的都是什么意思?通过例子来感受一下。列表函数和类假设我们有一个博客列表,列表既有GET方法、又有POST方法,那么用视图函数看起来像这样:views.pydef article_list_example(request): “““处理GET请求””” if request.method == ‘GET’: articles = ArticlePost.objects.all() context = {‘articles’: articles} return render(request, ‘article/list.html’, context)而在类视图中,则变为这样:views.pyfrom django.views import Viewclass ArticleListView(View): “““处理GET请求””” def get(self, request): articles = ArticlePost.objects.all() context = {‘articles’: articles} return render(request, ‘article/list.html’, context)从本质上讲,基于类的视图允许你使用不同的类实例方法(即上面的def get())响应不同的HTTP请求方法,而不需要使用条件分支代码。这样做的好处是把不同的HTTP请求都分离到独立的函数中,逻辑更加清晰,并且方便复用。需要注意的是,因为Django的URL解析器希望将请求发送到函数而不是类,所以类视图有一个 as_view()方法,该方法返回一个函数,当请求匹配关联模式的URL时,则调用该函数。即,视图函数的url原本写为:urls.py…urlpatterns = [ path(’…’, views.article_list_example, name=’…’),]类视图的url需改写为:urls.py…urlpatterns = [ path(’…’, views.ArticleListView.as_view(), name=’…’),]通用视图像列表这样的功能在web开发中是很常见的,开发者会一遍又一遍写几乎相同的列表逻辑。Django的通用视图正是为缓解这种痛苦而开发的。它们对常用模式进行抽象,以便你快速编写公共视图,而无需编写太多代码。因此用列表通用视图改写如下:views.pyfrom django.views.generic import ListViewclass ArticleListView(ListView): # 上下文的名称 context_object_name = ‘articles’ # 查询集 queryset = ArticlePost.objects.all() # 模板位置 template_name = ‘article/list.html’列表继承了父类ListView,也就获得了父类中的处理列表的方法,因此你可以看到,我们在自己的类中没有写任何处理的逻辑,仅仅是赋值了几个变量而已。动态过滤从数据库中筛选特定的内容也是常见的需求,类视图如何实现呢?你可能想到了,将上面代码中改为queryset = ArticlePost.objects.filter()就可以了。除此之外,更好的办法是覆写get_queryset()方法:views.py…class ArticleListView(ListView): context_object_name = ‘articles’ template_name = ‘article/list.html’ def get_queryset(self): """ 查询集 """ queryset = ArticlePost.objects.filter(title=‘Python’) return queryset例子中只是过滤出标题为“Python”的文章而已,有些大材小用了;但是你可以在get_queryset()中写复杂的联合查询逻辑,满足个性化的功能。添加上下文在博客列表的设计时,我们返回给模板的上下文除了articles以外,还有很多额外的信息,如order、search;在类视图中同样可以实现,改写get_context_data()方法即可:views.py…class ArticleListView(ListView): … def get_context_data(self, **kwargs): # 获取原有的上下文 context = super().get_context_data(**kwargs) # 增加新上下文 context[‘order’] = ’total_views’ return context除此之外,ListView还有些别的方法可以覆写,深入了解可以看这里:官方文档混入类混入类(Mixin)是指具有某些功能、通常不独立使用、提供给其他类继承功能的类。嗯,就是“混入”的字面意思。前面的列表视图中已经有get_context_data()方法了。假设需要写一个功能类似的视频列表,就可以用Mixin来避免重复代码:views.py…class ContextMixin: def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context[‘order’] = ’total_views’ return contextclass ArticleListView(ContextMixin, ListView): …class VideoListView(ContextMixin, ListView): …通过混入,两个子类都获得了get_context_data()方法。从语法上看,混入是通过多重继承实现的。有区别的是,Mixin是作为功能添加到子类中的,而不是作为父类。实际上Django内置了很多通用的Mixin类,实现了大部分常用的功能,点这里深入了解:官方文档详情页既然列表都有通用视图,详情页当然也有对应的DetailView。用类视图写一个简单的详情页:views.pyfrom django.views.generic import DetailViewclass ArticleDetailView(DetailView): queryset = ArticlePost.objects.all() context_object_name = ‘article’ template_name = ‘article/detail.html’然后配置url:urls.py…urlpatterns = [ # 详情类视图 path(‘detail-view/<int:pk>/’, views.ArticleDetailView.as_view(), name=’…’),]注意这里传入的参数不是id而是pk,这是视图的要求(也可以传入slug)。pk是数据表的主键,在默认情况下其实就是id。这就写好了!也可以添加任何别的功能,比如统计浏览量:views.py…class ArticleDetailView(DetailView): … def get_object(self): """ 获取需要展示的对象 """ # 首先调用父类的方法 obj = super(ArticleDetailView, self).get_object() # 浏览量 +1 obj.total_views += 1 obj.save(update_fields=[’total_views’]) return obj方法get_object()的作用是获取需要展示的对象。首先调用父类方法,将这个对象赋值给obj变量,然后再对其进行统计浏览量的操作,最后将对象返回。相当于在原有的方法中把自己的逻辑“塞”了进去。关于DetailView更多特性看这里:官方文档编辑除了能够展示信息,通用视图还包含CreateView、UpdateView、DeleteView等编辑数据的类。如果要新建文章,则视图可以这么写:views.pyfrom django.views.generic.edit import CreateViewclass ArticleCreateView(CreateView): model = ArticlePost fields = ‘all’ # 或者只填写部分字段,比如: # fields = [’title’, ‘content’] template_name = ‘article/create_by_class_view.html’创建create_by_class_view.html文件(目录在哪,你应该已经很清楚了),写入:create_by_class_view.html<form method=“post”>{% csrf_token %} {{ form.as_p }} <input type=“submit” value=“Save”></form>最后添加url:urls.pyurlpatterns = [ path(‘create-view/’, views.ArticleCreateView.as_view(), name=’…’),]虽然外观简陋(这不是重点),但现在这个视图确实已经能够创建新文章了!UpdateView和DeleteView这里就不再赘述了,以后用到的地方再进行讲解。想提前了解的同学戳这里:官方文档总结有没有感受到代码隔离和继承的强大?没有?以后的章节会逐渐使用类编写视图,你会慢慢体会的。类视图的内容非常丰富,短短一篇文章只能蜻蜓点水而已。读者在编程中遇到困难了,官方文档是你最好的教程。如果你有耐心从头到尾阅读类视图的官方文档,那当然是最好的了。有疑问请在杜赛的个人网站留言,我会尽快回复。或Email私信我:dusaiphoto@foxmail.com项目完整代码:Django_blog_tutorial转载请告知作者并注明出处。 ...

January 16, 2019 · 1 min · jiezi

SaaS化实践——如何用一个微信公众号登录多个不同的域名

背景SaaS 作为一种服务,需要为不同的客户定制不同的域名以满足客户定制化的需求。而微信登录时需要填写一个回调地址,单一的回调地址是难以处理多客户域名的业务需求的,经过不同的 SaaS 项目的实践,总结出了下面的方式。微信登录的核心代码依然采用 psa 这个库 https://github.com/python-soc…。微信说明阅读微信公众平台文档,可以看到,当同一个微信公众号需要在多个服务间使用时,微信的建议是提供一台中控服务器,防止access_token的重复刷新,这个坑确实踩到过。oauth 2.0https://tools.ietf.org/html/r…核心概念、表结构中控机中控机为同一引导用户登录的微信登录服务器,其中此机器做的为 oauth 2.0 截图部分的 A、B,引导用户授权,微信回调到此中控机,拿到code。中控机通过state参数,解除customerid,根据customer配置找到回调地址。回调是将state,code带去回调的客户域名。customercustomer表需要记录微信的appid,appsecret,这样即使客户需要定制自己的微信公众号,中控机也可以saas化。redirecturl由于微信的state参数长度有限,因此提供一张redirecturl表记录回调地址,登录时只需要将redirecturl_id带入state中即可。redirecturl记录的为回调客户域名+psa compelate地址的完整路由。statestate为oauth 2.0中允许的回调参数,state的构成为: 客户id,回调地址id,其他需要回调参数核心流程核心代码中控机通过customer获取对应的appid,secret。微信回调到cherrypick后,拿着code,state跳转到对应的客户域名。def _auth(request, backend): cid = request.GET[‘cid’] # TODO: DoesNotExist customer = Customer.objects.get(id=cid) appid, appsecret = customer.get_key_and_secret() log.info(’login cid:%s, key:%s’, cid, appid) def get_key_and_secret(): return appid, appsecret request.backend.get_key_and_secret = get_key_and_secret return do_auth(request.backend)@never_cache@psa(‘our_social:cherrypick’)def auth(request, backend, key=’’): return _auth(request, backend) @never_cache@psa()def cherrypick(request, backend): code = request.GET.get(‘code’, ‘’) state = request.GET.get(‘state’, ‘’) redirect_url_id = state.split(’,’)[0] redirect_url = RedirectURL.objects.get(id=redirect_url_id).url redirect_url = ‘{}?code={}&state={}’.format(redirect_url, code, state) log.info(‘cherrypick, redirect_url: %s, state: %s’, redirect_url, state) return redirect(redirect_url) SaaS 服务器处理 oauth 2.0 C、D之后的步骤@psa(‘our_social:complete’)def complete(request, backend, *args, **kwargs): “““Authentication complete view””” logout(request) state = request.GET.get(‘state’, ‘’) …… state解析出cid等参数 customer = Customer.objects.get(id=cid) appid, appsecret = product.get_key_and_secret() request._customer = customer 覆盖backend的方法 def get_key_and_secret(): log.info(’login complete use appid: %s %s’, appid, state) request.backend.get_key_and_secret = get_key_and_secret return do_complete(request.backend, _do_login, request.user, redirect_name=REDIRECT_FIELD_NAME, request=request, *args, **kwargs) ...

January 15, 2019 · 1 min · jiezi

Python猫荐书系列之五:Python高性能编程

稍微关心编程语言的使用趋势的人都知道,最近几年,国内最火的两种语言非 Python 与 Go 莫属,于是,隔三差五就会有人问:这两种语言谁更厉害/好找工作/高工资……对于编程语言的争论,就是猿界的生理周期,每个月都要闹上一回。到了年末,各类榜单也是特别抓人眼球,闹得更凶。其实,它们各有对方所无法比拟的优势以及用武之地,很多争论都是没有必要的。身为一个正在努力学习 Python 的(准)中年程序员,我觉得吧,先把一门语言精进了再说。没有差劲的语言,只有差劲的程序员,等真的把语言学好了,必定是“山重水复疑无路,柳暗花明又一村”。铺垫已了,进入今天的正题,Python 猫荐书系列之五——<center>Python高性能编程</center> 本书适合已入门 Python、还想要进阶和提高的读者阅读。所有计算机语言说到底都是在硬件层面的数据操作,所以高性能编程的一个终极目标可以说是“高性能硬件编程”。然而,Python 是一门高度抽象的计算机语言,它的一大优势是开发团队的高效,不可否认地存在这样或那样的设计缺陷,以及由于开发者的水平而造成的人为的性能缺陷。本书的一大目的就是通过介绍各种模块和原理,来促成在快速开发 Python 的同时避免很多性能局限,既减低开发及维护成本,又收获系统的高效。1、性能分析是基础首先的一个关键就是性能分析,借此可以找到性能的瓶颈,使得性能调优做到事半功倍。性能调优能够让你的代码能够跑得“足够快”以及“足够瘦”。性能分析能够让你用最小的代价做出最实用的决定。书中介绍了几种性能分析的工具:(1)基本技术如 IPython 的 %timeit 魔法函数、time.time()、以及一个计时修饰器,使用这些技术来了解语句和函数的行为。(2)内置工具如 cProfile,了解代码中哪些函数耗时最长,并用 runsnake 进行可视化。(3)line_profiler 工具,对选定的函数进行逐行分析,其结果包含每行被调用的次数以及每行花费的时间百分比。(4)memory_profiler 工具,以图的形式展示RAM的使用情况随时间的变化,解释为什么某个函数占用了比预期更多的 RAM。(5)Guppy 项目的 heapy 工具,查看 Python 堆中对象的数量以及每个对象的大小,这对于消灭奇怪的内存泄漏特别有用。(6)dowser 工具,通过Web浏览器界面审查一个持续运行的进程中的实时对象。(7)dis 模块,查看 CPython 的字节码,了解基于栈的 Python 虚拟机如何运行。(8)单元测试,在性能分析时要避免由优化手段带来的破坏性后果。作者强调了性能分析的重要性,同时也对如何确保性能分析的成功提了醒,例如,将测试代码与主体代码分离、避免硬件条件的干扰(如在BIOS上禁用了TurboBoost、禁用了操作系统改写SpeedStep、只使用主电源等)、运行实验时禁用后台工具如备份和Dropbox、多次实验、重启并重跑实验来二次验证结果,等等。性能分析对于高性能编程的作用,就好比复杂度分析对于算法的作用,它本身不是高性能编程的一部分,但却是最终有效的一种评判标准。2、数据结构的影响高性能编程最重要的事情是了解数据结构所能提供的性能保证。高性能编程的很大一部分是了解你查询数据的方式,并选择一个能够迅速响应这个查询的数据结构。书中主要分析了 4 种数据结构:列表和元组就类似于其它编程语言的数组,主要用于存储具有内在次序的数据;而字典和集合就类似其它编程语言的哈希表/散列集,主要用于存储无序的数据。本书在介绍相关内容的时候很克制,所介绍的都是些影响“速度更快、开销更低”的内容,例如:内置的 Tim 排序算法、列表的 resize 操作带来的超额分配的开销、元组的内存滞留(intern机制)带来的资源优化、散列函数与嗅探函数的工作原理、散列碰撞带来的麻烦与应对、Python 命名空间的管理,等等。理解了这些内容,就能更加了解在什么情况下使用什么数据结构,以及如何优化这些数据结构的性能。另外,关于这 4 种数据结构,书中还得出了一些有趣的结论:对于一个拥有100 000 000个元素的大列表,实际分配的可能是112 500 007个元素;初始化一个列表比初始化一个元组慢5.1 倍;字典或集合默认的最小长度是8(也就是说,即使你只保存3个值,Python仍然会分配 8 个元素)、对于有限大小的字典不存在一个最佳的散列函数。3、矩阵和矢量计算矢量计算是计算机工作原理不可或缺的部分,也是在芯片层次上对程序进行加速所必须了解的部分。然而,原生 Python 并不支持矢量操作,因为 Python 列表存储的不是实际的数据,而是对实际数据的引用。在矢量和矩阵操作时,这种存储结构会造成极大的性能下降。比如,grid[5][2] 中的两个数字其实是索引值,程序需要根据索引值进行两次查找,才能获得实际的数据。同时,因为数据被分片存储,我们只能分别对每一片进行传输,而不是一次性传输整个块,因此,内存传输的开销也很大。减少瓶颈最好的方法是让代码知道如何分配我们的内存以及如何使用我们的数据进行计算。Numpy 能够将数据连续存储在内存中并支持数据的矢量操作,在数据处理方面,它是高性能编程的最佳解决方案之一。Numpy 带来性能提升的关键在于,它使用了高度优化且特殊构建的对象,取代了通用的列表结构来处理数组,由此减少了内存碎片;此外,自动矢量化的数学操作使得矩阵计算非常高效。Numpy 在矢量操作上的缺陷是一次只能处理一个操作。例如,当我们做 A B + C 这样的矢量操作时,先要等待 A B 操作完成,并保存数据在一个临时矢量中,然后再将这个新的矢量和 C 相加。Numexpr 模块可以将矢量表达式编译成非常高效的代码,可以将缓存失效以及临时变量的数量最小化。另外,它还能利用多核 CPU 以及 Intel 芯片专用的指令集来将速度最大化。书中尝试了多种优化方法的组合,通过详细的分析,展示了高性能编程所能带来的性能提升效果。4、编译器书中提出一个观点:让你的代码运行更快的最简单的办法就是让它做更少的工作。 编译器把代码编译成机器码,是提高性能的关键组成部分。不同的编译器有什么优势呢,它们对于性能提升会带来多少好处呢?书中主要介绍了如下编译工具:Cython ——这是编译成C最通用的工具,覆盖了Numpy和普通的Python代码(需要一些C语言的知识)。Shed Skin —— 一个用于非Numpy代码的,自动把Python转换成C的转换器。Numba —— 一个专用于Numpy代码的新编译器。Pythran —— 一个用于Numpy和非numpy代码的新编译器。PyPy —— 一个用于非Numpy代码的,取代常规Python可执行程序的稳定的即时编译器。书中分析了这几种编译器的工作原理、优化范围、以及适用场景等,是不错的入门介绍。此外,作者还提到了其它的编译工具,如Theano、Parakeet、PyViennaCL、ViennaCL、Nuitka 与 Pyston 等,它们各有取舍,在不同领域提供了支撑之力。5、密集型任务高性能编程的一个改进方向是提高密集型任务的处理效率,而这样的任务无非两大类:I/O 密集型与 CPU 密集型。I/O 密集型任务主要是磁盘读写与网络通信任务,占用较多 I/O 时间,而对 CPU 要求较少;CPU 密集型任务恰恰相反,它们要消耗较多的 CPU 时间,进行大量的复杂的计算,例如计算圆周率与解析视频等。改善 I/O 密集型任务的技术是异步编程 ,它使得程序在 I/O 阻塞时,并发执行其它任务,并通过“事件循环”机制来管理各项任务的运行时机,从而提升程序的执行效率。书中介绍了三种异步编程的库:Gevent、Tornado 和 Asyncio,对三种模块的区别做了较多分析。改善 CPU 密集型任务的主要方法是利用多核 CPU 进行多进程的运算。Multiprocessing 模块使用基于进程和基于线程的并行处理,在队列上共享任务,以及在进程间共享数据,是处理 CPU 密集型任务的重要技术。书中没有隐瞒它的局限性:Amdahl 定律揭示的优化限度、适应于单机多核而多机则有其它选择、全局解释锁 GIL 的束缚、以及进程间通信(同步数据和检查共享数据)的开销。针对进程间通信问题,书中还分析了多种解决方案,例如 Less Naïve Pool、Manager、Redis、RawValue、MMap 等。6、集群与现场教训集群是一种多服务器运行相同任务的结构,也就是说,集群中的各节点提供相同的服务,其优点是系统扩展容易、具备容灾恢复能力。集群需要克服的挑战有:机器间信息同步的延迟、机器间配置与性能的差异、机器的损耗与维护、其它难以预料的问题。书中列举了两个惨痛的教训:华尔街公司骑士资本由于软件升级引入的错误,损失4.62亿美元;Skype 公司 24 小时全球中断的严重事故。书中给我们重点介绍了三个集群化解决方案:Parallel Python、IPython Parallel 和 NSQ。引申也介绍了一些普遍使用的方案,如 Celery、Gearman、PyRes、SQS。关于现场教训,它们不仅仅是一些事故或者故事而已,由成功的公司所总结出来的经验更是来之不易的智慧。书中单独用一章内容分享了六篇文章,这些文章出自几个使用 Python 的公司/大型组织,像是Adaptive Lab、RadimRehurek、Smesh、PyPy 与 Lanyrd ,这些国外组织的一线实践经验,应该也能给国内的 Python 社区带来一些启示。7、写在最后众所周知,Python 应用前景大、简单易学、方便开发与部署,然而与其它编程语言相比,它的性能几乎总是落于下风。如何解决这个难题呢?本期荐书的书目就是一种回应。《Python高性能编程》全书从微观到宏观对高性能编程的方方面面做了讲解,主要包含以下主题:计算机内部结构的背景知识、列表和元组、字典和集合、迭代器和生成器、矩阵和矢量计算、编译器、并发、集群和工作队列等。这些内容为编写更快的 Python 指明了答案。本篇文章主要以梳理书中的内容要点为主,平均而兼顾地理清了全书脉络(PS:介绍得太面面俱到了,但愿不被指责为一篇流水账的读书笔记才好……)。我认为,鉴于书中谈及的这些话题,它就足以成为我们荐书栏目的一员了。除去某些句段的糟糕翻译、成书时间比较早(2014年)而造成的过时外,这本书总体质量不错,可称为是一份优秀的高性能编程的指引手册。关于荐书栏目,我最后多说几句。本栏目原计划两周左右出一篇,但由于其它系列文章花费了我不少时间,而要写好一篇荐书/书评也特别费劲,最后生生造成了现在两月一更的尴尬局面……这篇文章是个错误的示范,我不该试图全面通读与概括其内容的。因此,我决定今后选一些易读的书目,在写作上也尽量走短小精悍风,希望能持续地将本栏目运作下去。若你有什么建议(如书目推荐、书评推荐、写作建议、甚至是投稿),我随时欢迎,先行致谢啦。往期荐书回顾: 第一期:《编写高质量代码改善 Python 程序的 91 个建议》第二期:《Python最佳实践指南》第三期:《黑客与画家》第四期:《Python源码剖析》—————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

January 13, 2019 · 1 min · jiezi

聊聊技术写作的个人体会

有群友问过,是什么原因使我开始写技术公众号,又是什么动力让我坚持写的。在我看来,写作是一件不能敷衍的事,通过写作来学习,反而要比单纯地学习的效果要好。为了写成一篇“拿得出手”的文章,我要反复查找资料,阅读与思考,拆解与整合,最终写成的时候,也是知识的拼图成型的时候。所以,对我来说,写作是一种咀嚼信息而后提炼知识,最终拓展成技能与认知的过程。虽然这个过程很缓慢,但曾经的急进方式并没有速成的效果啊,不妨就这样一文章一脚印地试试看咯。除此之外,还有一个很重要的原因。文章是一种公共对话的媒介,它是一个展示的窗口,也是一个接收反馈的通道。通过写作,我有了跟其它学习者对话的机会。看书学习可能只是个人的事情,但是,在写作平台上发布文章,这就超越了个人行为——你得随时准备着被批评、或者被请教、或者被误解、甚至是被无视(这是最常见的结果)。我享受写作文章,来跟其他处在相同处境的同学们交流,来向更优秀的大牛们学习取经。这就是我目前写技术文章的一些个人体会吧。对于上面提到的第二个原因,我最近颇有感触,想要多聊一些。为了更有针对性,本文姑且限定一个话题吧,那就是“写作技术文章,如何看待他人的批评/意见”。1、主观性的意见有些声音其实只是主观看法,我认为可以和而不同。主观世界往往没有确切的对错之分,毕竟——思想无罪 。面对主观性的意见,我认为要做到有理有据,坚持一点个性,最后会得到别人的尊重。比如,在翻译 Python 社区的七种治理模式的时候,有一个提案是“Python Governance Model Lead by Trio of Pythonistas”,我将它翻译成“三巨头治理模式”。有同学就指出,“Trio”应该翻译成“三人组”或者“三重奏”,翻译成“三巨头”是什么意思?这种留言,我认为是主观性的意见,应求同存异。我之所以这么翻译,一方面考虑,它要替代的是“终身仁慈独裁者”,三巨头对独裁者,意味深长;另一方面,我脑子里总想着一个皇帝死了,然后政权被三个摄政大臣把持,这种政治画面挥之不去,虽然是不着边际,但挺有趣味,所以我不肯放弃这“三巨头”的译法。主观性的意见带入了提出者的个人知识背景、思想结构、以及话语习惯等等,我觉得要先尝试交流,相互交换,能融洽兼容则最好啦,不能的话,及时终止。2、客观性的意见客观性的意见有如下几种:笔误(错别字和其它疏忽)、代码规范、知识性错误……对于笔误性的错误,这没啥好说的,我自己发现过几处,也被读者指出过几处。有则改之就好。对于代码规范,有时候为了举例方便,确实没有按照规范来。尽量避免,求一个兼顾。知识性错误是要热烈欢迎的——不是说欢迎错误,而是说欢迎别人来指出我所未知的错误。 出现知识性的错误,就意味着没有全面掌握知识,一旦出现,就必然意味着有提升的空间。本来以为知道了什么,如果被指出了错误,那改正后,才是真的知道了什么。知道自己不知道并且改正之,并不可耻,不知道自己不知道,这才可怜。 在写《Python是否支持复制字符串呢?》的时候,我根据已得的知识,以及查阅到的资料,早早就得出了一个很满意的结论。最后成文前,临时地加了一个未作验证的示例,没想到这会是一个致命的反例,推翻了前面辛辛苦苦建立起来的一切。这是一个客观性的错误,一被指出的时候,很快就能验证。因为这个错误,我重新梳理了相关的知识点,组成新的知识面,写成了一篇《join()方法的神奇用处与Intern机制的软肋》。还有一个例子,前不久的《Python进阶:自定义对象实现切片功能》,我在准备素材的时候,竟采用了一个不严谨的例子,而且自作聪明地批判了别人的实际无误的例子。最后,有读者留言了很长的不同观点,我才意识到自己的错误!得益于读者的留言,我修正了自己的错误,而且在修正过程中,也加强了对于其它知识的理解,真是塞翁失马焉知非福啊。3、内置函数与内置类这里还有一个客观性错误,藏得特别深,可能真的有 90% 的 Python 使用者不知道。 特别感谢 @xpresslink 同学指出。下面,我给大家分享一下。在文章《为什么range不是迭代器?range到底是什么类型?》里,我的注意点其实就在标题的两个问句里,大部分的留言互动也是基于此。但最后,很意外地,一名读者指出了一个客观性错误,让我有了额外的收获。这位同学指出我有些基本的概念是错误的:“range() 函数”这个说法是非常明显有错误的,range 不是内置函数( builtin method )而是个类对象,在 python 里面不要见到用括号调用的东西就认为是函数,类似的还是有很多,如 list, set, tuple, dict 等,这些都是类, 特别是 enumerate ,这个学 python 的人十有八九认为是函数而不知道是类,加了括号是实例化而不是函数调用。python 中类的实例化和函数调用非常容易对新手有大的迷惑性,相对来说在 java 中有明确的 new 关键字加在构造方法前面概念更清楚一些。根据这个评论,我就去查看文档。上图中 range() 虽然被归类到 Built-in Functions 里面,但是官方描述的是“functions and types”,即是说,在内置函数的大类下面,包含了内置函数与内置类。那 range() 属于哪一种呢?看看它的解释:Rather than being a function, range is actually an immutable sequence type…… range 实际是一种不可变的序列类型,而非一个(内置)函数……按照这里的说法,官方已经区分了 range() 不是函数,正像那位留言的同学所说。我第一反应当然是不能接受。我怎么会认为它是内置函数的呢,难道不是根据学习资料得来的么?难道我学习的资料是错的?为何从来没看到有人对此做过辨析呢?根据群友的提示,我去查看 Python2 的文档,然后就发现了很有意思的地方:首先一点, Built-in Functions 的描述跟 Python3 有点不同,它写的是 “functions”,并不包含“types”;还有一点,在 range() 和 xrange() 的具体内容中,官方都是称呼它们为 function 。由此看来,Python2 的官方文档就把 range() 当成内置函数,这个认识错误是有根源的!等到 Python3 的时候,官方把错误改正过来了,然而改得并不彻底。才有了前面同时存在“functions and types”的描述。官方已经把 range() 与 xrange() 规范为一个,或许在今后版本,还会专门分出一类 Built-in Types 来存放像 range() 和 enumerate() 这些内置类吧。在那之前,我只能先行给大家提个醒了:别再误以为 range() 是内置函数了。 那么,怎么辨别哪些是内置函数呢?我想到了两个方法:(1)看是否存在对应的魔术方法。例如,len() 是一个内置函数,因为它实际调用的是魔术方法__len__() ;还有最近一直在提的 iter(),它调用的是__iter__() ,所以也是内置函数;而因为不存在 range() 魔术方法,所以 range() 不是内置函数。(2)使用 type() 进行判断,结果为 builtin_function_or_method 的才是内置函数。>>> type(len)builtin_function_or_method>>> type(sorted)builtin_function_or_method>>> type(open)builtin_function_or_method>>> type(range)type>>> type(enumerate)type>>> type(str)type像 open 和 sorted 并没有对应的魔术方法,但判断出来都是内置函数;而 str 虽有对应魔术方法,但判断是 type ,这意味着,以上两种方法得要结合起来看。我不确定有多少人事先知道怎么区分内置函数与内置类,但我确实没看到过对这个问题进行辨析的文章,所以,这次是真正涨知识了,也希望这篇文章,能够消除一些读者的错误观念吧。4、小结我最近写的一些文章都不是心血来潮,不管是字符串系列、切片系列还是迭代器系列,本意都是想在一个主题上进行深入的多面性的思考与记录。如果没有一些热心读者的指正,我恐怕是很难知道自己错在了哪里,如果不是有这么多的认同以及意见,我恐怕也缺乏动力坚持写下去。最后鸣谢几位提意见的小能手同学(时间顺序,可能有漏):@疯琴、@德玛西亚之翼奎因、@发条橙、@gaieepo、@郭芮、@aijam、@xpresslink、@进击的团子、@不换……相关链接(单有错,双修正): 1、Python是否支持复制字符串呢?2、join()方法的神奇用处与Intern机制的软肋3、Python进阶:自定义对象实现切片功能4、Python进阶:全面解读高级特性之切片!5、为什么range不是迭代器?range到底是什么类型?—————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

January 11, 2019 · 1 min · jiezi

通过django的upload组件上传图片并重命名

在用django开发app服务端的时候,利用了django的upload组件上传图片,因图片名字不统一所以想给上传的图片重命名,从网上找了一些代码资料,在自己的项目中出错,所以自己摸索找到了解决办法,代码如下:1、在项目根目录中新建文件夹system,并在system文件夹下添加__init__.py和storage.py文件,并在storage.py中添加如下代码:# -- coding: UTF-8 --from django.core.files.storage import FileSystemStoragefrom django.http import HttpResponseclass ImageStorage(FileSystemStorage): from django.conf import settings def init(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL): # 初始化 super(ImageStorage, self).init(location, base_url) # 重写 _save方法 def save(self, name, content): import os, time, random # 文件扩展名 ext = os.path.splitext(name)[1] # 文件目录 d = os.path.dirname(name) # 定义文件名,年月日时分秒随机数 fn = time.strftime(’%Y%m%d%H%M%S’) fn = fn + ‘%d’ % random.randint(0,100) # 重写合成文件名 name = os.path.join(d, fn + ext) # 调用父类方法 return super(ImageStorage, self)._save(name, content)2、在models.py文件中添加如下代码:from system.storage import ImageStoragepic=models.ImageField(upload_to=‘img/%Y/%m/%d’,storage=ImageStorage())3、这样就解决了问题,效果如下: ...

January 7, 2019 · 1 min · jiezi