about 2 years ago

首先确保你安装了Homebrew, 然后:

brew install rbenv ruby-build

安装成功后, 编辑你的.bash_profile文件, 将一下两行命令添加在文件的最后:

export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

添加完后,重新执行一遍bash_profile:

cd ~
. .bash_profile

然后,用rbenv来安装一个你想要的ruby版本,比如你要安装2.2.2版本的话:

rbenv install 2.2.2
rbenv gloabl 2.2.2   

查看一下我们安装ruby是通过rbenv安装的,而不再是Mac系统自带的ruby:

which ruby

输出的结果应该包含(.rbenv/shims/ruby):

/Users/cheng/.rbenv/shims/ruby

然后更新gem到最新版本:

gem update --system

现在就可以使用我们的ruby而不使用系统自带的ruby了。如果要继续安装其它的gem,而且你又在国内,确保你换个安装源。因为国内防火墙的原因,无法直接使用gem自带的源来安装。

总结一下整个安装的过程:

  1. 为了避免和Mac系统自带的ruby发生冲突,我们通过安装rbenv来绕过系统自带的ruby。
  2. 通过brew来安装rbenv和ruby-build。(rbenv需要ruby-build)
  3. 将rbenv的执行目录放在系统自带的ruby目录前面来保证接下来运行的ruby命令是从rbenv来的。
  4. 通过rbenv选择一个先要安装的ruby版本进行安装。用gloabl命令将这个版本的ruby定为全局使用的默认ruby版本。
  5. 更新gem。
  6. 替换gem的安装源。

参考:
https://robots.thoughtbot.com/using-rbenv-to-manage-rubies-and-gems
http://stackoverflow.com/questions/10940736/rbenv-not-changing-ruby-version
http://stackoverflow.com/questions/14607193/installing-gem-or-updating-rubygems-fails-with-permissions-error

 
about 2 years ago

I had the following code:

from django.core.urlresolvers import reverse

class UserProfileView(FormView):
    template_name = 'profile.html'
    form_class = UserProfileForm
    success_url = reverse('index')

When the code above runs, an error is thrown:

django.core.exceptions.ImproperlyConfigured: The included urlconf 'config.urls' does not appear to have any patterns in it. If you see valid patterns in the file then the issue is probably caused by a circular import.

There are two solutions to this problem, solution one:

from django.core.urlresolvers import reverse_lazy 

class UserProfileView(FormView):
    template_name = 'profile.html'
    form_class = UserProfileForm
    success_url = reverse_lazy('index') # use reverse_lazy instead of reverse

Solution 2:

from django.core.urlresolvers import reverse

class UserProfileView(FormView):
    template_name = 'profile.html'
    form_class = UserProfileForm
    
    def get_success_url(self): # override this function if you want to use reverse

        return reverse('index')

According to Django's document, reverse_lazy should be used instead of reverse when your project's URLConf is not loaded. The documentation specifically points out that reverse_lazy should be used in the following situation:

  • providing a reversed URL as the url attribute of a generic class-based view. (this is the situation I encountered)

  • providing a reversed URL to a decorator (such as the login_url argument for the django.contrib.auth.decorators.permission_required() decorator).

  • providing a reversed URL as a default value for a parameter in a function’s signature.

It is unclear when URLConf is loaded. At least I cannot find the documentation on this topic. So if the above error occurs again, try reverse_lazy

 
about 2 years ago

I didn't expect creating a radio input in a form using Bootstrap3's style could have some many pitfalls.

Here is a model I have:

class UserProfile(models.Model):
    GENDER_CHOICES = (('M', 'Male',), ('F', 'Female',))
    fullname = models.CharField(u'Name', max_length=10)
    sex = models.CharField(u'Gender', max_length=1, choices=GENDER_CHOICES)
    # ... and 11 other fields

Here is the corresponding ModelForm:

from django.forms.widgets import RadioSelect

class UserProfileForm(ModelForm):
    class Meta:
        model = UserProfile
        widgets = {
            'sex': forms.RadioSelect()
        }

I have a sex field which let users to pick their gender. When running the code above, I get three choices instead of two:

  • '--------' # django automatically insert this 'default' option
  • 'Male'
  • 'Female'

This is of course not what we want. There should only be two options, 'Male' and 'Female'. So to correct this issue, I had to add TWO extra parameters to the Model:

sex = models.CharField(u'Gender', max_length=1, choices=GENDER_CHOICES, blank=False, default='M')

Note that I added blank=False AND default='M'. If you only include blank=False, you will still see the '--------' option. It is important to have both of them set.

Now, I want to style my forms using Bootstrap3. To do this, I recommend using crispy-forms

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from crispy_forms.bootstrap import InlineRadios

class UserProfileForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(UserProfileForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.method = 'post'
        self.helper.form_action = 'index'
        self.helper.layout.append(Submit('save', 'Save'))

    class Meta:
        model = UserProfile

Crispy-form creates a Bootstrap3 style input field for every single field defined in the model when this line runs:

self.helper = FormHelper(self)

The UserProfile model has 13 fields. It is nice that I don't have to specify each one of the 13 fields. But here comes the problem, when the radio input is been rendered. The male and female options are listed vertically. I want them to be listed horizontally. Crispy-form does provide an inline option for rendering radio inputs horizontally. This is nice, but how can I replace the default Layout of the gender field with the suggested inline Layout?

self.helper[1] = InlineRadios('sex')

Crispy-forms allows you to access the Layout of each field by using index.

With the tweaks above, now I have a wonderful radio input for gender selection :) So many small things need to be taken care of for something seems so trivial.

 
about 2 years ago

When writing a user registration form in Django, you are likely to encounter this error message:

A user with that Username already exists.

This happens when a new user wants to register with a name that is already stored in the database. The message itself is self explainatory but what I need is to display this message in Chinese. According to Django's documentation, I should be able to do this:

class RegistrationForm(ModelForm):
    class Meta:
        model = User
        error_messages = {
            'unique': 'my custom error message',
        }

But this didn't work. It turns out that Django's CharField only accepts the following error message keys:

Error message keys: required, max_length, min_length

Thanks to this StackOverflow post, here is how Django developers solved this problem in the UserCreationForm, we can adopt their solution to this situation:

class RegistrationForm(ModelForm):
    # create your own error message key & value

    error_messages = {
        'duplicate_username': 'my custom error message'
    }
    
    class Meta:
        model = User
    
    # override the clean_<fieldname> method to validate the field yourself

    def clean_username(self):
        username = self.cleaned_data["username"]
       
        try:
            User._default_manager.get(username=username)
            #if the user exists, then let's raise an error message

            raise forms.ValidationError( 
              self.error_messages['duplicate_username'],  #user my customized error message

              code='duplicate_username',   #set the error message key

                )
        except User.DoesNotExist:
            return username # great, this user does not exist so we can continue the registration process


Now when you try to enter a duplicate username, you will see the custom error message being shown instead of the default one :)

 
over 2 years ago

gem默认的亚马逊S3服务器被墙,可以改用淘宝提供的安装链接: https://ruby.taobao.org

替换默认服务器的方法:

$ gem sources --remove https://rubygems.org/
$ gem sources -a https://ruby.taobao.org/
$ gem sources -l
*** CURRENT SOURCES ***

https://ruby.taobao.org
# 请确保只有 ruby.taobao.org
$ gem install rails

 
over 2 years ago

在一个文字框里加入灰色的注释文字帮助用户理解应该输入什么信息是常见的做法。在Django里有两种方式可以为输入框添加文字注释。假设我们有一个Model:

from django.db import models

class User(models.Model):
    user_name = models.CharField()

如果需要提示用户名应该是Email地址的话,有两种方法.

方法一:

import django.forms as forms
from models import User

class Login(forms.ModelForm):
    user_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': u'输入Email地址'}))
    
    class Meta:
        model = User

方法二:

import django.forms as forms
from models import User

class Login(forms.ModelForm):
  
    class Meta:
        model = User
        widgets = {
             'user_name': forms.TextInput(attrs={'placeholder': u'输入Email地址'}),
        }
 
over 2 years ago

检查Tuple里的每个元素

假设有一个Tuple,里面包含了几个元素:

p = (170, 0.1, 0.6)
if p[1] >= 0.5:
      print u'好深的'
if p[2] >= 0.5:
    print u'好亮啊'

这段代码本身没有任何问题,但是写的时候需要记住Tuple里每个元素都是什么,才能打印出对的描述。为了让代码更容易看懂:

from collections import namedtuple

Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
p = Color(170, 0.1, 0.6)
if p['saturation'] >= 0.5:
      print u'好深的'
if p['luminosity'] >= 0.5:
    print u'好亮啊'

计算列表里的重复元素

假设有一个叫做颜色的列表, 需要计算出这个列表里每个颜色的名字被重复了几次

colors = ['red', 'green', 'red', 'blue', 'green', 'red']

d = {}

一般书写方式:

for color in colors:
    if color not in d:
        d[color] = 0
    d[color] += 1

稍好一点的写法:

for color in colors:
    d[color] = d.get(color, 0) + 1

最好的写法:

from collections import defaultdict
d = defaultdict(int)

for color in colors:
    d[color] += 1

将一个字典里的内容归类

有一个列表,需要将列表中的内容根据长度归类

names = ['raymond', 'rachel', 'matthew', 'roger', 'bettry', 'melissa', 'judith', 'charlie']

一般写法:

d = {}
for name in names:
    key = len(name)
    if key not in d:
        d[key] = []
    d[key].append(name)

稍好一点的写法:

for name in names:
    key = len(name)
    d.setdefault(key, []).append(name)

最好的写法:

d = defaultdict(list)
for name in names:
    key = len(name)
    d[key].append(name)

使用Keyword Argument

tw('@obama', False, 20, True)

如果不看ts函数的内容的话,是无法理解这个函数是干什么用的,如果改写成这样呢:

twitter_search('@obama', retweets=False, numtweets=20, popular=True)

同时更新多个变量

编程的时候经常会碰到这种情况,需要用一个临时的变量来存住一个数值,然后过一会再把这个数值取出来

t = y
y = x + y
x = t

最好的写法:

x, y = y, x+y

所有等号右侧的数值都是旧的数值。这个写法的好处是不需要像原来那样担心每一行顺序的问题。

对序列起始位置做改动

当改动序列第一位的元素时,经常会引起程序速度变慢

names = ['raymond', 'rachel', 'matthew', 'roger', 'bettry', 'melissa', 'judith', 'charlie']

#以下任意操作都会很慢

del names[0]
names.pop(0)
names.insert(0, 'mark')

最好的方式:

from collections import deque

#将names变为可以在左右两端添加或删减的数据类型

names = deque(['raymond', 'rachel', 'matthew', 'roger', 'bettry', 'melissa', 'judith', 'charlie'])

引自:
https://www.youtube.com/watch?v=OSGv2VnC0go
https://www.youtube.com/watch?v=wf-BqAjZb8M

 
over 2 years ago

用select_related来提取数据和不用select_related来提取数据的差别。

我有一个Model(用户每天会填写一些问题,这个Model用来收集用户每天填写的问题的答案):

class Answer(models.Model):
    day_id = models.IntegerField()
    set_id = models.IntegerField()
    question_id = models.IntegerField()
    answer = models.TextField(default='')
    user = models.ForeignKey(User)

    def __unicode__(self):
        result = u'{4}:{0}.{1}.{2}: {3}'.format(self.day_id, self.set_id, self.question_id, self.answer, self.user.id)
        return result

这个Model除了自带的Field外还有一个外键 user,用于引用Django自带的User表。

我需要将所有用户的每天的答案都输出到一个文件里, 所以首先要提取出所有的答案:

answers = Answer.objects.all()

for answer in answers:
    print answer

运行这个指令会对数据库进行981次请求。数据库里有980行数据,Answer.objects.all()算是一次请求。那么剩下的980次请求是在干嘛呢?问题出在:

result = u'{4}:{0}.{1}.{2}: {3}'.format(self.day_id, self.set_id, self.question_id, self.answer, self.user.id)

当打印answer这个Object的时候,除了会打印出自身的Field外,还会打印出user.id. 由于Answer这个表中没有user.id这个Field,所以要单独请求User的表给出相应的id。这个就是造成980次请求的原因。每次打印一个answer,都要请求一遍User表来获取相对应的用户id。

这种提取数据的方式明显是不合理的,为了能一次完成任务,可以使用select_related:

answers = Answer.objects.all().select_related('user')

for answer in answers:
    print answer

这样,数据库请求只有一次。select_related()的作用其实是把Answer和User这两张表给合并了:

SELECT ... FROM "answer" INNER JOIN "auth_user" ON ( "answer"."user_id" = "auth_user"."id" )

这样页面的载入时间就会被大大减少。

 
over 2 years ago

最近在搭建服务器时需要每天对数据库里的内容进行备份,所以简单学习了下crontab这个命令的使用方式。

目标

  • 每天凌晨1点对数据库进行备份
  • 每次备份后将数据库文件加上时间戳并存储到指定文件夹
  • 备份完成后在log里写入当前时间和备份文件名

先写一个bash脚本

#! /bin/bash

cur_time=$(date +%Y_%m_%d_%k_%M_%S)    #获取当前时间,并按照 年_月_日_小时_分钟_秒 的格式存储

file_name=./db_backup/db.sqlite3.bak_$cur_time    #将刚获取的时间接在备份文件名的后面

sqlite3 db.sqlite3 .dump > $file_name    #对一个sqlite数据库进行备份

now=$(date +'%x %X')    #获取现在的时间

output=$(printf "[ %s ] db backed @ %s" "$now" "$file_name")    #写一行log

echo $output >> ~/logs/cron.log    #将log写入指定文件

将上述文件保存为一个脚本,比如backup.sh

再来添加定时重复的工作

在Linux下使用crontab这个命令可以做到:

  1. crontab -e | 编辑你的定时工作脚本
  2. crontab -l | 查看你的工作脚本
  3. crontab -r | 删除你的工作脚本
  4. crontab -l > 文件名 | 将你的脚本备份到一个文件里
  5. crontab 文件名 | 将指定文件的内容复制到你的工作脚本

编辑定时工作脚本

首先通过下面的命令来创建一个脚本,如果你有设置系统默认的文本编辑器的话,则该文件会在默认的编辑器下打开

crontab -e

如果输入完上述命令后你得到的结果是:

no crontab for root - using an empty one
888

则说明你还没有设置默认的文本编辑器,可以通过下面的命令来设置:

export EDITOR=nano

上面例子中使用了nano,但你可以随意替换成pico、vi等任意编辑器。设置好后,再次执行crontab -e

我希望每天凌晨一点自动备份一下数据库,那么我会在crontab的文件里这么写:

0 1 * * * /home/cheng/backup.sh

第一个0代表0分,1代表1点,后面的三个*分别代表日,月,周日。最后的文件路径是你需要执行的脚本。

前五个数值的范围分别为:

  • 分 0 - 59
  • 小时 0 - 23
  • 日 1 - 31
  • 月 1 - 12
  • 周日 sun, mon, tue, wed, thu, fri, sat (可以用这些缩写表示一周中的那一天,也可以用数字 0 - 7 表示, 注意0和7都是周日的意思)

下面是一些简单的示例:

每周日下午五点执行一次:

0 17 * * sun  /scripts/script.sh

每6小时执行一次:

0 */6 * * * /scripts/script.sh

每分钟执行一次:

* * * * * /scripts/script.sh

找到一个很好的参考网站: http://tecadmin.net/crontab-in-linux-with-20-examples-of-cron-schedule/

Crontab文件的存储位置

每个用户通过crontab -e创建的脚本文件会被存储在:

cd /var/spool/cron/crontabs/

这个文件夹下每个用户的脚本文件会以该用户的用户名进行区分存储。

Reference:
http://www.ibm.com/developerworks/library/l-job-scheduling/

 
over 2 years ago

I wrote two simple scripts for starting and stopping a gunicorn server that hosts a Django project.

Create a run.sh:

#!/bin/bash


source `which virtualenvwrapper.sh`  #assuming you use virtualenv

workon your_env_name # if you do not use virtualenv, remove this line the line above

gunicorn -c gunicorn.conf.py your_project_name.wsgi --daemon 

Create a stop.sh:

#!/bin/bash


kill -9 `ps aux |grep gunicorn |grep your_app_name | awk '{ print $2 }'`  # will kill all of the workers

Once these scripts are created, don't forget to set the permission:

chmod 755 run.sh
chmod 755 stop.sh

To run them:

./run.sh
./stop.sh