236 lines
29 KiB
HTML
236 lines
29 KiB
HTML
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="X-UA-Compatible" content="IE=edge"><title> MarkDown</title><meta name="description" content="A Blog Powered By Hexo"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="short icon" href="/favicon.png"><link rel="stylesheet" href="/css/apollo.css"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600" type="text/css"><style><!-- hexo-inject:begin --><!-- hexo-inject:end -->mjx-container[jax="SVG"] {
|
||
direction: ltr;
|
||
}
|
||
|
||
mjx-container[jax="SVG"] > svg {
|
||
overflow: visible;
|
||
}
|
||
|
||
mjx-container[jax="SVG"] > svg a {
|
||
fill: blue;
|
||
stroke: blue;
|
||
}
|
||
|
||
mjx-container[jax="SVG"][display="true"] {
|
||
display: block;
|
||
text-align: center;
|
||
margin: 1em 0;
|
||
}
|
||
|
||
mjx-container[jax="SVG"][justify="left"] {
|
||
text-align: left;
|
||
}
|
||
|
||
mjx-container[jax="SVG"][justify="right"] {
|
||
text-align: right;
|
||
}
|
||
|
||
g[data-mml-node="merror"] > g {
|
||
fill: red;
|
||
stroke: red;
|
||
}
|
||
|
||
g[data-mml-node="merror"] > rect[data-background] {
|
||
fill: yellow;
|
||
stroke: none;
|
||
}
|
||
|
||
g[data-mml-node="mtable"] > line[data-line] {
|
||
stroke-width: 70px;
|
||
fill: none;
|
||
}
|
||
|
||
g[data-mml-node="mtable"] > rect[data-frame] {
|
||
stroke-width: 70px;
|
||
fill: none;
|
||
}
|
||
|
||
g[data-mml-node="mtable"] > .mjx-dashed {
|
||
stroke-dasharray: 140;
|
||
}
|
||
|
||
g[data-mml-node="mtable"] > .mjx-dotted {
|
||
stroke-linecap: round;
|
||
stroke-dasharray: 0,140;
|
||
}
|
||
|
||
g[data-mml-node="mtable"] > svg {
|
||
overflow: visible;
|
||
}
|
||
|
||
[jax="SVG"] mjx-tool {
|
||
display: inline-block;
|
||
position: relative;
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
[jax="SVG"] mjx-tool > mjx-tip {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
}
|
||
|
||
mjx-tool > mjx-tip {
|
||
display: inline-block;
|
||
padding: .2em;
|
||
border: 1px solid #888;
|
||
font-size: 70%;
|
||
background-color: #F8F8F8;
|
||
color: black;
|
||
box-shadow: 2px 2px 5px #AAAAAA;
|
||
}
|
||
|
||
g[data-mml-node="maction"][data-toggle] {
|
||
cursor: pointer;
|
||
}
|
||
|
||
mjx-status {
|
||
display: block;
|
||
position: fixed;
|
||
left: 1em;
|
||
bottom: 1em;
|
||
min-width: 25%;
|
||
padding: .2em .4em;
|
||
border: 1px solid #888;
|
||
font-size: 90%;
|
||
background-color: #F8F8F8;
|
||
color: black;
|
||
}
|
||
|
||
foreignObject[data-mjx-xml] {
|
||
font-family: initial;
|
||
line-height: normal;
|
||
overflow: visible;
|
||
}
|
||
|
||
.MathJax path {
|
||
stroke-width: 3;
|
||
}
|
||
|
||
mjx-container {
|
||
overflow: auto hidden;
|
||
}
|
||
|
||
mjx-container + br {
|
||
display: none;
|
||
}
|
||
</style><!-- hexo-inject:begin --><!-- hexo-inject:end --></head><body><header><a href="/" class="logo-link"><img src="/logo.png"></a><ul class="nav nav-list"><li class="nav-list-item"><a href="/" target="_self" class="nav-list-link active">ALL</a></li><li class="nav-list-item"><a href="/categories/leetcode/" target="_self" class="nav-list-link">LEETCODE</a></li><li class="nav-list-item"><a href="https://bearmiebear.blogspot.com" target="_blank" class="nav-list-link">BEAR</a></li><li class="nav-list-item"><a href="/atom.xml" target="_self" class="nav-list-link">RSS</a></li></ul></header><section class="container"><ul class="home post-list"><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2020/03/23/leetcode-169/" class="post-title-link">leetcode-169</a></h2><div class="post-meta"><div class="post-time">2020年3月23日</div></div><div class="post-content"><h3 id="169-__u591A_u6570_u5143_u7D20"><a href="#169-__u591A_u6570_u5143_u7D20" class="headerlink" title="169. 多数元素"></a>169. 多数元素</h3><p><a href="https://leetcode-cn.com/problems/majority-element/" target="_blank" rel="noopener">题目</a></p><a href="/2020/03/23/leetcode-169/" class="read-more">...more</a></div></article></li><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2020/03/18/leetcode-206/" class="post-title-link">leetcode-206</a></h2><div class="post-meta"><div class="post-time">2020年3月18日</div></div><div class="post-content"><h3 id="206-__u53CD_u8F6C_u94FE_u8868"><a href="#206-__u53CD_u8F6C_u94FE_u8868" class="headerlink" title="206. 反转链表"></a>206. 反转链表</h3><p><a href="https://leetcode-cn.com/problems/reverse-linked-list/" target="_blank" rel="noopener">题目</a></p><a href="/2020/03/18/leetcode-206/" class="read-more">...more</a></div></article></li><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2020/03/17/leetcode-121/" class="post-title-link">leetcode-121</a></h2><div class="post-meta"><div class="post-time">2020年3月17日</div></div><div class="post-content"><h3 id="121-__u4E70_u5356_u80A1_u7968_u7684_u6700_u4F73_u65F6_u673A"><a href="#121-__u4E70_u5356_u80A1_u7968_u7684_u6700_u4F73_u65F6_u673A" class="headerlink" title="121. 买卖股票的最佳时机"></a>121. 买卖股票的最佳时机</h3><p><a href="https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/" target="_blank" rel="noopener">题目</a></p><a href="/2020/03/17/leetcode-121/" class="read-more">...more</a></div></article></li><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2019/11/14/AWS-KMS/" class="post-title-link">AWS KMS</a></h2><div class="post-meta"><div class="post-time">2019年11月14日</div></div><div class="post-content"><p>We used to keep private credentials on production servers without any protection or encryption. Well, luckily we don’t have any leak but this practice is not recommended for both security and easy of use reasons.</p>
|
||
<!-- hexo-inject:begin --><!-- hexo-inject:end --><p>Since AWS finally provides <a href="https://www.amazonaws.cn/kms/" target="_blank" rel="noopener">KMS(Key Management Service)</a> in our local region, we try to encrypt every private credentials by KMS and store them on S3.</p>
|
||
<p><em>TBD</em></p>
|
||
</div></article></li><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2019/03/12/Postgresql-Partitioning/" class="post-title-link">Postgresql Partitioning</a></h2><div class="post-meta"><div class="post-time">2019年3月12日</div></div><div class="post-content"><p><code>Partitioning</code> refers to splitting what is logically one large table inot smaller physical pieces.</p>
|
||
<p>Currently, PostgreSQL supports partitioning via table <a href="https://www.postgresql.org/docs/9.6/ddl-inherit.html" target="_blank" rel="noopener">inheritance</a>. Each partition must be created as a child table of a single parent table. <strong>The parent table itself is normally empty</strong>; It exists just to represent the entire data set.</p>
|
||
<p>There are two forms of partitioning can be implemented in PostgreSQL:</p>
|
||
<ul>
|
||
<li><p>Range Partitioning</p>
|
||
<p> The table is partitioning into “range” defined by a key column or a set of columns, with no overlap between the ranges of values assigned to different partitions. eg. partition by date ranges or by identifiers.</p>
|
||
</li>
|
||
<li><p>List Partitioning</p>
|
||
<p> The table is partitioned by explicitly listing which key values appear in each partition.</p>
|
||
</li>
|
||
</ul>
|
||
<h3 id="Implementing_Partitioning"><a href="#Implementing_Partitioning" class="headerlink" title="Implementing Partitioning"></a>Implementing Partitioning</h3><ol>
|
||
<li><p>Create the “master” / “parent” table, from which all the partitions will inherit.</p>
|
||
<p> This table will not contain any data. Do not define any check on this table, unless you intend them to be applied equally to all partitions. There is no point in defining any indexes or unique constraints on it either.</p>
|
||
</li>
|
||
<li><p>Create “child” tables that each inherit form the master table. Normally, these tables will not add any columns to the set inherited from the master. </p>
|
||
</li>
|
||
<li><p>Add table constraints to the partition tables to define the allowed key values in each partitions. </p>
|
||
<p> Ensure that the constraints guarantee that there is no overlap between the key values premitted in different partitions. And there is no difference in syntax between range and list partitioning. </p>
|
||
</li>
|
||
<li><p>Create indexes on column(s) for each partitions.</p>
|
||
</li>
|
||
<li><p>Optionally, define a trigger or rule to redirect data inserted into the master table to the appropriate partition.</p>
|
||
</li>
|
||
<li><p>Ensure hte <a href="https://www.postgresql.org/docs/9.6/runtime-config-query.html#GUC-CONSTRAINT-EXCLUSION" target="_blank" rel="noopener">constraint_exclusion</a> configuration parameter is not disabled in <code>postgresql.conf</code>. If it is, queries will not be optimized as desired.</p>
|
||
</li>
|
||
</ol>
|
||
<h3 id="Trigger"><a href="#Trigger" class="headerlink" title="Trigger"></a>Trigger</h3><p>As we are creating new table and hopping data insered to right partition, a trigger function and a trigger are needed.</p>
|
||
</div></article></li><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2018/05/31/bash-function-and-awk/" class="post-title-link">bash function and awk</a></h2><div class="post-meta"><div class="post-time">2018年5月31日</div></div><div class="post-content"><p>I’ll come across many hg branch-switching or log searching tasks during my work. Using bash functions and awk greatly reduce the time I spend on dealing with these tasks. So let’s see what these functions can do to help me improve my productivity.</p>
|
||
<h4 id="bash_functions"><a href="#bash_functions" class="headerlink" title="bash functions"></a>bash functions</h4><p>Bash functions are scripts like <code>alias</code>, but can do much a lot than aliasThe first kind of usages of function is to run several scripts continuously, the same as <code>&&</code> I guess:</p>
|
||
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> run {</span><br><span class="line"> <span class="built_in">source</span> ~/Develop/django/bin/activate</span><br><span class="line"> ./manage.py runserver</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
|
||
<p>I activate django virtural enviroment first and then run django serve.</p>
|
||
<p>Functions, like in any other languages, can take parameters and returns a result. So I can use this ability to do a liitle more complex work:</p>
|
||
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> ba { hgba | grep <span class="string">"<span class="variable">$1</span>"</span> }</span><br><span class="line"><span class="comment"># hgba is an alias for hg branches</span></span><br></pre></td></tr></table></figure>
|
||
<p>I can just type <code>ba release</code> then get current release branch info.</p>
|
||
<hr>
|
||
<p>Append:</p>
|
||
<p>I made some updates to the <code>ba</code> function and make it auto copying the branch result to my clipboard:</p>
|
||
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">function</span> ba { var=<span class="string">"<span class="variable">$(hgba | grep $1)</span>"</span> && <span class="built_in">echo</span> <span class="variable">$var</span> | awk -F <span class="string">':'</span> END{<span class="built_in">print</span>} | awk -F <span class="string">':'</span> <span class="string">'{print $NF}'</span> | tr -d <span class="string">'\n'</span> | pbcopy && <span class="built_in">echo</span> <span class="variable">$var</span> }</span><br></pre></td></tr></table></figure>
|
||
<h4 id="awk"><a href="#awk" class="headerlink" title="awk"></a>awk</h4><p><code>awk</code> is a command I just know recently. I need to process a bunch of logs and analyze them. What I used to do is download the logs, grep things I want into a txt file and then process the txt file with python. </p>
|
||
</div></article></li><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2016/05/10/TastyPie-Note-1/" class="post-title-link">TastyPie Note 1</a></h2><div class="post-meta"><div class="post-time">2016年5月10日</div></div><div class="post-content"><h3 id="Flow_Through_The_Request/Response_Cycle"><a href="#Flow_Through_The_Request/Response_Cycle" class="headerlink" title="Flow Through The Request/Response Cycle"></a>Flow Through The Request/Response Cycle</h3><p>Tastypie can be thought of as a set of class-based view that provide the API functionality. All routing/middleware/response-handling aspectss are the same as a typical Django app. Where the differs is in the view itself.</p>
|
||
<p>Walking through what a GET request to a list endpoint looks like:</p>
|
||
<ul>
|
||
<li><p>The <code>Resource.urls</code> are checked by Django’s url resolvers.</p>
|
||
</li>
|
||
<li><p>On a match for the list view, <code>Resource.wrap_view('dispatch_list')</code> is called. <code>wrap_view</code> provides basic error handling & allows for returning serilized errors.</p>
|
||
</li>
|
||
<li><p>Because dispatch_list was passed to <code>wrap_view</code>, <code>Resource.dispatch_list</code> is called next. This is a thin wrapper around <code>Resource.dispatch</code>.</p>
|
||
</li>
|
||
<li><p><code>dispatch</code> does a bunch of havy lifting. It ensures:</p>
|
||
<ul>
|
||
<li>the requested HTTP method is in <code>allowed_methos</code> (<code>method_check</code>).</li>
|
||
<li>the class has a method that can handle the request(<code>get_list</code>)</li>
|
||
<li>the user is authenticated(<code>is_authenticated</code>)</li>
|
||
<li>the user has no exceeded their throttle(<code>throttle_check</code>).</li>
|
||
</ul>
|
||
<p>At this point, <code>dispatch</code> actually calls the requested method (<code>get_list</code>).</p>
|
||
</li>
|
||
<li><p><code>get_list</code> does the actual work of API. It does:</p>
|
||
<ul>
|
||
<li>A fetch of the available objects via <code>Resource.obj_get_list</code>. In the case of <code>ModelResource</code>, this builds the ORM filters to apply (<code>ModelResource.build_filters</code>). It then gets the <code>QuerySet</code> via <code>ModelResource.get_object_list</code> (which performs <code>Resource.authorized_read_list</code> to possibly limit the set the user can work with) and applies the built filters to it.</li>
|
||
<li>It then sorts the objects based on user input (<code>ModelResource.apply_sorting</code>).</li>
|
||
<li>Then it paginates the results using the supplied <code>Paginator</code> & pulls out the data to be serialized.</li>
|
||
<li>The objects in the page have <code>full_dehydrate</code> applied to each of them, causing Tastypie to traslate the raw object data into the fields the endpoint supports.</li>
|
||
<li>Finally, it calls <code>Resource.create_response</code>.</li>
|
||
</ul>
|
||
</li>
|
||
<li><p><code>create_response</code> is a shortcut method that:</p>
|
||
<ul>
|
||
<li>Determines the desired response format (<code>Resource.determine_format</code>).</li>
|
||
<li>Serializes the data given to it in the proper format.</li>
|
||
<li>Returns a Django <code>HttpResponse</code> (200 OK) with the serialized data.</li>
|
||
</ul>
|
||
</li>
|
||
<li><p>We bubble back up the call stack to <code>dispatch</code>. The last thing <code>dispatch</code> does is potentially store that a request occured for future throttling (<code>Resource.log_throttled_access</code>) then either returns the <code>HttpResponse</code> or wraps whatever data came back in a response (so Django doesn’t freak out).</p>
|
||
</li>
|
||
</ul>
|
||
</div></article></li><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2016/05/04/Tastypie/" class="post-title-link">Tastypie</a></h2><div class="post-meta"><div class="post-time">2016年5月4日</div></div><div class="post-content"><h4 id="Resources_in_Tastypie"><a href="#Resources_in_Tastypie" class="headerlink" title="Resources in Tastypie"></a>Resources in Tastypie</h4><p>Resources are the heart of Tastypie. By defining a resource we can actually convert a model into an API stream. The data is automatically converted into API response.</p>
|
||
<p>Understanding the process of creating a resource.</p>
|
||
<ol>
|
||
<li>Import ModelResource from Tastypie.</li>
|
||
<li>Import models from services app</li>
|
||
<li>Create custom resource by inheriting ModelResource and link app model in inner Meta class of resource. </li>
|
||
</ol>
|
||
<p>Add API URL in the urls.py of app.</p>
|
||
<h4 id="Dehydrating_the_JSON_data"><a href="#Dehydrating_the_JSON_data" class="headerlink" title="Dehydrating the JSON data"></a>Dehydrating the JSON data</h4><p><img src="https://impythonist.files.wordpress.com/2016/04/tastypie_ill.png?w=800" alt="flow"></p>
|
||
<p>Dehydration in Tastypie means making alterations before sending data to the client. Suppose we need to send capitalized product names instead of small letters. Now we see two kinds of dehydrate methods.</p>
|
||
<h5 id="Dehydrate_field_method"><a href="#Dehydrate_field_method" class="headerlink" title="Dehydrate_field method"></a>Dehydrate_field method</h5><p>This <code>dehydrate_field</code> is uesd to modify field on the response JSON. </p>
|
||
<h5 id="Dehydrate_method"><a href="#Dehydrate_method" class="headerlink" title="Dehydrate method"></a>Dehydrate method</h5><p>Dehydrate method is useful for aadding additional fields to bundle (response data). </p>
|
||
<p>Similarly using <code>hydrate</code> method we can alter the bundle data which is generated from request at the time of PUT or POST methods.</p>
|
||
</div></article></li><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2016/04/25/Django-Manager-Method/" class="post-title-link">Django Manager Method</a></h2><div class="post-meta"><div class="post-time">2016年4月25日</div></div><div class="post-content"><h4 id="Django_Manager"><a href="#Django_Manager" class="headerlink" title="Django Manager"></a>Django Manager</h4><p>Django 里会为每一个 model 生成一个 Manager,默认名字为 objects,一般情况下对 model 进行的处理都是通过 model.objects.XXX( ) 来进行的。其实是调用了 model 的 manager 的方法,而 manager 之中的方法是 QuerySet 方法的代理,QuerySet 方法是对数据库操作的封装。</p>
|
||
<p>eg.</p>
|
||
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span><span class="params">(models.Model)</span>:</span></span><br><span class="line"> ...</span><br><span class="line"> people = models.Manager()</span><br></pre></td></tr></table></figure>
|
||
<p>上面这个 model,<code>Person.objects</code>会产生一个<code>AttributeError</code>,但是<code>Person.people</code>就可以正常操作。因为默认的 manager 已经变成 people,objects 这个 manager 没有重新声明,不起作用。</p>
|
||
<h4 id="u81EA_u5B9A_u4E49_Manager"><a href="#u81EA_u5B9A_u4E49_Manager" class="headerlink" title="自定义 Manager"></a>自定义 Manager</h4><p>通常需要自定义 manager 的情况有两点:</p>
|
||
<ol>
|
||
<li>需要修改/扩展 Django 的 manager 方法</li>
|
||
<li>需要修改返回的 QuerySet</li>
|
||
</ol>
|
||
<h4 id="u9ED8_u8BA4_Manager"><a href="#u9ED8_u8BA4_Manager" class="headerlink" title="默认 Manager"></a>默认 Manager</h4><p>如果使用自定义的 manager 需要注意的是,Django 将 model 中定义的第一个 manager 认为是默认 manager,而且 Django 框架中会用到默认 manager。</p>
|
||
<p>笨方法是使用自定义 manager 的时候,对于 model 依然提供 objects 这个默认 manager,并放在第一个。</p>
|
||
<p>eg.</p>
|
||
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span><span class="params">(models.Model)</span>:</span></span><br><span class="line"> title = models.CharField(max_length=<span class="number">100</span>)</span><br><span class="line"> author = models.CharField(max_length=<span class="number">50</span>)</span><br><span class="line"> </span><br><span class="line"> objects = models.Manager() <span class="comment"># default manager</span></span><br><span class="line"> custom_objects = CustomBOokManager() <span class="comment"># custom manager</span></span><br></pre></td></tr></table></figure>
|
||
<p><a href="http://blog.csdn.net/sicofield/article/details/49283751" target="_blank" rel="noopener">source</a></p>
|
||
</div></article></li><li class="post-list-item"><article class="post-block"><h2 class="post-title"><a href="/2016/02/16/Flask-Day-2/" class="post-title-link">Flask Day 2</a></h2><div class="post-meta"><div class="post-time">2016年2月16日</div></div><div class="post-content"><p>To handle web forms we use <a href="http://packages.python.org/Flask-WTF" target="_blank" rel="noopener">Flask-WTF </a>. So we need to write a config file (file <code>config.py</code>):</p>
|
||
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">WTF_CSRF_ENABLED = <span class="keyword">True</span></span><br><span class="line">SECRET_KEY = <span class="string">'you-will-never-guess'</span></span><br></pre></td></tr></table></figure>
|
||
<p>And then you need to use this config (file <code>app/__init__.py</code>):</p>
|
||
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line">app.config.from_object(<span class="string">'config'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> app <span class="keyword">import</span> views</span><br></pre></td></tr></table></figure>
|
||
<p>Let’s build a simple form (file <code>app/forms.app</code>):</p>
|
||
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask.ext.wtf <span class="keyword">import</span> Form</span><br><span class="line"><span class="keyword">from</span> wtforms <span class="keyword">import</span> StringField, BooleanField</span><br><span class="line"><span class="keyword">from</span> wtforms.validators <span class="keyword">import</span> DataRequired</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LoginForm</span><span class="params">(Form)</span>:</span></span><br><span class="line"> openid = StringField(<span class="string">'openid'</span>, validators=[DataRequired()])</span><br><span class="line"> remember_me = BooleanField(<span class="string">'remember_me'</span>, default=<span class="keyword">False</span>)</span><br></pre></td></tr></table></figure>
|
||
<p>The <code>DataRequired()</code> is a validator that checks the field is empty or not.</p>
|
||
<p>After that, we need a HTML page to show the form (file <code>app/templates/login.html</code>):</p>
|
||
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- extend from base layout --></span></span><br><span class="line">{% extends "base.html" %}</span><br><span class="line"></span><br><span class="line">{% block content %}</span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>Sign In<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">form</span> <span class="attr">action</span>=<span class="string">""</span> <span class="attr">method</span>=<span class="string">"post"</span> <span class="attr">name</span>=<span class="string">"login"</span>></span></span><br><span class="line"> {{ form.hidden_tag() }}</span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span></span><br><span class="line"> Please enter your OpenID:<span class="tag"><<span class="name">br</span>></span></span><br><span class="line"> {{ form.openid(size=80) }}<span class="tag"><<span class="name">br</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>{{ form.remember_me }} Remember Me<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span><span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> <span class="attr">value</span>=<span class="string">"Sign In"</span>></span><span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">form</span>></span></span><br><span class="line">{% endblock %}</span><br></pre></td></tr></table></figure>
|
||
<p>The final step is to code a view function that renders the template and receiving data from form (file <code>app/views.py</code>):</p>
|
||
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> render_template, flash, redirect</span><br><span class="line"><span class="keyword">from</span> app <span class="keyword">import</span> app</span><br><span class="line"><span class="keyword">from</span> .forms <span class="keyword">import</span> LoginForm</span><br><span class="line"></span><br><span class="line"><span class="comment"># index view function suppressed for brevity</span></span><br><span class="line"></span><br><span class="line">app.route(<span class="string">'/login'</span>, methods=[<span class="string">'GET'</span>, <span class="string">'POST'</span>])</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">login</span><span class="params">()</span>:</span></span><br><span class="line"> form = LoginForm()</span><br><span class="line"> <span class="keyword">if</span> form.validate_on_submit():</span><br><span class="line"> flash(<span class="string">'Login requested for OpenID="%s", remember_me=%s'</span> %</span><br><span class="line"> (form.openid.data, str(form.remember_me.data)))</span><br><span class="line"> <span class="keyword">return</span> redirect(<span class="string">'/index'</span>)</span><br><span class="line"> <span class="keyword">return</span> render_template(<span class="string">'login.html'</span>, </span><br><span class="line"> title=<span class="string">'Sign In'</span>,</span><br><span class="line"> form=form)</span><br></pre></td></tr></table></figure>
|
||
</div></article></li></ul></section><footer><div class="paginator"><a href="/page/2/" class="prev">PRVE</a><a href="/page/4/" class="next">NEXT</a></div></footer><script src="https://cdn.bootcss.com/mathjax/2.5.3/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script><!-- hexo-inject:begin --><!-- hexo-inject:end --></body></html> |