django-userena使用记录

发布在 Django

django-userena扩展了django原生的用户系统,提供了注册、登录、修改密码、邮件验证等一系列常用功能。直接使用pip安装即可:pip install django-userena

会自动安装其所需的依赖包,不过个人建议为了更好的定制模板或相关功能,把这个包放到项目目录下当作一个app更方便一些。安装完成后修改settings.py,首先来创建一个app用于扩展用户系统
python manage.py startapp accounts
然后修改Models.py来扩展原生用户字段,我这里以添加用户等级为例:

1
2
3
4
5
6
7
8
9
10
11
# coding=utf-8
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from userena.models import UserenaBaseProfile
class CustomerProfile(UserenaBaseProfile):
user = models.OneToOneField(User,
unique=True,
verbose_name=_('user'),
related_name='customer_profile')
level = models.IntegerField(_(u"用户等级"), default=0)

接下来修改settings.py,首先把

1
2
3
4
5
"django.contrib.sites",
"accounts",
'userena',
'guardian',
'easy_thumbnails'

添加到INSTALLED_APPS中,然后把'django.contrib.sites.middleware.CurrentSiteMiddleware',
添加到MIDDLEWARE_CLASSES中,在django1.7.7中如果不添加和site有关的东西时,userena注册用户会抛出”Site matching query does not exist“异常。最后添加下面的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AUTHENTICATION_BACKENDS = (
'userena.backends.UserenaAuthenticationBackend',
'guardian.backends.ObjectPermissionBackend',
'django.contrib.auth.backends.ModelBackend',
)
EMAIL_HOST = 'xxxx'
EMAIL_HOST_USER = 'xxxx@xxxx'
EMAIL_HOST_PASSWORD = 'xxxx'
EMAIL_PORT = 25
DEFAULT_FROM_EMAIL = 'xxxx@xxxx'
ANONYMOUS_USER_ID = -1
AUTH_PROFILE_MODULE = 'accounts.CustomerProfile'
USERENA_SIGNIN_REDIRECT_URL = '/accounts/%(username)s/'
LOGIN_URL = '/accounts/signin/'
LOGOUT_URL = '/accounts/signout/'

xxxx根据实际情况修改,和发送验证邮件有关。这些都添加完毕后,执行
./manage.py migrate
创建数据库,接下来执行
./manage.py check_permissions
否则报错”Permission matching query does not exist“。最后记得添加url

1
url(r'^accounts/', include('userena.urls')),

都完成后,运行程序,访问http://127.0.0.1:8000/accounts/signup/就可以看见注册页面了。

所有html文件以及email模板都在userena/templates/userena路径下,进行相应定制即可。

评论和分享

最近有个需求就是当执行save时需要把数据写入2个数据库,查看文档后发现直接重写save方法比较简单。

首先建立2个测试数据库testa和testb,然后在settings中配置数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'testa',
'USER': 'root',
'PASSWORD': 'asdasd',
'HOST': '192.168.0.17',
},
'testdb': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'testb',
'USER': 'root',
'PASSWORD': 'asdasd',
'HOST': '192.168.0.18',
},
}

接下来定义model:

1
2
3
4
5
6
7
8
9
10
11
from django.db import models
# Create your models here.
class ABCD(models.Model):
name = models.CharField(max_length=200, unique=False)
def __unicode__(self):
return "%s" % (self.name)
def save(self, *args, **kwargs):
print "save testa"
super(ABCD, self).save(*args, **kwargs)
print "save testb"
super(ABCD, self).save(using="testdb", *args, **kwargs)

注意最后一行的using指定了写入哪个库中,接下来执行syncdb命令创建表,这里只会在default中创建,所以我们手动在testdb中创建表。

然后进入shell中测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
% python manage.py shell
Python 2.7.8 (default, Nov 10 2014, 08:19:18)
Type "copyright", "credits" or "license" for more information.
IPython 2.3.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: from abcd.models import ABCD
In [2]: c = ABCD(name="testc")
In [3]: c.save()
save testa
save testb

再看数据库中,插入都成功了。这里可能产生的问题就是大量并发请求时造成主键的不一致,但对于写操作不频繁的情况下也是一种可以接受的选择。

评论和分享

回来半个月,终于抽出时间写点东西了。最近这天气真是让人不开心阿~不过昨天居然见到了彩虹,突然想起来在飞机上看见的彩虹是直立在太阳两边而不是桥形的,各有各的美感。

至于在win下如何安装python、setuptools、pip等网上很多这里不再重复,可是那些大小姐们向我反映说她们不会“运行cmd,输入python manage.py runserver”。正所谓收人钱财替人消灾,干脆写个批处理让她们双击运行算了,内容如下:

1
2
3
@echo off
python %cd%\manage.py runserver
pause

真是被微软“惯坏了”的孩子们阿….

评论和分享

django自带的评论模块

发布在 Django

某个设计本来采用了一个比较不错的在线评论模块,不过答辩的时候丫居然不给网!!想偷个懒还是挺难啊….

那就用自带的评论模块吧,django版本是1.5.3。

首先把'django.contrib.comments'添加到INSTALLED_APPS中,然后添加url:

1
2
3
4
5
urlpatterns = patterns('',
......
url(r'^comments/', include('django.contrib.comments.urls')),
......
)

在需要显示评论的HTML中:

1
2
3
{% load comments %}
{% render_comment_list for post %} <!--显示所有属于这个文章的评论-->
{% render_comment_form for post %} <!--显示自带的添加评论的form-->

不过自带的添加评论实在太丑了,我们可以自己改造一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{% get_comment_form for post as form %}
<form action='{%comment_form_target%}' method='post'>
{% csrf_token %}
{{form.object_pk}}
{{form.content_type}}
{{form.timestamp}}
{{form.security_hash}}
<p><label for="id_name">姓名(必填):</label><input name="name" id="id_name"></p>
<p><label for="id_email">邮箱(必填):</label><input name="email" id="id_email"></p>
<p><label for="id_url">网站(可选):</label><input name="url" id="id_url"></p>
<p><label for="id_comment">评论(必填):</label></p>
<p><textarea id="id_comment" rows="10" cols="40" name="comment"></textarea></p>
<p style="display:none;"><label for="id_honeypot">垃圾评论。</label>
<input type="text" name="honeypot" id="id_honeypot"></p>
<p><input name="post" value="发表" type="submit" /></p>
<input type='hidden' name='next' value="/blog/post/{{post.id}}"/> <!--这个是提交后的重定向,使用软编码总报错...-->
</form>

然后再进行CSS或者JS一类的特效美化就OK了。

评论和分享

自己封装了一个logging在django中使用,结果发现输出的时候总是重复输出,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DEBUG---- 2014/03/31 10:38:13:
!!
DEBUG---- 2014/03/31 10:38:13:
!!
DEBUG---- 2014/03/31 10:38:13:
!!
DEBUG---- 2014/03/31 10:38:13:
!!
DEBUG---- 2014/03/31 10:38:13:
140091903388560
DEBUG---- 2014/03/31 10:38:13:
140091903388560
DEBUG---- 2014/03/31 10:38:13:
140091903388560
DEBUG---- 2014/03/31 10:38:13:
140091903388560

可以看出,id都是一样的。而且我查了一下,正好4个地方引入了mylog。mylog的完整代码见以前日志,这里给出导致错误的原因:
self.logger = logging.getLogger(__name__)

简单测试:

1
2
3
4
5
6
mlog = MyLog()
print id(mlog)
print id(mlog.logger)
xmlog = MyLog()
print id(xmlog)
print id(xmlog.logger)

结果如下:

1
2
3
4
123456666
123456789
123457777
123456789

可以看出虽然类id不同,不过核心的logger却是一个。因为我们执行runserver后,__name__就确定为mylog了,所以不管我们引用的时候如何写,__name__不变则都指向了同一个logger。更简单的例子如下:

1
2
3
4
5
6
In [1]: x = 1
In [2]: y = 1
In [3]: id(x)
Out[3]: 11279464
In [4]: id(y)
Out[4]: 11279464

更细节的原因可以看《python源码分析》。

改起来就让__name__不一样就解决了。比如:

self.logger = logging.getLogger(str(uuid.uuid4()))

评论和分享

第一种HttpResponse,最基本的返回方式,可以直接返回字符:

1
2
3
from django.http import HttpResponse
def index(request):
return HttpResponse(“a test”)

或者结合contextloder返回网页:

1
2
3
4
5
6
7
from django.template import Context, loader
from polls.models import Poll
def index(request):
latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = Context({ 'latest_poll_list': latest_poll_list, })
return HttpResponse(template.render(context))

阅读全文

django数据库分库

发布在 Django

有些时候我们需要项目中的app访问不同的数据库,这时就要进行分库操作。

首先建立一个db_router.py,内容示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#coding=utf-8
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': 'xxxx', # Or path to database file if using sqlite3.
'USER': 'root', # Not used with sqlite3.
'PASSWORD': '123456', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '3306', # Set to empty string for default. Not used with sqlite3.
'OPTIONS': {
'init_command': 'SET storage_engine=MyISAM',
},
},
'analysis_db':{
'ENGINE': 'django.db.backends.mysql',
'NAME': 'analysis_qq', #分析相关的操作用这个库
'HOST': '',
'USER': 'root',
'PASSWORD': '123456',
'PORT':'3306',
'OPTIONS':{
'init_command': 'SET storage_engine=MyISAM',
},
},
}
class MasterSlaveRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label == 'analysisqq':
return 'analysis_db'
else:
return 'default'
def db_for_write(self, model, **hints):
if model._meta.app_label == 'analysisqq':
return 'analysis_db'
else:
return 'default'
def allow_relation(self, obj1, obj2, **hints):
db_list = ('default','analysis_db',)
if obj1._state.db in db_list and obj2._state.db in db_list:
return True
return None
def allow_syncdb(self, db, model):
if db == 'analysis_db':
print " in analysis db"
return True
elif db == 'default':
return False
else:
return None
DATABASE_ROUTERS = ['db_router.MasterSlaveRouter']

然后在settings.py中删除和数据库相关的代码,添加from db_router import *,这样之后指定的app就会使用特定的库了。

注意,这里分库是针对于读写操作,以及default已经存在后的syncdb,如果说你想执行syncdb同时就在不同数据库建表,这样操作是无效的。知道怎么做的请告知!

评论和分享

django文件上传功能

发布在 Django

今天需要一个文件上传功能,所以代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def ci_test(request):
try:
if request.POST.has_key("send"):
xml_string=""
final_xml = ""
file_obj = request.FILES.get('file', None)
for keys in request.POST:
if request.POST[keys]:
print keys,request.POST[keys]
xml_string = "<%s>%s</%s>n%s" % (keys,request.POST[keys],keys,xml_string)
final_xml = "<root>n%s</root>" % xml_string
dirname = os.path.dirname(os.path.abspath(__file__))
parent = os.path.split(dirname)[0]
if request.POST["source"] == "text" and file_obj:
if file_obj.name.endswith(".txt"):
#print parent
filepath = "%s/core/emaillist.txt" % parent
#print filepath
open(filepath,'w').write(file_obj.read()) #注意这里
else:
raise forms.ValidationError("上传文件类型错误!")
path = "%s/core/config/%s.xml" % (parent,request.POST['txtDate'])
open(path,'w').write(final_xml.encode('utf-8'))
else:
smtp = request.POST['smtp']
username = request.POST['username']
passwd = request.POST['passwd']
title = request.POST['title'] #邮件标题
content = request.POST['content'] #邮件内容
attach = request.POST['useattach'] #是否是附件发送
ctype = request.POST['c_type'] #邮件格式
testsend = request.POST['testsend']#测试邮箱
subtem = u"%s: %s" % (u"测试",title)
conn = creat_conn(smtp, username, passwd)
tem = testsend.split(';')
if ctype.upper() == "HTML":
final_body = creat_htmlbody(content)
#测试发送,不用记录日志直接传列表
final_testmail(subtem,final_body,username,tem,conn,attach)
print "test send to %s sucess!" % tem
else:
final_testmail(subtem,content,username,tem,conn,"test")
print "test send to %s sucess!" % tem
return render(request,'test.html',{"msg":"请求已经提交,关闭本页面即可"})
except forms.ValidationError:
print "in my error"
return render(request,'test.html',{{"msg":"文件类型错误,请重新操作!"}})
except Exception:
return render(request,'test.html',{{"msg":"发生错误,请重新操作!"}})

此处需要注意模版文件中form的写法:

1
2
3
<form action="ci_test" method="post" onsubmit="return validate_form(this);" enctype="multipart/form-data">
<input type="file" name="file"/>
</form>

不过此处有个奇怪的问题,根据流程来看,如果上传的不是.txt文件则引发异常,也确实输出了”in my error”这句话,不过随后就引发了下面的异常,不知道render为什么错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
TypeError at /mailinfo/ci_test
unhashable type: 'dict'
Request Method: POST
Request URL: http://127.0.0.1:8000/mailinfo/ci_test
Django Version: 1.5.3
Exception Type: TypeError
Exception Value:
unhashable type: 'dict'
Exception Location: /home/xingsongyan/workspace/send/mailinfo/views.py in ci_test, line 66
Python Executable: /usr/bin/python2.7
Python Version: 2.7.5
Python Path:
['/home/xingsongyan/workspace/send',
'/usr/lib/python2.7/site-packages/pip-1.4.1-py2.7.egg',
'/home/xingsongyan/workspace/send',
'/usr/share/eclipse/dropins/pydev/eclipse/plugins/org.python.pydev_2.8.2.2013090511/pysrc',
'/usr/lib64/python27.zip',
'/usr/lib64/python2.7',
'/usr/lib64/python2.7/plat-linux2',
'/usr/lib64/python2.7/lib-tk',
'/usr/lib64/python2.7/lib-old',
'/usr/lib64/python2.7/lib-dynload',
'/usr/lib64/python2.7/site-packages',
'/usr/lib64/python2.7/site-packages/gtk-2.0',
'/usr/lib64/python2.7/site-packages/wx-2.8-gtk2-unicode',
'/usr/lib/python2.7/site-packages',
'/usr/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info',
'/usr/lib64/python2.7/site-packages/PIL']
Server time: Wed, 4 Dec 2013 16:05:25 +0800

评论和分享

django中静态文件的使用

发布在 Django

记得我刚刚开始接触django的时候,对于静态文件的引用始终一头雾水,按照网上说明的添加代码就是不好使。
今天再回头看看,突然发现以前自己还真是笨阿!补一篇记录算是弥补以前的缺憾了~
django版本1.5.3
目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
projectname
----projectname
--------templates
------------base.html
------------appname
----------------a.html
--------media
------------mp3
------------flv
--------static
------------img
----------------s.jpg
------------css
------------js
--------settings.py
--------urls.py
----manage.py

方式一

setting.py中添加、修改以下代码

1
2
3
4
5
6
7
import os
SITE_ROOT = os.path.dirname(__file__)
STATIC_ROOT=''
STATIC_URL = '/static/'
STATICFILES_DIRS = (
SITE_ROOT+STATIC_URL, #注意逗号!
)

urls.py修改如下

1
2
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns += staticfiles_urlpatterns()

<img name="a" src="/static/img/s.jpg"/>进行引用。

方式二

避开static相关,用media。

settings.py:

1
2
MEDIA_ROOT = os.path.join(os.path.dirname(__file__),'media/').replace('\','/')
MEDIA_URL = '/site_media/'

urls.py:

from django.conf import settings
    url(r'^site_media/(.*)$','django.views.static.serve',
                          {'document_root':settings.MEDIA_ROOT}),

<img name="a" src="/site_media/img/a.png"/>这里的set_media和上面urls.py中的名要对应。

目录结构自行修改。

个人推荐第一种方式。

评论和分享

django-I18n-国际化语言

发布在 Django

上一篇文章提到了django 的语言问题,查阅资料后发现刚才的解释不是十分准确,看到一篇介绍相关内容的博客感觉不错,节选部分全文

先来看两个概念:

  • 国际化:是指为了该软件在任何地区的潜在使用而进行程序设计的过程。 它包括了为将来翻译而标记的文本(比如用户界面要素和错误信息等)、日期和时间的抽象显示以便保证不同地区的标准得到遵循、为不同时区提供支持,并且一般 确保代码中不会存在关于使用者所在地区的假设。 您会经常看到国际化被缩写为“I18N” (18表示Internationlization这个单词首字母I和结尾字母N之间的字母有18个)。
  • 本地化: 是指使一个国际化的程序为了在某个特定地区使用而进行实际翻译的过程。 有时,本地化缩写为L10N 。

使用Django 国际化

  1. 第一步:在你的Python代码和模板中嵌入待翻译的字符串。
  2. 第二步:把那些字符串翻译成你要支持的语言。
  3. 第三步:在你的Django settings文件中激活本地中间件。

如何嵌入待翻译的字符串

使用函数django.utils.translation.ugettext() 来指定一个翻译字符串。 作为惯例,使用短别名_来引入这个函数以节省键入时间.

1
2
3
4
from django.utils.translation import ugettext as _
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)

小注意:

  1. _ugettext()函数的参数可以是变量,但检测工具make_messages.py将找不到这些字符串。
  2. _占位符:特定语言的译文可能对翻译字符串重新排序,使用位置占位符可能达不到预期效果,建议使用命名字符串插入,如_('today is %(day)s'%{'day':'monday'})
  3. 本人补充——把ugettext函数重命名为下划线不是强制要求,但这样更符合惯例。

扩展知识:

  1. 使用 django.utils.translation.gettext_noop() 函数来标记一个不需要立即翻译的字符串。 这个串会稍后从变量翻译。使用这种方法的环境是,有字符串必须以原始语言的形式存储(如储存在数据库中的字符串)而在最后需要被翻译出来(如显示给用户时)。
  2. 使用 django.utils.translation.gettext_lazy() 函数,使得其中的值只有在访问时才会被翻译,而不是在gettext_lazy() 被调用时翻译。
  3. 使用django.utils.translation.ungettext()来指定以复数形式表示的消息。

模块中嵌入翻译字符串

Django模板使用两种模板标签,为了使得模板访问到标签,需要将

1
{% load i18n %}

放在模板最前面。这个

1
{% trans %}

模板标记翻译一个常量字符串 或 可变内容:

1
2
3
{%load i18n%}
<title>{%trans 'this is a title' %}</title>
<title>{%trans strvar %}</title>

小注意:

在一个带

1
{% trans %}

的字符串中,混进一个模板变量是不可能的。如果译文要求字符串带有变量,使用

1
2
{%blocktrans%}
{%endblocktrans%}

评论和分享

Roy.S

微信公众号:hi-roy


野生程序员


China