almost 3 years ago

There are times when you are not worried about user authentication but still want to have each user sees only his/her stuff. Then you need a way to login a user without password, here is the solution posted on stackoverflow

Normally, when logging a user with password, authenticate() has to be called before login():

username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
login(request, user)

What authenticate does is to add an attribute called backend with the value of the authentication backend, take a look at the line of the source code. The solution is to add this attribute yourself:

#assuming the user name is passed in via get
username = self.request.GET.get('username')
user = User.objects.get(username=username)

#manually set the backend attribute
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)

That's it!

 
almost 3 years ago

When a user request a page view from a website (powered by Django), a cookie is returned along with the requested page. Inside this cookie, a key/value pair is presented:

Cookie on the user's computer

Key          Value
---          -----
sessionid    gilg56nsdelont4740onjyto48sv2h7l

This id is used to uniquely identify who's who by the server. User A's id is different from User B's etc. This id is not only stored in the cookie on the user's computer, it is also stored in the database on the server (assuming you are using the default session engine). By default, after running ./manage.py migrate, a table named django_session is created in the database. It has three columns:

django_session table in database

session_key        session_data        expire_date
---------------------------------------------------
y5j0jy3l4v3        ZTJlMmZiMGYw        2015-05-08 15:13:28.226903

The value stored in the session_key column matches the value stored in the cookie received by the user.

Let's say this user decides to login to the web service. Upon successfully logged into the system, a new sessionid is assigned to him/her and a different session_data is stored in the database:

Before logging in:

session_key        session_data        expire_date
---------------------------------------------------
437383928373        anonymous           2015-05-08 15:13:28.226903
After logging in:

session_key        session_data        expire_date
---------------------------------------------------
218374758493        John               2015-05-08 15:13:28.226903

*I made up this example to use numbers and usernames instead of hash strings. For security reasons, these are all hash strings in reality.

As we can see here, a new session_key has been assigned to this user and we now know that this user is 'John'. Form now on, John's session_key will not change even if he closes the browser and visit this server again. Thus, when John comes back the next day, he does not need to login again.

Django provides a setting to let developers to specify this behaviour, in settings.py, a variable named SESSION_SAVE_EVERY_REQUEST can be set:

SESSION_EXPIRE_AT_BROWSER_CLOSE = False # this is the default value. the session_id will not expire until SESSION_COOKIE_AGE has reached.

If this is set to True, then John is forced to login everytime he visits this website.

Since saving and retrieving session data from the database can be slow, we can store session data in memory by:

#Assuming memcached is installed and set as the default cache engine
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'

The advantage of this approach is that session store/retrival will be faster. But the downside is if the server crashes, all session data is lost.

A mix of cache & database storage is:

SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'

According to django's documentation:

every write to the cache will also be written to the database. Session reads only use the database if the data is not already in the cache.

This approach is slower than a pure cache solution but faster than a purse db solution.

Django's offical document did warn to not use local-memory cache as it doesn't retain data long enough to be a good choice.

By default the session data for a logged in user lasts two weeks in Django, users have to log back in after the session expires. This time period can be adjusted by setting the SESSION_COOKIE_AGE variable.

 
almost 3 years ago

I had to deploy a django project on CentOS 6.5 and here are the steps to set up Python and then Django. The problem with CentOS 6.5 is that it comes with python 2.6.6 and it's package installer yum relies on it.

Although we can install python 2.7.x and soft link 2.7.x as the system default, it will break yum and who knows what else relies on 2.6.6. Thus, I am installing python 2.7.x via pyenv, which is suggested by this blog post.

This solution is cleaner as it does not temper with the OS default python 2.6.6.

  1. Update all of yum's package
yum -y update 
  1. Install pyenv, which works like rbenv:
curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash

Put the following lines into your .bashrc file:

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

Then, reload bashrc:

. .bashrc

This will enable the pyenv command in bash.

  1. Install these packages before installing python:
yum install zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel mysql-devel 
  1. Use this command to list all of the 2.7.x version of python (grep 3.x for python 3):
pyenv install -l | grep 2.7 
  1. Install the python version you prefer:
pyenv install 2.7.10 
  1. Use the newly installed version of python:
pyenv local 2.7.10

You can list all python versions abailable to pyenv by:

pyenv versions
  1. Update pip to its latest version:
pip install -U pip

Now, we have everything setup and ready to go. The natural thing to do next is to install virtualenv and virtualenvwrapper:

Installing virtualenv and virtualenvwrapper

WARNING: DO NOT install virtualenv and virtualenvwrapper !!!
Let me tell you what happend after I installed both via pip. I put the following lines in .bashrc

export WORKON_HOME=$HOME/.virtualenvs
source ~/.pyenv/shims/virtualenvwrapper.sh  #virtualenvwrapper.sh got installed to this dir instead of /usr/local/bin/

Then, I was disconnected from the cloud server with the following warning:

pyenv: -bash: command not found

It doesn't matter what I try, I cannot connect to the cloud server anymore because every time I login successfully, I get disconnected from the server with the same error above. I cannot even login using the VNC connection provided by the cloud service provider. The only option I had was to reinstall the image on the cloud server...

I cannot find the cause of this issue on Google, but from the look of it, I messed out the path to bash so everytime I logged in, cannot find bash -> disconnect

Don't worry you can still use virtualenv

If you look under the .pyenv/plugins directory, you can see a directory named pyenv-virtualenv. This is what we can use to create virtual environments.

To create a test environment:

pyenv virtualenv test  # create a virtualenv named test based on the current local or system version of python

Since we have already set our local version of python to be 2.7.10, this test env inherits from that. But if you did not set the local version, the test env will inherit from the system version of python which may not be what you want. (Note: while running the command above, the 'virtualenv' package is installed to your local version of python)

You can also specify which version of python to use (by putting a version number after 'pyenv virutalenv'):

pyenv virtualenv 2.6.6 test # use version 2.6.6 for this environment

Once the test environment has been setup, you can verify it by:

pyenv versions

You should see something similar to this:

  system
* 2.7.10 (set by /.python-version)
  test

You can switch to the test env by:

pyenv activate test

To deactivate:

pyenv deactivate

To list virtualenvs:

pyenv virtualenvs

To delete existing virtualenvs:

pyenv uninstall my-virtual-env

If you have seen pyenv-virtualenv's github page, you have probably noticed something named pyenv-virtualenvwrapper. Don't install it unless you know what it is. I don't have the time to figure out what it is ATM. But it is definitely NOT the virtualenv + virtualenvwrapper combination you are familiar with. So be cautious!

 
almost 3 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

 
almost 3 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

 
almost 3 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.

 
almost 3 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 :)

 
almost 3 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

 
almost 3 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地址'}),
        }
 
about 3 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