Gowhich

Durban's Blog

yii常规post方式提交表单方式请参考:

http://www.yiichina.com/forum/topic/45/

除了常规post提交方式外,还有异步提交方式。
原理是使用iframe代替原来的页面跳转,大大提升用户体验。

1,前端html代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<form id="upForm" action="<?php echo $this->createUrl('repairUpload'); ?>" method="post" enctype    ="multipart/form-data" target="upload_target">  
<input type="file" name="repair_attached_file" id="repair_attached_file" /><input type="subm it" name="submitBtn" value="立即上传" />
<iframe id="upload_target" name="upload_target" src="#" style="width:0;height:0;border:0px solid #fff;"></iframe>
</form>
<span id="upload_repairinfo_success" style="color:blue;"></span>

<script type="text/javascript">
function startUpload() {
var spanObj = document.getElementById("upload_repairinfo_success");
spanObj.innerHTML = " 开始上传";
document.getElementById("upForm").sumbit();
}
function stopUpload(responseText){
var spanObj = document.getElementById("upload_repairinfo_success");
//spanObj.innerHTML = "上传成功";
spanObj.innerHTML = responseText;
}
</script>

2、后端php代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function actionRepairUpload(){  
$attach = CUploadedFile::getInstanceByName('repair_attached_file');
$retValue = "";
if($attach->size > 3*1024*1024){
$retValue = "提示:文件大小不能超过3M";
}else{
$f = file_get_contents($attach->tempName);
$a = new Attachment();
$a->ref_type = "failParts";
$a->data = $f;
$a->file_path = $attach->name;
$a->save();
$retValue = $a->id;
}
echo "<script type='text/javascript'>window.top.window.stopUpload('{$retValue}')</script>";
}

在Yii中使用session

1,CHttpSession
与原生态php5的session使用差别是,php5使用session_start();$_session[‘key’] = $value;
在yii中,session已经被封装。
To start the session, call open(); To complete and send out session data, call close(); To destroy the session, call destroy().

If autoStart is set true, the session will be started automatically when the application component is initialized by the application.

/***** 方式一、实例添加 *****/

1
2
3
$session=new CHttpSession;  
$session->open();
$value1=$session['name1'];

/***** 方式二、直接调用应用添加 *****/

1
2
3
Yii::app()->session->add('name','foobar');  
Yii::app()->session->add('name2','foobar');
Yii::app()->session->add('name3','foobar');

或者

1
2
3
$session = Yii::app()->session;  
$session['key'] = 'value';
var_dump($session['key']);

遍历

1
foreach($session as $name=>$value)

一个实例,

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
$session = new CHttpSession;  
$session->open();

$user_id = $this->user->id;
$sessionKey = $user_id.'_is_sending';

if(isset($session[$sessionKey])){
$first_submit_time = $session[$sessionKey];
$current_time = time();
if($current_time - $first_submit_time < 10){
$session[$sessionKey] = $current_time;
$this->response(array('status'=>1, 'msg'=>'不能在10秒钟内连续发送两次。'));
}else{
unset($session[$sessionKey]);//超过限制时间,释放session";
}
}

//第一次点击确认按钮时执行
if(!isset($session[$sessionKey])){
$session[$sessionKey] = time();
}

var_dump($sessionKey);
var_dump($session[$sessionKey]);
exit();

在index.php
在$app->run();前

1
2
3
4
5
6
7
8
9
$session = Yii::app()->session;  
session_set_save_handler(
array($session,'openSession'),
array($session,'closeSession'),
array($session,'readSession'),
array($session,'writeSession'),
array($session,'destroySession'),
array($session,'gcSession')
);

2,CDbHttpSession
CDbHttpSession继承自 CHttpSession ,把session数据存储在数据库中(表名是YiiSession),
The table name can be changed by setting sessionTableName. If the table does not exist, it will be automatically created if autoCreateSessionTable is set true.

1
2
3
4
5
6
7
#The following is the table structure:
CREATE TABLE YiiSession
(
id CHAR(32) PRIMARY KEY,
expire INTEGER,
data TEXT
)

CDbHttpSession relies on PDO to access database.

By default, it will use an SQLite3 database named ‘session-YiiVersion.db’ under the application runtime directory. You can also specify connectionID so that it makes use of a DB application component to access database.

When using CDbHttpSession in a production server, we recommend you pre-create the session DB table and set autoCreateSessionTable to be false. This will greatly improve the performance. You may also create a DB index for the ‘expire’ column in the session table to further improve the performance.

1
2
3
4
5
6
7
CREATE TABLE `YiiSession` (  
`id` char(32) NOT NULL,
`expire` int(11) default NULL,
`data` text,
PRIMARY KEY (`id`),
KEY `expire` (`expire`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

例,在../config/main.php中配置

1
2
3
4
5
6
7
'session'=>array(  
'class' => 'CDbHttpSession',
'autoStart' => true,
'sessionTableName'=>'YiiSession',
'autoCreateSessionTable'=> false,
'connectionID'=>'db',
),

在Yii中使用cookie

Yii实现了一个cookie验证机制,可以防止cookie被修改。启用之后可以对cookie的值进行HMAC检查。
Cookie验证在默认情况下是禁用的。如果你要启用它,可以编辑应用配置 中的组件中的CHttpRequest部分。
一定要使用经过Yii验证过的cookie数据。使用Yii内置的cookies组件来进行cookie操作,不要使用$_COOKIES。
实例:

1
2
3
4
5
6
7
// 检索一个名为$name的cookie值  
$cookie=Yii::app()->request->cookies[$name];
$value=$cookie->value;
......
// 设置一个cookie
$cookie=new CHttpCookie($name,$value);
Yii::app()->request->cookies[$name]=$cookie;

首先,在Yii框架中,你不需要像标准PHP代码那样使用session_start(),
在Yii框架中,autoStart 属性缺省被设置为true,所以,
虽然没有使用session_start(),你仍然可以使用$_SESSION全局变量,但最好使用
Yii框架封装的Yii::app->session:

设置session变量:

1
Yii::app()->session['var']='value';

使用:Yii::app()->session['var'];

移除:unset(Yii::app()->session['var']);

更为复杂一点的使用时如何配置你的session
配置项可设在 protected/config/main.php的components中:

1
2
3
4
5
6
'session'=>array(
'autoStart'=>false(/true),
'sessionName'=>'Site Access',
'cookieMode'=>'only',
'savePath'='/path/to/new/directory',
),

将session保持在数据库的设置:

1
2
3
4
5
'session' => array (
'class' => 'system.web.CDbHttpSession',
'connectionID' => 'db',
'sessionTableName' => 'actual_table_name',
),

好,还有什么呢?对了,为了调试,有时需要知道当前用户的session ID,
该值就在 Yii::app()->session->sessionID 中。

最后,当用户退出登录(logout),你需要消除痕迹,可使用:
Yii::app()->session->clear()移去所有session变量,然后,调用
Yii::app()->session->destroy()移去存储在服务器端的数据。

其实很简答的,只要你想加入类似keywords或者content的内容的meta的话是很简单的,代码如下:

1
2
Yii::app()->clientScript->registerMetaTag('Content description ',"description");
Yii::app()->clientScript->registerMetaTag("新媒体指数、[name]vlinkage投票排名[rank]","keywords");

添加js,css文件

1
2
Yii::app()->clientScript->registerCssFile(Yii::app()->baseUrl.'/css/my.css');
Yii::app()->clientScript->registerScriptFile(Yii::app()->baseUrl.'/css/my.js');

关于Yii中的url的跳转,有两个方法,第一个是Redirect,第二个是Forward

Yii的Rediret

使用方法

1
$this->redirect(array('index'));

对应的是当前controller的index action

http://www.localyii.com/testwebap/index.php?r=user/index

1
$this->redirect(array('view','id'=>$model->id));

对应的是当前controller的view action并传递id参数值为3

http://www.localyii.com/testwebap/index.php?r=user/view&id=3

1
2
$this->redirect(array('/site/contact','id'=>12));
$this->redirect(array('site/contact','id'=>12));

http://www.localyii.com/testwebap/index.php?r=site/contact&id=12

1
$this->redirect(array('site/contact','id'=>'idv','name'=>'namev'));

http://www.localyii.com/testwebap/index.php?r=site/contact&id=idv&name=namev

1
$this->redirect(array('site/contact','v1','v2','v3'));

http://www.localyii.com/testwebap/index.php?r=site/contact&0=v1&1=v2&2=v3

1
$this->redirect(array('site/contact','v1','v2','v3','#'=>'ttt'));

带anchor的
http://www.localyii.com/testwebap/index.php?r=site/contact&0=v1&1=v2&2=v3#ttt

1
$this->redirect(array('user/create','v1','v2','v3','#'=>'ttt'));

http://www.localyii.com/testwebap/index.php?r=user/create&0=v1&1=v2&2=v3#ttt
modules的redirect

1
$this->redirect(array('testmod/default/index','v1','v2','v3','#'=>'ttt'));

http://www.localyii.com/testwebap/index.php?r=testmod/default/index&0=v1&1=v2&2=v3#ttt
跳转到一个绝对路径

1
$this->redirect('http://www.baidu.com');

函数的原型是

1
2
3
4
5
6
7
8
public function redirect($url,$terminate=true,$statusCode=302)
{
if(strpos($url,'/')===0)
$url=$this->getHostInfo().$url;
header('Location: '.$url, true, $statusCode);
if($terminate)
Yii::app()->end();
}

Yii的forward

1
2
3
$this->forward('/testmod/default/index');

$this->forward('testmod/default/index');

地址栏url

http://www.localyii.com/testwebap/index.php

forward是不需要参数的

函数的原型是:

1
2
3
4
5
6
7
8
9
10
11
12
13
public function forward($route,$exit=true)
{
if(strpos($route,'/')===false)
$this->run($route);
else
{
if($route[0]!=='/' && ($module=$this->getModule())!==null)
$route=$module->getId().'/'.$route;
Yii::app()->runController($route);
}
if($exit)
Yii::app()->end();
}

forward和redirect的区别显而易见

1,浏览器url地址

2,是否支持绝对地址

3,是否传递参数

关于Yii 隐藏index.php的方法

配置环境是nginx+ubuntu+php

ubuntu的配置和php的配置这里不再讨论,讨论一下nginx的配置和yii的配置

nginx的配置

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
server {
listen 80; ## listen for ipv4; this line is default and implied
root /home/davidzhang/local.ubuntu.new.star.vlinkage.com;
index index.html index.htm index.php;

# Make site accessible from http://localhost/
server_name local.ubuntu.new.star.vlinkage.com;

#include yii.conf;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.html
#try_files $uri $uri/ /index.html;
try_files $uri $uri/ /index.php?$args;
# Uncomment to enable naxsi on this location
# include /etc/nginx/naxsi.rules
index index.php;
}

location /doc/ {
alias /usr/share/doc/;
autoindex on;
allow 127.0.0.1;
deny all;
}
}

这里的配置主要是这里

1
2
3
4
5
6
7
8
9
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.html
#try_files $uri $uri/ /index.html;
try_files $uri $uri/ /index.php?$args;
# Uncomment to enable naxsi on this location
# include /etc/nginx/naxsi.rules
index index.php;
}

当然也可以使用下面的方式

1
2
3
4
5
6
location / {
if (!-e $request_filename)
{
rewrite ^(.*)$ /index.php last;
}
}

接下来是yii的配置

1
2
3
4
5
6
7
8
9
'urlManager'=>array(
'urlFormat'=>'path',
'showScriptName'=>false,
'rules'=>array(
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
),

这里面关键的是这里

1
'showScriptName'=>false,

不然index.php还是会有的,在自己的项目中请注意这里

$criteria = new CDbCriteria;

addCondition() 方法

1
public CDbCriteria addCondition(mixed $condition, string $operator='AND')
1
2
3
$criteria->addCondition("id = :id");
$criteria->addCondition('id=1','OR');
$criteria->params[':id']=1;

addInCondition() 方法

1
public CDbCriteria addInCondition(string $column, array $values, string $operator='AND')
1
$criteria->addInCondition('id', array(1, 2, 3));

addNotInCondition() 方法

1
public CDbCriteria addNotInCondition(string $column, array $values, string $operator='AND')
1
$criteria->addNotInCondition(‘id’, array(1, 2, 3));

addSearchCondition() 方法

1
public CDbCriteria addSearchCondition(string $column, string $keyword, boolean $escape=true, string $operator='AND', string $like='LIKE')
1
$criteria->addSearchCondition('name','分类');

compare() 方法

1
public CDbCriteria compare(string $column, mixed $value, boolean $partialMatch=false, string $operator='AND', boolean $escape=true)
1
$criteria->compare('id,1);

addBetweenCondition()

1
public CDbCriteria addBetweenCondition(string $column, string $valueStart, string $valueEnd, string $operator='AND')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$criteria->addBetweenCondition('id', 1, 4);

$criteria->select = 'id,parentid,name';//field default='*';

$criteria->join = 'xxx'; //连接表

$criteria->with = 'xxx';//调用relations

$criteria->limit =10; //取1条数据,如果小于0,则不作处理

$criteria->offset =1; //两条合并起来,则表示 limit 10 offset1,或者代表了。limit 1,10

$criteria->order = 'xxx DESC,XXX ASC' ;//排序条件

$criteria->group = 'group 条件';

$criteria->having = 'having 条件 ';

$criteria->distinct = FALSE;//是否唯一查询

最近在使用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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def upload_large_image_file(file, type='man'):
'''文件上传函数,上传的文件夹一定要存在'''
if file:
size = 480
#先移动文件,然后截取指定大小尺寸的文件
parser = ImageFile.Parser()
for content in file.chunks():
parser.feed(content)
img = parser.close()

width, height = img.size
if width > size:
delta = width / size
height = int(height / delta)
img.thumbnail((size, height), Image.ANTIALIAS)


name, ext = os.path.splitext(os.path.basename(file.name))
name = uuid.uuid4().hex
file_name = os.path.join('upload/'+type+'/large',name+'_large'+ext)
path_name = os.path.join(MEDIA_ROOT, file_name)
img.save(path_name)

return(file_name)
return(file_name)


def upload_middle_image_file(file, type='man'):
'''文件上传函数,上传的文件夹一定要存在'''
if file:
size = 320
#先移动文件,然后截取指定大小尺寸的文件
parser = ImageFile.Parser()
for content in file.chunks():
parser.feed(content)
img = parser.close()

width, height = img.size
if width > size:
delta = width / size
height = int(height / delta)
img.thumbnail((size, height), Image.ANTIALIAS)


name, ext = os.path.splitext(os.path.basename(file.name))
name = uuid.uuid4().hex
file_name = os.path.join('upload/'+type+'/middle',name+'_middle'+ext)
path_name = os.path.join(MEDIA_ROOT, file_name)
img.save(path_name)

return(file_name)
return(file_name)


def upload_small_image_file(file,type='man'):
'''文件上传函数,上传的文件夹一定要存在'''
if file:
size = 100
#先移动文件,然后截取指定大小尺寸的文件
parser = ImageFile.Parser()
for content in file.chunks():
parser.feed(content)
img = parser.close()

width, height = img.size
if width > size:
delta = width / size
height = int(height / delta)
img.thumbnail((size, height), Image.ANTIALIAS)


name, ext = os.path.splitext(os.path.basename(file.name))
name = uuid.uuid4().hex
file_name = os.path.join('upload/'+type+'/small',name+'_small'+ext)
path_name = os.path.join(MEDIA_ROOT, file_name)
img.save(path_name)

return(file_name)
return(file_name)

django 执行 uwsgi进行自动删除进程id并进行重启 防止多个进行 导致服务器挂掉

最近使用django+uwsgi+虚拟机(ubuntu),每次进行uwsgi重启的时候都会增加一个进程的,导致自己的mysql无意间挂掉了,莫名其妙,结果才发现,有大量的uwsgi的进程,但是每次启动的时候都去删除,手动肯定很麻烦,于是自己就google后,写了一个自己的脚本。

脚本如下,仅供参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh
NAME="walkerfree"
if [ ! -n "$NAME" ];then
echo "no arguments"
exit;
fi

echo $NAME
ID=`ps -ef | grep "$NAME" | grep -v "$0" | grep -v "grep" | awk '{print $2}'`
echo $ID
echo "################################################"
for id in $ID
do
kill -9 $id
echo "kill $id"
done
echo "################################################"
uwsgi --ini /usr/local/etc/uwsgi/walkerfree-uwsgi.ini

Create thumbnails for an ImageField in django,在django中创建缩略图的过程其实有两种方法,第一种方法是比较传统的就是直接使用写入的方法,第二种方式就是使用PIL,我这里针对自己项目的需要,需要实现直接上传直接切图的功能。

功能实现:

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def upload_large_image_file(file, type='man'):
'''文件上传函数,上传的文件夹一定要存在'''
if file:
size = 480
#先移动文件,然后截取指定大小尺寸的文件
parser = ImageFile.Parser()
for content in file.chunks():
parser.feed(content)
img = parser.close()

width, height = img.size
if width > size:
delta = width / size
height = int(height / delta)
img.thumbnail((size, height), Image.ANTIALIAS)


name, ext = os.path.splitext(os.path.basename(file.name))
file_name = os.path.join('upload/'+type+'/large',name+'_large'+ext)
path_name = os.path.join(MEDIA_ROOT, file_name)
img.save(path_name)

return(file_name)
return(file_name)


def upload_middle_image_file(file, type='man'):
'''文件上传函数,上传的文件夹一定要存在'''
if file:
size = 320
#先移动文件,然后截取指定大小尺寸的文件
parser = ImageFile.Parser()
for content in file.chunks():
parser.feed(content)
img = parser.close()

width, height = img.size
if width > size:
delta = width / size
height = int(height / delta)
img.thumbnail((size, height), Image.ANTIALIAS)


name, ext = os.path.splitext(os.path.basename(file.name))
file_name = os.path.join('upload/'+type+'/middle',name+'_middle'+ext)
path_name = os.path.join(MEDIA_ROOT, file_name)
img.save(path_name)

return(file_name)
return(file_name)


def upload_small_image_file(file,type='man'):
'''文件上传函数,上传的文件夹一定要存在'''
if file:
size = 100
#先移动文件,然后截取指定大小尺寸的文件
parser = ImageFile.Parser()
for content in file.chunks():
parser.feed(content)
img = parser.close()

width, height = img.size
if width > size:
delta = width / size
height = int(height / delta)
img.thumbnail((size, height), Image.ANTIALIAS)


name, ext = os.path.splitext(os.path.basename(file.name))
file_name = os.path.join('upload/'+type+'/small',name+'_small'+ext)
path_name = os.path.join(MEDIA_ROOT, file_name)
img.save(path_name)

return(file_name)
return(file_name)

因为是要传递三种不同尺寸的图片,并且图片的也不同,一个大的一个中等的一个小的。

创建的form是这样的 如下:

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class ManCreateForm(forms.Form):
GENDER = (
(0, u'展示'),
(1, u'不展示'),
)

title = forms.CharField(max_length=255, label='标题',widget=forms.TextInput(attrs={'placeholder':'标题','class':'input-block-level span8'}))
url_address = forms.CharField(label='详情地址', widget=forms.TextInput(attrs={'placeholder':'详情地址','class':'input-block-level span8'}))
description = forms.CharField(label='详情简介',widget=forms.Textarea(attrs={'placeholder':'详情简介','class':'input-block-level span8'}))
large_image = forms.ImageField(label='大图',widget=forms.ClearableFileInput(attrs={'placeholder':'大图','class':'input-block-level span8'}))
middle_image = forms.ImageField(label='中图',widget=forms.ClearableFileInput(attrs={'placeholder':'中图','class':'input-block-level span8'}))
small_image = forms.ImageField(label='小图',widget=forms.ClearableFileInput(attrs={'placeholder':'小图','class':'input-block-level span8'}))
show = forms.ChoiceField(label='是否展示',widget=forms.Select,choices=GENDER)

def clean_title(self):
title = self.cleaned_data['title']
if title is None:
raise forms.ValidationError("标题不能为空")

return title

def clean_url_address(self):
url = self.cleaned_data['url_address']
validate = URLValidator()
try:
validate(url)
except ValidationError:
raise forms.ValidationError("详情地址必须是标准的url地址")

return url



class WomanCreateForm(forms.Form):
GENDER = (
(0, u'展示'),
(1, u'不展示'),
)
title = forms.CharField(max_length=255, label='标题',widget=forms.TextInput(attrs={'placeholder':'标题','class':'input-block-level span8'}))
description = forms.CharField(label='详情简介',widget=forms.Textarea(attrs={'placeholder':'详情简介','class':'input-block-level span8'}))
url_address = forms.CharField(label='详情地址',widget=forms.TextInput(attrs={'placeholder':'详情地址','class':'input-block-level span8'}))
large_image = forms.ImageField(label='大图',widget=forms.ClearableFileInput(attrs={'placeholder':'大图','class':'input-block-level span8'}))
middle_image = forms.ImageField(label='中图',widget=forms.ClearableFileInput(attrs={'placeholder':'中图','class':'input-block-level span8'}))
small_image = forms.ImageField(label='小图',widget=forms.ClearableFileInput(attrs={'placeholder':'小图','class':'input-block-level span8'}))
show = forms.ChoiceField(label='是否展示',widget=forms.Select,choices=GENDER)

def clean_title(self):
title = self.cleaned_data['title']
if title is None:
raise forms.ValidationError("标题不能为空")

return title

def clean_url_address(self):
url = self.cleaned_data['url_address']
validate = URLValidator()
try:
validate(url)
except ValidationError:
raise forms.ValidationError("详情地址必须是标准的url地址")

return url

class GiftsCreateForm(forms.Form):
GENDER = (
(0, u'展示'),
(1, u'不展示'),
)
title = forms.CharField(max_length=255, label='标题',widget=forms.TextInput(attrs={'placeholder':'标题','class':'input-block-level span8'}))
description = forms.CharField(label='详情简介',widget=forms.Textarea(attrs={'placeholder':'详情简介','class':'input-block-level span8'}))
url_address = forms.CharField(label='详情地址',widget=forms.TextInput(attrs={'placeholder':'详情地址','class':'input-block-level span8'}))
large_image = forms.ImageField(label='大图',widget=forms.ClearableFileInput(attrs={'placeholder':'大图','class':'input-block-level span8'}))
middle_image = forms.ImageField(label='中图',widget=forms.ClearableFileInput(attrs={'placeholder':'中图','class':'input-block-level span8'}))
small_image = forms.ImageField(label='小图',widget=forms.ClearableFileInput(attrs={'placeholder':'小图','class':'input-block-level span8'}))
show = forms.ChoiceField(label='是否展示',widget=forms.Select,choices=GENDER)

def clean_title(self):
title = self.cleaned_data['title']
if title is None:
raise forms.ValidationError("标题不能为空")

return title

def clean_url_address(self):
url = self.cleaned_data['url_address']
validate = URLValidator()
try:
validate(url)
except ValidationError:
raise forms.ValidationError("详情地址必须是标准的url地址")

return url

三个类里面的内容基本上是一样的,只不过我追求名称的区分,就写了三个

0%