从Google的简朴的单个搜索框,到常见的Blog评论提交表单,再到复杂的自定义数据输入接口,HTML表单一直是交互性网站的支柱。 本章介绍如何用Django对用户通过表单提交的数据进行访问、有效性检查以及其它处理。 与此同时,我们将介绍HttpRequest对象和Form对象。
从Request对象中获取数据
我们在第三章讲述View的函数时已经介绍过HttpRequest对象了,但当时并没有讲太多。 让我们回忆下:每个view函数的第一个参数是一个HttpRequest对象,就像下面这个hello()函数:
���ͳeʡ���������������˴��ѣ��ٷ�ȫ�Ϊͳeʡ�����̨,ͳeʡΪ���ʵ�ͳͳ��ʡ������������Ҷ����ѣ���������һЩ��ѹ�λ������ң������ʡ�����ͳeʡ,�Ϸ��eʡ,���ͳeʡ,���ͳeʡ,���eʡ,�����eʡ,���ͳeʡ,���ͳeʡ,��ͳeʡ,��ͳeʡ,��ݸͳeʡ,���ͳeʡ, �ɶ�ͳeʡ http://www.tongesheng.com ���ͳeʡ��--��ͳeʡ 2160
HttpRequest类是定义在django.http包的__init__.py模块中,也就是说,初始化django时,或说引入http这个模块时就会把HttpRequest初始化。
from django.http import HttpResponse
def hello(request):
return HttpResponse("Hello world")
HttpRequest对象,比如上面代码里的request变量,会有一些有趣的、你必须让自己熟悉的属性和方法,以便知道能拿它们来做些什么。 在view函数的执行过程中,你可以用这些属性来获取当前request的一些信息(比如,你正在加载这个页面的用户是谁,或者用的是什么浏览器)。
URL相关信息
在view函数里,要始终用这个属性或方法来得到URL,而不要手动输入。 这会使得代码更加灵活,以便在其它地方重用。 下面是一个简单的例子:
һ�����������tp://www.17hezu.com���������������˺����ѡ�����ѱ��� �Ӫҵ������˫�˫IP ������⣨�ɷ��˵վwap��վ�ȣ����˫�˫IP ����ܽ�������VIP�����ܣ��ɷ��˵վ wap ��վ�ȣ�����BGP�·����������ǿ3.6 4G���siӲ� sataӲ��ķ����� �������������������Q:86361102.���ͷ�QQ�� 61769162 61769163 61769164 61769165 ����ϵ:13811510185(�ʱΪ����)һ�����������
看一下全部的返回值:def current_url_view_good(request): return HttpResponse("Welcome to the page at %s, %s, %s, %s " % (request.path,request.get_host(),request.get_full_path(),request.is_secure()))
# BAD!
def current_url_view_bad(request):
return HttpResponse("Welcome to the page at /current/")
# GOOD
def current_url_view_good(request):
return HttpResponse("Welcome to the page at %s" % request.path)
有关request的其它信息
request.META 是一个Python字典,包含了所有本次HTTP请求的Header信息,比如用户IP地址和用户Agent(通常是浏览器的名称和版本号)。 注意,Header信息的完整列表取决于用户所发送的Header信息和服务器端设置的Header信息。 这个字典中几个常见的键值有:
HTTP_REFERER,进站前链接网页,如果有的话。 (请注意,它是REFERRER的笔误。)HTTP_USER_AGENT,用户浏览器的user-agent字符串,如果有的话。 例如:"Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17".REMOTE_ADDR客户端IP,如:"12.345.67.89"。(如果申请是经过代理服务器的话,那么它可能是以逗号分割的多个IP地址,如:"12.345.67.89,23.456.78.90"。)
HTTP_REFERER – The referring URL, if any. (Note the misspelling of REFERER.) 原文照翻
注意,因为 request.META 是一个普通的Python字典,因此当你试图访问一个不存在的键时,会触发一个KeyError异常。 (HTTP header信息是由用户的浏览器所提交的、不应该给予信任的“额外”数据,因此你总是应该好好设计你的应用以便当一个特定的Header数据不存在时,给出一个优雅的回应。)你应该用 try/except 语句,或者用Python字典的 get() 方法来处理这些“可能不存在的键”:
# BAD!
def ua_display_bad(request):
ua = request.META['HTTP_USER_AGENT'] # Might raise KeyError!
return HttpResponse("Your browser is %s" % ua)
# GOOD (VERSION 1)
def ua_display_good1(request):
try:
ua = request.META['HTTP_USER_AGENT']
except KeyError:
ua = 'unknown'
return HttpResponse("Your browser is %s" % ua)
# GOOD (VERSION 2)
def ua_display_good2(request):
ua = request.META.get('HTTP_USER_AGENT', 'unknown')
return HttpResponse("Your browser is %s" % ua)
备注一下:dict.get(key,default=None) 对字典dict 中的键key,返回它对应的值value,如果字典中不存在此键,则返回default 的值(注意,参数default 的默认值为None)
我们鼓励你动手写一个简单的view函数来显示 request.META 的所有数据,这样你就知道里面有什么了。 这个view函数可能是这样的:
def display_meta(request):
values = request.META.items()
values.sort()
html = []
for k, v in values:
html.append('<tr><td>%s</td><td>%s</td></tr>' % (k, v))
return HttpResponse('<table>%s</table>' % '\n'.join(html))
这块内容用模板系统来实现,我自己做了一个,但没成功,我把模块做成一个文件,然后在模板中来for迭代想生成表格,用的是"for value in values",内容用的value.keys和values.values,没有成功呢,有做成功的朋友吗?交流下代码。
楼上的同学,建议你对value再进行一次遍历即可,代码如下(只需对模板文件进行修改即可): <table id=showmeta > {% for value in values%} <tr> {% for v in value%} <td >{{ v }}</td> {% endfor %} </tr> {% endfor %} </table>
貌似这样就可以了,是字符集的问题 values = request.META.items() values.sort() html = [] for k, v in values: html.append(u'<tr><td>%s</td><td>%s</td></tr>' % (k, str(v).decode('gbk'))) return HttpResponse('<table>%s</table>' % '\n'.join(html))
你可以用用 for key in request.GET 获取所有的键-->你可以用用 for key in request.GET.keys() 获取所有的键 MS应该是这样的
{% extends 'base.html' %} {% block content %} <table> {% for k,v in values %} <tr><td>{{k}}</td><td>{{v}}</td></tr> {% empty %} <tr><td>..</tr></td> {% endfor %} </table> {% endblock %} 然后views这边是 def display_meta(request): values = request.META.items() return render_to_response("metaapp/meta.html" , locals())
views 可以这么写def display(request): values=request.META.items() values.sort() t=get_template('meta.html') html=t.render(Context({'values':values})) return HttpResponse(html) 模板:<table> {% for k,v in values %} <tr><td>{{k}}</td><td>{{v}}</td></tr> {% endfor %} </table>
要使用render_to_response别忘了传递字典,我这个是可以的: 函数: def display_meta(request): values = request.META.items() values.sort() return render_to_response('meta.html',{'values':values}) 模板: {% extends 'base.html' %} {% block content %} <table> {% for key,value in values %} <tr> <td>{{ key }}</td> <td>{{ value }}</td> </tr> {% empty %} <tr><td>NoData!</td><td>CheckIt!</td></tr> {% endfor %} </table> {% endblock %}
Qi 说的就行了。render_to_response时候在values压入字典中传过去。 如: def display_meta(request): return render_to_response('hello.html', { 'values': request.META.items()}); 这样子,模板里面就能使用values变量了。如:{% for k, v in values %}
我是直接用Template : values = request.META.items() values.sort() html = [] for k, v in values: html.append('<tr><td>%s</td><td>%s</td></tr>' % (k, v)) return HttpResponse('<table>%s</table>' % '\n'.join(html))
上面的发错成原文了,下面这个是用Template的 : t = Template('<table><td> {{ index }} </td><td> {{ value }} </td></table>') html = "" for index, value in request.META.items() : html = html + t.render(Context(locals())) return HttpResponse(html)
<table> {% for k, v in values %} <tr><td>{{k}}</td><td>{{v}}</td></tr> {% endfor %} </table>
<html> <body> <table> {%for k,v in values %} <tr><td>{{ k }}</td><td> {{ v }} </td></tr> {%endfor%} </table> </body> </html> 直接这样! views.py def display_meta(request): values=request.META.items() values.sort() t=get_template('meta.html') html=t.render(Context({'values':values})) return HttpResponse(html)
view.py::: def display_meta(request): values = request.META.items() values.sort() return render_to_response('meta.html',locals()) meta.html::: <html> <body> <table> {% for k,v in values %} <tr> <td> {{ k }} </td> <td> {{ v }} </td> </tr> {% endfor %} </table> </body> </html>
来个简洁版的: views: from django.shortcuts import render_to_response def model_meta(request): values = request.META.items() values.sort() return render_to_response('meta.html', {'all_meta': values}) 模板: <table> {% for k in all_meta %} <tr><td>{{ k.0 }}</td><td> {{ k.1 }}</td></tr> {% endfor %} </table>
测试了代码,其中values.sort()错误,取消正常 错误: AttributeError: 'dict_items' object has no attribute 'sort'
实例代码中:return HttpResponse('<table>%s</table> % '\n'.join(html)。我感觉,'\n'在显示效果上是多余的,真的是多余的,我知道join()的函数用法。大家有没有觉得,如果用''.join(),可以实现相同的功能?我的邮箱啊,yan95207396@126.com,哎,这么强大的评论系统,网站作者好像没有好好利用啊!!!!!
jadesoul 2010-12-30 18:04:25 貌似这样就可以了,是字符集的问题 values = request.META.items() values.sort() html = [] for k, v in values: html.append(u'<tr><td>%s</td><td>%s</td></tr>' % (k, str(v).decode('gbk'))) return HttpResponse('<table>%s</table>' % '\n'.join(html)) 的确可以。
for v in values: c=Context({'v':v}) html.append(t.render(c)) 将每个 tr 标签内的两块内容存为数组 模板中: 1) <tr> {% for i in v %} <td>{{v.0}}</td> <td>{{v.1}}</td> </tr> 2) <tr> {% for i in v %} <td>{{i}}</td> {% endfor %} </tr> 都可以解决
上个代码有点小问题,这个测试都可以 1.for v in values: html.append(v) return render_to_response('display_meta.htm',{'v':html}) <html><body><table> {% for i in v %} <tr> <td>{{i.0}}</td> <td>{{i.1}}</td> </tr> {% endfor %} <table></body></html> 2. t=get_template('display_meta.htm') for v in values: c=Context({'v':v}) html.append(t.render(c)) return HttpResponse('<table>%s</table>' % '\n'.join(html)) <!--<tr> {% for i in v %} <td>{{v.0}}</td> <td>{{v.1}}</td> {% endfor %}</tr> --> <tr> {% for i in v %} <td>{{i}}</td> {% endfor %} </tr>
报'dict_items' object has no attribute 'sort'错误的,字典是没办法sort的,转成list后再sort,list(request.META.items())
我的是下面,照搬转换,OK def display_meta2(request): values = request.META.items() values.sort() m1m2=values return render_to_response('request_test.html', {'m1m2':m1m2}) <html> <table> {% for m1,m2 in m1m2 %} <tr><td>{{m1}}</td><td>{{m2}}</td></tr> {% endfor %} </table> </html>
我会报错:Exception Value: 'utf8' codec can't decode byte 0xc5 in position 29: invalid continuation byte
原样copy进views.py里面,再在urls.py里面import一下该方法,配置一下路由表,然后就可以了from APPNAME.views import display_meta///// url(r'^meta/$', display_meta),
views: return HttpResponse('a.html',locals()) a.html: <ul> {% for k, v in values %} <li>this is key :{{ k }} <br> this is value:{{v}}</li> {% endfor %} </ul>
如果遇到ascii decode错误可以在Python安装目录lib/mimetype.py文件import下以下代码 if sys.getdefaultencoding() != 'gbk': reload(sys) sys.setdefaultencoding('gbk')
这样改吧 return render_to_response('display_meta.html', {'item_list': values}) <table> {% for key, value in item_list %} <tr><td>{{ key }}</td><td>{{ value }}</td></tr> {% endfor %} </table>
@李辉的可行,补充一下。主要修改两个文件,第一步:在之前的temblates文件夹(~/djcode/mysite/mysite/templates)中添加一个文件"display_meta.html",内容如下: <table> {% for k, v in values %} <tr> <td>{{k}}</td> <td>{{v}}</td> </tr> {% endfor %} </table> 第二步:修改books文件夹下的views.py文件,先import一个函数from django.shortcuts import render_to_response,然后将示例中display_meta函数中的4-6行注释掉,添加return render_to_response('display_meta.html', {'values': values})。
注释掉4-7行,最后books/views.py文件中的内容如下: from django.shortcuts import render_to_response # Create your views here. def display_meta(request): values = request.META.items() values.sort() return render_to_response('display_meta.html', {'values': values})
python3实现,view.py文件内容: def display_meta(request): values = request.META.items() values = sorted(values, key=lambda x: x[0]) return render_to_response('display_meta.html', {'values': values}) templates下创建display_meta.html,内容为<table> {% for k, v in values %} <tr> <td>{{k}}</td> <td>{{v}}</td> </tr> {% endfor %} </table> url.pyt添加:url(r'^hello$', view.display_meta),
def display_meta(request): values = request.META.items() return render(request, 'display_meta.html', {'values': values}) =================== {% block content %} {% for k,v in values %} <ul> <li>{{ k }}</li> <li>{{ v }}</li> </ul> {% endfor %} {% endblock %} ============== url(r'^meta$', display_meta), 在这里可以从你的views里面import *这样就不会找不到函数了
python3 + django 2.2.2版本代码示例: views.py: def display_meta(request): values = request.META.items() values = sorted(values, key=lambda x: x[0]) return render_to_response('display_meta.html', {'meta': values}) templates: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>request meta</title> </head> <body> <table border="1"> <tr> <th>request</th> <th>info</th> </tr> {% for k, v in meta %} <tr> <td>{{ k }}</td> <td>{{ v }}</td> </tr> {% endfor %} </table> </body> </html> url.py: path('meta/', views.display_meta),
怎么包含了全部的环境变量?安全不? {% for k,v in request.META.items %} <tr> <td>{{k}}</td> <td>{{v}}</td> </tr>...
做为一个练习,看你自己能不能把上面这个view函数改用Django模板系统来实现,而不是上面这样来手动输入HTML代码。 也可以试着把前面提到的 request.path 方法或 HttpRequest 对象的其它方法加进去。
对于出现TemplateDoesNoteExist错误的同学,请参考SO上的这个回答(关于TEMPLATE_LOADERS如何工作): http://stackoverflow.com/questions/12837440/template-loading-locations
坑爹的中文路径CLASSPATH,导致编码报错,改成对字符串decode('gbk')问题得以解决 def display_meta1(request): values = request.META.items() values.sort() html = [] for k, v in values: html.append('<tr><td>%s</td><td>%s</td></tr>' % (k,v.decode('gbk') if type(v)==types.StringType else v)) return HttpResponse('<table>%s</table>' % '\n'.join(html))
import sys reload(sys) sys.setdefaultencoding('gbk') in setting.py
{% extends "base.html" %} {% block display_meta %} <table> {% for k,v in values %} <tr> <td>{{ k }}</td> <td>{{ v }}</td> </tr> {% empty %} <tr> <td>NoData!</td> <td>CheckIt!</td> </tr> {% endfor %} </table> {% endblock %} 使用了base.html,需要在其中添加block displaymeta {% block display_meta %}{% endblock %}
{% extends "dateapp/base.html" %} {% block content %} <table> {% for k,v in values %} <tr> <td>{{ k }}</td> <td>{{ v }}</td> </tr> {% empty %} <tr> <td>NoData!</td> <td>CheckIt!</td> </tr> {% endfor %} </table> {% endblock %}
def display_meta(request): meta = request.META.items() meta.sort() return render_to_response('request.html',{'meta_line':meta})
Django1.11 python3: 在books/views.py from django.shortcuts import render_to_response def display_meta(request): values = request.META.items() values = sorted(values, key=lambda x: x[0]) return render_to_response('display_meta.html', {'values': values}) 在mysite/urls.py添加两行: from books.views import display_meta url(r'^display/$', display_meta), #display链接名字是自己起的 在mysite/templates/下确保有display_meta.html <table> {% for k, v in values %} <tr> <td>{{k}}</td> <td>{{v}}</td> </tr> {% endfor %} </table> 然后运行python manage.py runserver 打开响应链接+/display.html 即可显示
比较简单的一个方法 def display_meta(request): values = request.META.items() return render(request,'request.html',{'list':values}) {% for key,value in list %} {{ key }}======={{ value }}<br> {% endfor %}
提交的数据信息
除了基本的元数据,HttpRequest对象还有两个属性包含了用户所提交的信息: request.GET 和 request.POST。二者都是类字典对象,你可以通过它们来访问GET和POST数据。
类字典对象
我们说“request.GET和request.POST是类字典对象”,意思是他们的行为像Python里标准的字典对象,但在技术底层上他们不是标准字典对象。 比如说,request.GET和request.POST都有get()、keys()和values()方法,你可以用用 for key in request.GET 获取所有的键。
那到底有什么区别呢? 因为request.GET和request.POST拥有一些普通的字典对象所没有的方法。 我们会稍后讲到。
你可能以前遇到过相似的名字:类文件对象,这些Python对象有一些基本的方法,如read(),用来做真正的Python文件对象的代用品。
POST数据是来自HTML中的〈form〉标签提交的,而GET数据可能来自〈form〉提交也可能是URL中的查询字符串(the query string)。
一般在浏览器中输入网址访问资源都是通过GET方式;在FORM提交中,可以通过Method指定提交方式为GET或者POST,默认为GET提交 Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE URL全称是资源描述符,我们可以这样认 为:一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查 ,改 ,增 ,删 4个操作。到这里,大家应该有个大概的了解了,GET一般用于获取/查询 资源信息,而POST一般用于更新 资源信息(个人认为这是GET和POST的本质区别,也是协议设计者的本意,其它区别都是具体表现形式的差异 )。
一个简单的表单处理示例
继续本书一直进行的关于书籍、作者、出版社的例子,我们现在来创建一个简单的view函数以便让用户可以通过书名从数据库中查找书籍。
通常,表单开发分为两个部分: 前端HTML页面用户接口和后台view函数对所提交数据的处理过程。 第一部分很简单;现在我们来建立个view来显示一个搜索表单:
from django.shortcuts import render_to_response
def search_form(request):
return render_to_response('search_form.html')
����������ͨ����������������ʱ�վ�����Ķ������Ͻ���������Ļ �߿����13�߿�¼ȡ��-�߿�����2013�߿�����,��ǰ���߷�������а�������-�����- 2013���� �������-���� -2013�������-�������-ȫ���� -2013������� 2013ȫ���� -��������� -�߿�ѧϰ� - 2013�������ǿ -2013�����������-���ҵ�� -������ -���� -����ѧУ -�߿���� -������-�߿����ʱ��-������ -����ϰ - 2013�߿����� -2013���а�� -��� -������ -�����߿�� -����߿�� -�㶫�߿�� -ɽ���߿��- ����߿�� -����߿�� -�����߿�� -����߿�� -�����-���߿�� -����߿�� -�����߿�� -���߿��- ���ո߿�� -���߿��- ����߿�� -����߿�� - ���߿��-����߿��-2013�߿���ϰȫ���.�����������������073178.com����������en.073178.com����������gyu.073178.com����������xiehouyu.073178.com ����������hua.073178.com����������������������������ȵ��ϵ���� �ַ ��www.073178.com//�������/������� 8621
英文原版中使用的是render,我用的django 1.4,在第4章中按照实例使用render_to_response执行正确,但这个地方始终提示template does not exist,改成render后才运行成功。实在没有弄明白怎么回事。 render和render_to_response的区别是什么?译版何必画蛇添足的将render改为render_to_response呢?
这个地方要在setting.py中配置TEMPLATE_DIRS 例如: TEMPLATE_DIRS = ( 'D:/Python27/Scripts/newtest/newtest/books', )
TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'), u'f:/djangosite/books/templates', 一次性成功,哈哈。
from django.shortcuts import render OR from django.shortcuts import render_to_response
在第三章已经学过,这个view函数可以放到Python的搜索路径的任何位置。 为了便于讨论,咱们将它放在 books/views.py 里。
这个 search_form.html 模板,可能看起来是这样的:
按照这个教程的流程下来,最好是在books这个APP里面新建一个文件夹,比如:templates。把search_form.html文件放到此文件夹中,最后把这个文件夹的路径添加到settings里的TEMPLATE_DIRS里面。
django1.4.3的配置(win&linux): TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'), )
我也遇到了这个问题,但稀里糊涂的解决了。是这样的:版本Django version 1.4.2 文件目录是三层的myweb/src/book/views.py. views中的内容都原样。urlpy照这个搞不对,我改成这样:from myweb import views,从最顶层引入,然后再次运行,页面左上角的search表单神奇般的出来了。希望对你有点参考意义。
这里做成功了,search_form.html可以放到放到任何已经注册的路径下。 比如放到“E:\PythonWebSite\mysite\templates”路径下,那么就在 settings.py 的TEMPLATE_DIRS中加上"E:\PythonWebSite/mysite/templates", 主要:这里的路径要用"/"不能用"\",否则会报错。 我的是python2.7,Django 1.4
这里做成功了,search_form.html可以放到放到任何已经注册的路径下。 比如放到“E:\PythonWebSite\mysite\templates”路径下,那么就在 settings.py 的TEMPLATE_DIRS中加上"E:\PythonWebSite/mysite/templates", 注意:这里的路径要用"/"不能用"\",否则会报错。 我的是python2.7,Django 1.4
可以是project 里面的templates 也可以是app里面的templates 只要Django搜到就可以,规范来说既然这个是app books的页面,还是放在books/templates 好。可以参考官网教程
<html>
<head>
<title>Search</title>
</head>
<body>
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
如果你在app中建立templates文件夹 然后在setting中的 INSTALLED——APPS = ( 。。。 'app_name', # 不要写你的项目名称 , 像'projects_name.app_name'是不好的 。。。 ) 那么系统会自动在app中的templates文件夹寻找是否有合适的模板
/search/这个地方。一拼接就变成http://127.0.0.1:8000/search/?q=xxx这个肯定是错的。。。所以。。/search/要改成/search
Django-1.11.3中,search_form.html 模板文件可以放在项目中的任何一个app的模板文件中,比如books/templates下,唯一要注意的就是各app下的模板文件名要一样,并且得是在settings.py文件中配置的'DIRS'的值。在运行时,Django会自动在这些目录下寻找相应的模板文件。
而 urls.py 中的 URLpattern 可能是这样的:
from mysite.books import views
urlpatterns = patterns('',
# ...
(r'^search-form/$', views.search_form),
# ...
)
此处使用from mysite.books import views 需要取消注释下面两句: from django.contrib import admin admin.autodiscover() 即加上这两句才行,不然要出现找不到books模块
lastmayday 正解。如果文件目录是mysite/books/views.py 的,(区别于mysite/mysite/books/views.py),在这里import的时候要用ls的方式。这问题让我折腾了好久,谢谢lastmayday
路径是d:/mysite/mysite/books/views.py 用from books.views import * 可以但是from books import views就不行,提示找不到search_form
django版本为:1.4.3的配置(环境为mysite/books/views.py): 添加: from books import views 需要取消注释下面两句: from django.contrib import admin admin.autodiscover()
from mysite.views import hello,current_datetime,hours_ahead,mypage,display_meta,search_form (r'^search-form/$', search_form)
whille 的可以,为啥'books.views.search_form'需要单引号呢? 然后我是/mysite/books 用 from books import views
这里确实需要注意路径,还有一个要注意的就是 (r'^search_from/$') 这里面不需要这个“/$”,我们导入的那是个search_from.html ,后面就不能要/$,否则就会显示找不到网页的。。。
到这里我算是彻底的被 搞糊途了。 因为最开始建 出的来的文件夹就有两层mysite,而manager.py是在第一层mysite下面的,而urls.py、setting.py、是在mysite/mysite下面的,templates文件夹是在mysite/mysite/template,books是在第一层mysite下面的,好乱啊!!!!
我推荐看一下这一篇,这一篇最后说了,各个app应该和mysite独立,不应该放在mysite里面。http://site.douban.com/192043/widget/notes/11418540/note/247044376/
from books import view from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', #... (r'^search-form/$', views.search-form), #...)
from books import view<br/> from django.contrib import admin<br/> admin.autodiscover()<br/> urlpatterns = patterns('',<br/> #...<br/> (r'^search-form/$', views.search-form),<br/> #...)
from books import view\n from django.contrib import admin\n admin.autodiscover()<br/> urlpatterns = patterns('',<br/> #...<br/> (r'^search-form/$', views.search-form),<br/> #...)
from books import view\n from django.contrib import admin\n admin.autodiscover()\n urlpatterns = patterns('',\n #...\n (r'^search-form/$', views.search-form),\n #...)
到这里大家应该区分一下project与app的区别,之前的例子都是按照在project的思路做下来的,到这里突然换成app肯定会有一些混乱,其实在之前的Templates_dirs时就应该思考一下project与app的区别,看到有评论说默认为templates了,当时就不是很理解,所以深入跟进了一下这个问题,在网上搜以及查官网资料(我用的是1.6.2),然后发现评论所说的其实是针对app的默认设置,从那开始就把project下的代码切换到了app里了。所以大家先别急,耐心的把你project里的代码切换到app里,之后就顺利成章了
应该是 form mysite.books import search_form url(r'^search-form',search_form),
from books.views import hello,current_datetime,hours_ahead,display_meta,search#一路下来我都写了这么多了,留下来参考参考
url(r'^$', hello),#2 url(r'^admin/', include(admin.site.urls)),#1 url(r'^hello/$', hello),#2 url(r'^time/$', current_datetime), url(r'^time/plus/(\d{1,2})/$', hours_ahead), url(r'^meta/$', display_meta),#一个简单的view函数来显示 request.META 的所有数据 url(r'^search/$', search),
1.11版本里面是这样操作的 在books这个app里建立templates文件夹 新建search_from.html 在books里的views.py里写那个函数 使用render记得request参数一定要传,可以继承在project目录里的base.html 并不需要设置目录 写好urls就可以了
(注意,我们直接将views模块import进来了,而不是用类似 from mysite.views import search_form 这样的语句,因为前者看起来更简洁。 我们将在第8章讲述更多的关于import的用法。)
而不是用类似 rom mysite.views import search_form 这样的语句-----应该为:From mysite.views import search_form
此处使用from mysite.books import views 需要取消注释下面两句: from django.contrib import admin admin.autodiscover() 即加上这两句才行,不然要出现找不到books模块
Django Version: 1.7.2 目录结构如下: ./mysite/mysite ./mysite/books ./mysite/templates ./mysite from books.views import search_form 能显示search界面
setting中的模板路径设置:'DIRS': [os.path.join(BASE_DIR, 'app1/templates')], ## 如果模板在APP中,则需设置APP/模板文件夹名称 'DIRS': [os.path.join(BASE_DIR, 'templates')], ## 如果模板在项目中,只需设置模板文件夹名称
现在,如果你运行 runserver 命令,然后访问http://127.0.0.1:8000/search-form/,你会看到搜索界面。 非常简单。
我也遇到了这个问题,但稀里糊涂的解决了。是这样的:版本Django version 1.4.2 文件目录是三层的myweb/src/book/views.py. views中的内容都原样。urlpy照这个搞不对,我改成这样:from myweb import views,从最顶层引入,然后再次运行,页面左上角的search表单神奇般的出来了。希望对你有点参考意义。
回ping,我也遇到了这个问题,把‘search-form’改成‘’searchform'就没问题了,当然url.py里也要改,不知道是不是url中不准出现‘-’。
我点击了search按钮,浏览器地址栏变成:http://localhost:8000/search-form/?q=aaa,但是,没有任何新网页出现,也没有404网页,没有任何变化,一直停留在search-form页面,是search按钮没起作用吗? 谁能解答一下呢?谢谢
报错'module' object has no attribute 'search_form'。已崩溃,之前是可以搜索页面是可以出来的,突然不行了。而且所有写在book.views里的视图都报这个错。
出现mysite.books问题是因为导入的模块应该是boos,不应该是mysite.books。点击网址search-form进不去是正常的,因为网址中出现了-,建议把“-”换成“_”或者另起他名,当然,相应的ulrs.py中也要做相应的改变。
差一点把自己绕进去了... project里面的那个views.py 是我们自己建立的,并不是django自己生成的...而app 里面的views 则是django生成的
不过,当你通过这个form提交数据时,你会得到一个Django 404错误。 这个Form指向的URL /search/ 还没有被实现。 让我们添加第二个视图函数并设置URL:
# urls.py
urlpatterns = patterns('',
# ...
(r'^search-form/$', views.search_form),
(r'^search/$', views.search),
# ...
)
# views.py
def search(request):
if 'q' in request.GET:
message = 'You searched for: %r' % request.GET['q']
else:
message = 'You submitted an empty form.'
return HttpResponse(message)
当提交空的内容去搜索的时候,无法获得'You submitted an empty form.'这个结果.而是得到"You searched for: u''"
def search(request): if 'q' in request.GET: if request.GET['q']: message = 'You searched for: %s' % request.GET['q'] else: message = 'You submitted an empty form.' return HttpResponse(message)
需要添加from django.http import HttpResponse,%r为什么要使用它?还有输出的结果为什么会是You searched for: u'Test'?多了个'u',应该需要google大婶出马了。
1.使用if 'q' in request.GET,是防止没有通过提交表单跳转,如直接访问/search/。可以修改为if 'q' in request.GET: if request.GET['q']: message = 'You searched for: %r' % request.GET['q'] else: message = 'You submitted an empty form.' else: message = 'How did you go to this page?'
综上改良版 def search(request): if 'q' in request.GET: if request.GET['q']: message = 'You searched for: %s' % request.GET['q'] else: message = 'You submitted an empty form.' else: message = 'How did you go to this page?' return HttpResponse(message)
if 'q' in request.GET判断的好处在于即使有人恶意修改了前台表单,后台也能正确处理,而不是报错。仅就这里而言这样的做的意义不大,因为恶意修改的人得到错误也活该,反正也影响不到后台。但是后台逻辑即使在前台表单被串改仍能正常处理这个观念很重要,就像有些表单的输入检查仅在前台做一样,稍微改个表单就可以提交不合法的数据了,而好的应用会在后台“重复”做和前台一样的检查。
'q' in request.GET 的值一直是True. 因为request.GET是一个字典,它一定有一个键是'q' 这里我们要做的是判断'q'这个键的值是否为空,应该写成: if (request.GET['q']):
这里的 if 'q' in request.GET 条件是为了处理用户直接通过比如:http://127.0.0.1:8000/search/来访问,而不是通过http://127.0.0.1:8000/search-form/跳转过来访问时引发的异常。
(r'^search-form/$', views.search_form), (r'^search/$', views.search), 我這兩句放在一起爲什麽老是出來:'tuple' object is not callable的錯誤提示,必須要砍掉一個才行
if (request.GET['q']): message = 'You searched for: %s' % request.GET['q'] 可以既显示中文又可以避免'q' in request.GET一直为真的问题
@yooke说的是对的,如果搜索框里输入是空,我们可以不让其跳转,仍然停在此页面比较符合情理,或是弹出提示框也可以。 def search(request): if 'q' in request.GET: message = 'You searched for: %s'% request.GET['q'] if not request.GET['q']: return render_to_response('search_form.html') else: message = 'You submitted an empty form.' return HttpResponse(message)
写成 if request.GET['q']:这样有可能会抛出异常,因为当用户修改地址时'q'可能不存在GET请求中,因此会抛出一个nullPointerException
我使用if request.GET['q']: message = "test search for : %s" % request.GET['q'] else: message = "test submitted an empty from." 才跳转正常
def search(request): if request.GET: if request.GET['q']: message = 'You searched for: %s' % request.GET['q'] else: message = 'You submitted an empty form.' else: message = 'You submitted an empty form.'
暂时先只显示用户搜索的字词,以确定搜索数据被正确地提交给了Django,这样你就会知道搜索数据是如何在这个系统中传递的。 简而言之:
- 在HTML里我们定义了一个变量q。当提交表单时,变量q的值通过GET(method=”get”)附加在URL /search/上。
- 处理/search/(search())的视图通过request.GET来获取q的值。
需要注意的是在这里明确地判断q是否包含在request.GET中。就像上面request.META小节里面提到,对于用户提交过来的数据,甚至是正确的数据,都需要进行过滤。 在这里若没有进行检测,那么用户提交一个空的表单将引发KeyError异常:
q是上面网页里那个文本输入框的name,在提交的时候,这些会被封装起来,即q=文本内容,这样的显示在url上,那么你的文本框是空的话,url也不过是q=''这样的,如果要走else,就把url的q=改成其它的,比如qs=,这样在GET中就无法找到q的这个key了。
if request.GET['q'].strip() == '' : message = 'empty form' 再加一个判断,就可以 控制空格
# BAD!
def bad_search(request):
# The following line will raise KeyError if 'q' hasn't
# been submitted!
message = 'You searched for: %r' % request.GET['q']
return HttpResponse(message)
查询字符串参数
因为使用GET方法的数据是通过查询字符串的方式传递的(例如/search/?q=django),所以我们可以使用requet.GET来获取这些数据。 第三章介绍Django的URLconf系统时我们比较了Django的简洁的URL与PHP/Java传统的URL,我们提到将在第七章讲述如何使用传统的URL。通过刚才的介绍,我们知道在视图里可以使用request.GET来获取传统URL里的查询字符串(例如hours=3)。
获取使用POST方法的数据与GET的相似,只是使用request.POST代替了request.GET。那么,POST与GET之间有什么不同?当我们提交表单仅仅需要获取数据时就可以用GET; 而当我们提交表单时需要更改服务器数据的状态,或者说发送e-mail,或者其他不仅仅是获取并显示数据的时候就使用POST。 在这个搜索书籍的例子里,我们使用GET,因为这个查询不会更改服务器数据的状态。 (如果你有兴趣了解更多关于GET和POST的知识,可以参见http://www.w3.org/2001/tag/doc/whenToUseGet.html。)
这里 POST 和 GET 的区别冒似没说到点上。其实GET 和 POST 目的都是为了将表单数据提交给服务器,所有数据将被组织成 key1=value1&key2=value2&…… 的格式,区别在于,GET会将数据跟在目标URL后面并使用?隔开(如:http://www.baidu.com?q=abcd),POST则将数据放在HTTP协议的body部分提交,从客户端的角度来观察,GET能在URL上明显看到所有提交的数据,而POST的URL仅显示目标地址,傻瓜式抓屏木马则无法得知提交的数据内容,所以类似提交密码等敏感信息时,使用POST比GET更加合适。该使用哪种方式提交,需要根据使用场景而定。
既然已经确认用户所提交的数据是有效的,那么接下来就可以从数据库中查询这个有效的数据(同样,在views.py里操作):
from django.http import HttpResponse
from django.shortcuts import render_to_response
from mysite.books.models import Book
def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
else:
return HttpResponse('Please submit a search term.')
if 'q' in request.GET and request.GET['q']: 为什么要if 'q' in request.GET这个条件呢?前文中 <form action="/search/" method="get"> <input type="text" name="q"> <input type="submit" value="Search"> </form> 说明‘q’通过GET提交上去的,那么‘q’一定会在request.GET这个字典中的,为什么还要检查一遍呢?
if 'q' in request.GET and request.GET['q'] 为什么要 and request.GET['q']呢 前面的'q' in request.GET 已经确定了‘q'有其值啊 那么后面的也必然为true。。
if request.GET['q']判断q为空,如果已经有上交表单的动作,q肯定不为空;如果不判断,在地址栏里令q为空,将抛出一个404错误,这就浪费了../seach/这个地址。 后文有个更复杂的例子 q = request.GET['q'] if not q: ...... else: ......
'q' in request.GET 是判斷是否有'q'這個key, request.GET['q']判斷是否為' '空值,如果在搜尋框輸入'空白' submit後 if 'q' in request.GET and request.GET['q']會是false
此处的 if 'q' in request.GET and request.GET['q'] 改为 if 'q' in request.GET and request.GET['q'].strip() 是不是好一点。。否则提交一堆空格也会查询的
作者,请问下,你的Book你是怎么提交的? 我用你的代码运行,Book明明就是错误的.提示: cant't import Book.. 我只想说,要是英文版的没有这个介绍,我绝对不看这种错误太多的书,很恼火阿!!!麻烦作者你先运行下再介绍好吗
For new version of Django, here would be an issue that request.GET is a QueryDict which is not callable. Using request.GET.get('q') solved it.
关于查询存在的书却显示书不存在的问题,我在百度知道提问了http://zhidao.baidu.com/question/581802637.html,希望知道的可以帮忙解答一下
我都无语了,那些说找不到book的人,还总是说本书的翻译,要么你自己没有从头看这本书,要么你就是2,作者多次介绍说版本不同,目录不同,import当然不同啊,连我这个初学者都气愤了。。。
楼上正解啊!总埋怨啥啊!这书都出多少年了,Django都1.5.2了,怎么可能没改动,你一个程序员连import都用别人告诉你在自己加???我去,能不能动动脑子啊!不动脑子当什么程序员啊!
if 'q' in request.GET and request.GET['q']:的优先级为 if (('q' in request.GET) and (request.GET['q'])):我的的可以这样理解
此处 from mysite.books.models import Book 需要改为 from books.models import Book , 否则会报错
if 'q' in request.GET and request.GET['q']: 为什么要if 'q' in request.GET这个条件呢?前文中 <form action="/search/" method="get"> <input type="text" name="q"> <input type="submit" value="Search"> </form> 说明‘q’通过GET提交上去的,那么‘q’一定会在request.GET这个字典中的,为什么还要检查一遍呢?
如果不判断 one in request 直接 request.get['one'] 如果request请求参数列表中没有one 而不是有one没有值 则会报错,其实试下就知道了很简单
>>> title__icontains是大小写不敏感匹配。>>> if 'q' in request.GET 用来判断提交的form表单内容参数是否是合法的, if request.GET['q'] 是用来判断提交的q值是否是非空内容。
让我们来分析一下上面的代码:
除了检查q是否存在于request.GET之外,我们还检查来reuqest.GET[‘q’]的值是否为空。
我们使用Book.objects.filter(title__icontains=q)获取数据库中标题包含q的书籍。 icontains是一个查询关键字(参看第五章和附录B)。这个语句可以理解为获取标题里包含q的书籍,不区分大小写。
这是实现书籍查询的一个很简单的方法。 我们不推荐在一个包含大量产品的数据库中使用icontains查询,因为那会很慢。 (在真实的案例中,我们可以使用以某种分类的自定义查询系统。 在网上搜索“开源 全文搜索”看看是否有好的方法)
最后,我们给模板传递来books,一个包含Book对象的列表。 查询结果的显示模板search_results.html如下所示:
<p>You searched for: <strong>{{ query }}</strong></p>
{% if books %}
<p>Found {{ books|length }} book{{ books|pluralize }}.</p>
<ul>
{% for book in books %}
<li>{{ book.title }}</li>
{% endfor %}
</ul>
{% else %}
<p>No books matched your search criteria.</p>
{% endif %}
这里如果要显示作者应该怎了写?直接写book.author会显示成<django.db.models.fields.related.ManyRelatedManager object at 0x025ADA90>这个。
<p>Found {{ books|length }} book{{ books|pluralize }}.</p>——其中的“books|length”怎么理解呢?在那一章有解释吗?
length与pluralize属于模板标签,{{ books | length }}是计算books中元素个数的;{{ books | pluralize }} 如果books值不是,就返回s,你可以在后面自己添加需要返回的字符串。 模板标签页:http://djangobook.py3k.cn/appendixF/
注意这里pluralize的使用,这个过滤器在适当的时候会输出s(例如找到多本书籍)。
"book{{ books|pluralize }}" ---> "pluralize" 如果值不是 1 的话返回 's' 用于 '1 vote' vs. '2 votes' 这种场合.
就是当单词为复数时,添加一个复数eg:s(默认,可以修改)的后缀。 pluralize Returns a plural suffix if the value is not 1. By default, this suffix is 's'. Example: You have {{ num_messages }} message{{ num_messages|pluralize }}. If num_messages is 1, the output will be You have 1 message. If num_messages is 2 the output will be You have 2 messages. For words that require a suffix other than 's', you can provide an alternate suffix as a parameter to the filter. Example: You have {{ num_walruses }} walrus{{ num_walruses|pluralize:"es" }}. For words that don't pluralize by simple suffix, you can specify both a singular and plural suffix, separated by a comma. Example: You have {{ num_cherries }} cherr{{ num_cherries|pluralize:"y,ies" }}.
改进表单
同上一章一样,我们先从最为简单、有效的例子开始。 现在我们再来找出这个简单的例子中的不足,然后改进他们。
首先,search()视图对于空字符串的处理相当薄弱——仅显示一条”Please submit a search term.”的提示信息。 若用户要重新填写表单必须自行点击“后退”按钮, 这种做法既糟糕又不专业。如果在现实的案例中,我们这样子编写,那么Django的优势将荡然无存。
在检测到空字符串时更好的解决方法是重新显示表单,并在表单上面给出错误提示以便用户立刻重新填写。 最简单的实现方法既是添加else分句重新显示表单,代码如下:
from django.http import HttpResponse
from django.shortcuts import render_to_response
from mysite.books.models import Book
def search_form(request):
return render_to_response('search_form.html')
def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
else:
**return render_to_response('search_form.html', {'error': True})**
为什么我会出现这样的错误提示: Cannot resolve keyword 'title_icontains' into field. Choices are: authors, id, publication_date, publisher, title
这里的裸机有明显的错误,没有针对books进行判断,换句话说就是是否搜出book,都会返回结果页面下面那个返回到搜索页面不可能到达,更改如下: books = Book.objects.filter (title__icontains = q) if books: return render_to_response ('search_result.html', {'books': books, 'query': q}) else: return render_to_response ('search_form.html', ) else: return render_to_response ('search_form.html', )
from django.shortcuts import render_to_response,render<---- It's important.....zhong wen da buliao QAQ
django1.6 网页重定向,可以用 from django.http import HttpResponseRedirect return HttpResponseRedirect('/search-form')
(注意,将search_form()视图也包含进来以便查看)
这段代码里,我们改进来search()视图:在字符串为空时重新显示search_form.html。 并且给这个模板传递了一个变量error,记录着错误提示信息。 现在我们编辑一下search_form.html,检测变量error:
<html>
<head>
<title>Search</title>
</head>
<body>
**{% if error %}**
**<p style="color: red;">Please submit a search term.</p>**
**{% endif %}**
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
" **表示是新添加的内容 " 是吗?! 这本书不是用 RST 写的吗? **text** 在 RST 里面用来强调(主题显示), 可是在预编辑块里是无效的.
我们修改了search_form()视图所使用的模板,因为search_form()视图没有传递error变量,所以在条用search_form视图时不会显示错误信息。
通过上面的一些修改,现在程序变的好多了,但是现在出现一个问题: 是否有必要专门编写search_form()来显示表单? 按实际情况来说,当一个请求发送至/search/(未包含GET的数据)后将会显示一个空的表单(带有错误信息)。 所以,只要我们改变search()视图:当用户访问/search/并未提交任何数据时就隐藏错误信息,这样就移去search_form()视图以及对应的URLpattern。
最后一句不好理解,还是看原文:We can remove the search_form() view, along with its associated URLpattern, as long as we change search() to hide the error message when somebody visits /search/ with no GET parameters。
def search(request):
error = False
if 'q' in request.GET:
q = request.GET['q']
if not q:
error = True
else:
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
return render_to_response('search_form.html',
{'error': error})
我觉得这里error变量的初始值应该为True,不然在直接访问/search/的时候是不会提示error信息的。因为直接返回了False啊!我试过了。把第一个error = False改成error = True之后才真正达到了作者需要传达的技巧
一楼理解有误 1.初始化 error必)点须是False(用于当用户打开search_form时,没有点击submit按钮或刷新页面) 2.判断'q'是否在request.GET字典中,如果在并且'q'的值为空时,证明用户没有提交任何数据却点了submit按钮,此时应重新回到search_form页面,并且置error为True,让它显示错误信息.
@silent第一次访问就出现错误信息是不是因为你的最后一行不是{'error': error}这个字典 而是{'error': True},这段代码在第一次访问时没有q。error = False.进入search页面时是没有error的,页面不会有错误信息
漏了一段代码,我补上 if not q: error=True return render_to_response('search_form.html',{'error':error})
代码应该是这样修改会比较好: (1)在"error = True"的后面再添加一条语句"return render_to_response('search_form.html',{'error':error})" (2)最后一个return,删掉",{'error':error}"
我觉得不是q的问题,我把books=...这句移到第二个if外面,if not books这样就能在查询不到结果的时候跳转到search_form.html了。
代码正确,各位注意细节。 如果用户访问/search/并且没有带GET数据,那么他将看到一个没有错误信息的表单。(因为error=False,'q' in request.GET为假,直接执行最后一行跳转到search_form.html,且error为假,不显示提示信息); 如果用户提交了一个空表单,他将看到错误提示信息,还有表单;(因为if not q为真,error被置为真,执行最后一行跳转到search_form.html,提示出错i型哪些); 如果用户提交了一个非空值,他将看到出错结果。
这样写更好理解,只要把最后一行,放到第二个if里面就好了。 def search(request): error = False if 'q' in request.GET: q = request.GET['q'] if not q: error = True return render_to_response('search_form.html',{'error': error}) else: books = Book.objects.filter(title__icontains=q) return render_to_response('search_results.html', {'books':books, 'query': q})
代码没有问题,将第四行改成q = request.GET['q'].strip(),要不然一个空格也能搜索出结果(所有包含空格的记录都会被搜出来)。
代码没有问题 首先访问 /search/的时候request.GET字典中并没有'q',所以初始化error为false 这样search_form才不会有错误提示 当用户第一次按下表单按钮的时候 'q'出现在字典中 然后执行 if 语句里面的东西
# 如果request不含'q'这个键,error = False(默认) # 如果request含有'q'这个键,但是q=xxx 后面的xxx为空,error = True return render_to_response('search_form.html',{'error': error})
这里捋一捋逻辑,这个if是判断url中是否含有‘?q=’这个东西的,如果含有则进行下一步操作 所以这一系列操作都是在if这个分支里的,不管如何最后肯定要render一个东西。所以不存在什么少了else,至于如果有else,意思是不存在'?q=',一般性只会出现在用户手动更改url你可以给个 else: return render(request,'search_from.html')
初始化应该为false,这样第一次才不会显示错误信息。真正更改error的情况是在点了提交按钮但是q为空的情况,当q为空,就会执行最后一个return返回error为True的网页。如果q不为空,才返回查找后的网页。
感觉很乱,作者的意思应该是这样 def search(request): error = True if 'q' in request.GET and request.GET['q']: q = request.GET['q'] books = Book.objects.filter(title__contains=q) if books: return render_to_response('template/search_request.html',{'books':books,'query':q}) else: return render_to_response('template/search_form.html',{'error':error}) else: error = False return render_to_response('template/search_form.html', {'error': error})
@ Belter xin 大佬的说法正确,原文翻译的代码没有错(只要你别敲错了),再加上大佬Belter xin 所说的 去除空格,则可以筛去空格也能搜索出结果的BUG
在改进后的视图中,若用户访问/search/并且没有带有GET数据,那么他将看到一个没有错误信息的表单; 如果用户提交了一个空表单,那么它将看到错误提示信息,还有表单; 最后,若用户提交了一个非空的值,那么他将看到搜索结果。
最后,我们再稍微改进一下这个表单,去掉冗余的部分。 既然已经将两个视图与URLs合并起来,/search/视图管理着表单的显示以及结果的显示,那么在search_form.html里表单的action值就没有必要硬编码的指定URL。 原先的代码是这样:
<form action="/search/" method="get">
现在改成这样:
<form action="" method="get">
action=”“意味着表单将提交给与当前页面相同的URL。 这样修改之后,如果search()视图不指向其它页面的话,你将不必再修改action。
���ϵ�绰��13873134385רҵ���ǹ����:��˹�,,������ǹ��ͺӥ�ǹ,����ǹ,���ǹǦ���(4.5MM��.5MM)2��
一开始search()只负责渲染search_results.html,所以q需要提交到search()进行处理,后来加入了q的相关判断,search()也负责渲染search_form.html。所以另一search_form()自然就不需要。
我的理解是action=""时候,点击search提交表单时候还是原来(当前)的url,否则假如:action="/xxx/"时,提交表单时候会指向/xxx/这个页面,url会变了。
如果修改成<form action="" method="get"> 那么视图里的search_form(request)就没有必要了,这时候就必须从/search/发起,而不能在从/search-form/发起了
最后一句翻译的有点问题,原文“With this change in place, you won’t have to remember to change the action if you ever hook the search() view to another URL” 就是说,可以将action="/search/"改成action="",这样的话,当其他URL需要用到search()的时候,就不需要对search_form.html进行更改了。
如果再访问/search/的话,也是要加上参数的比如:/search/?q=q sorry。因为我的代码是: return HttpResponse('Please submit a search term.')
有點看不太懂。action=""时候,点击search提交表单时候还是原来(当前)的url,只會停留在搜尋頁面,並不會執行 def search(request): 這樣話就不能看到搜尋結果了
可能有很多我这样的新手会搞晕掉,这里的意思是原来你需要访问“search_form”去查询数据,现在可以直接访问“search”来查询数据了。因为search既可以渲染search_form,也可以渲染search_results
楼上正解,action=''之后得再127.0.0.1:8000/search访问了 在从127.0.0.1:8000/search-form访问就会出现提交内容后还是这个页面的问题,所以修改后 就直接通过search访问 views.py里的search_form 也可以删掉了 urls.py里的search_form 路径配置也可以删掉了 只通过 search 访问 但是 search_form.html需要保留
还需要修改的是: 1.注释掉urls.py中的“search_form”,只保留"search" url拦截, 2.注释掉views.py中的“search_form()"方法,保留"search()"方法。 OK,此时,search_form 和search 都走"search"的路由-也就是文中提到的action=""时会默认执行url相同的路由。 然后,search方法的逻辑就会根据是否有GET['q']数据来判断render到对应路径。
这里search页面需要从127.0.0.1:8000/search/进入,而不能从/search-form/进入,即舍弃了search-form,在urls和views中可以删去search-form相关,保留search_form.html,search_form.html和search_results.html都通过search/ 来渲染。
简单的验证
我们的搜索示例仍然相当地简单,特别从数据验证方面来讲;我们仅仅只验证搜索关键值是否为空。 然后许多HTML表单包含着比检测值是否为空更为复杂的验证。 我们都有在网站上见过类似以下的错误提示信息:
- 请输入一个有效的email地址, foo’ 并不是一个有效的e-mail地址。
- 请输入5位数的U.S 邮政编码, 123并非是一个有效的邮政编码。
- 请输入YYYY-MM-DD格式的日期。
- 请输入8位数以上并至少包含一个数字的密码。
可以使用Javascript在客户端浏览器里对数据进行验证,这些知识已超出本书范围。 要注意: 即使在客户端已经做了验证,但是服务器端仍必须再验证一次。 因为有些用户会将JavaScript关闭掉,并且还有一些怀有恶意的用户会尝试提交非法的数据来探测是否有可以攻击的机会。
请教一下,页面上做了一个提交按钮,然后用JS写了点击按钮之后页面发生的变化,但是因为提交要调用一个views里的函数,必须返回一个httpresponse,这样页面就被刷新了,JS写的功能也就一闪而过了。请高手指教。
作者说的比较明白了,js端的验证需要存在,用以保证良好的客户体验,同时对提交给服务器端的数据进行验证。同时需要服务器端的验证,防止绕过js验证对服务器端进行攻击。 实际上是js端的单纯验证已经退出了历史了。
除了在服务器端对用户提交的数据进行验证(例如在视图里验证),我们没有其他办法。 JavaScript验证可以看作是额外的功能,但不能作为唯一的验证功能。
我们来调整一下search()视图,让她能够验证搜索关键词是否小于或等于20个字符。 (为来让例子更为显著,我们假设如果关键词超过20个字符将导致查询十分缓慢)。那么该如何实现呢? 最简单的方式就是将逻辑处理直接嵌入到视图里,就像这样:
def search(request):
error = False
if 'q' in request.GET:
q = request.GET['q']
if not q:
error = True
**elif len(q) > 20:**
**error = True**
else:
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
return render_to_response('search_form.html',
{'error': error})
现在,如果尝试着提交一个超过20个字符的搜索关键词,系统不会执行搜索操作,而是显示一条错误提示信息。 但是,search_form.html里的这条提示信息是:”Please submit a search term.”,这显然是错误的, 所以我们需要更精确的提示信息:
<html>
<head>
<title>Search</title>
</head>
<body>
{% if error %}
<p style="color: red;">Please submit a search term 20 characters or shorter.</p>
{% endif %}
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
����������ͨ����������������ʱ�վ�����Ķ������Ͻ���������Ļ �߿����13�߿�¼ȡ��-�߿�����2013�߿�����,��ǰ���߷�������а�������-�����- 2013���� �������-���� -2013�������-�������-ȫ���� -2013������� 2013ȫ���� -��������� -�߿�ѧϰ� - 2013�������ǿ -2013�����������-���ҵ�� -������ -���� -����ѧУ -�߿���� -������-�߿����ʱ��-������ -����ϰ - 2013�߿����� -2013���а�� -��� -������ -�����߿�� -����߿�� -�㶫�߿�� -ɽ���߿��- ����߿�� -����߿�� -�����߿�� -����߿�� -�����-���߿�� -����߿�� -�����߿�� -���߿��- ���ո߿�� -���߿��- ����߿�� -����߿�� - ���߿��-����߿��-2013�߿���ϰȫ���.�����������������073178.com����������en.073178.com����������gyu.073178.com����������xiehouyu.073178.com ����������hua.073178.com����������������������������ȵ��ϵ���� �ַ ��www.073178.com//�������/������� 8503
但像这样修改之后仍有一些问题。 我们包含万象的提示信息很容易使人产生困惑: 提交一个空表单怎么会出现一个关于20个字符限制的提示? 所以,提示信息必须是详细的,明确的,不会产生疑议。
问题的实质在于我们只使用来一个布尔类型的变量来检测是否出错,而不是使用一个列表来记录相应的错误信息。 我们需要做如下的调整:
def search(request):
**errors = []**
if 'q' in request.GET:
q = request.GET['q']
if not q:
**errors.append('Enter a search term.')**
elif len(q) > 20:
**errors.append('Please enter at most 20 characters.')**
else:
books = Book.objects.filter(title__icontains=q)
return render_to_response('search_results.html',
{'books': books, 'query': q})
return render_to_response('search_form.html',
{**'errors': errors** })
�ᄁ���������й�ʱ�������Ч�� �ᄁ�������������������Ԥ��� .���ʱ�� �С�ܵ������������ȾͿ����������tp://shop34608622.taobao.com/ ��������������������������ȫ���������������ȫ�֧�֣�֧�������Ƹ�ͨ���ٸ�����ȫ�����������������QQ��635670440 ���ID���ñ���6431 ��ǡ� 1981
出现错误: Exception Value: The view books.views.search didn't return an HttpResponse object. 代码我都是复制粘贴过来的,实在找不到哪里出错了。 views.search如下: def search(request): def search_form(request): errors = [] if 'q' in request.GET: q = request.GET['q'] if not q: errors.append('Enter a search term.') elif len(q) > 20: errors.append('Please enter at most 20 characters.') else: books = Book.objects.filter(title__icontains=q) return render_to_response('search_results.html', {'books': books, 'query': q}) return render_to_response('search_form.html', {'errors': errors })
"not q"的语句中应该加一句"return render_to_response('search_form.html',{'errors':errors})", "elif"的后面也要加同一句话,这样不会报错
接着,我们要修改一下search_form.html模板,现在需要显示一个errors列表而不是一个布尔判断。
<html>
<head>
<title>Search</title>
</head>
<body>
**{% if errors %}**
**<ul>**
**{% for error in errors %}**
**<li>{{ error }}</li>**
**{% endfor %}**
**</ul>**
**{% endif %}**
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
<li style="color: red;">{{ error }}</li> 这样效果也有了, 用list的好处是一次可以传递多个错误信息 比如说 error=['page':'index','info':'too much xxx ','time':'2014-xx-xx'] 就像Django里面debug返回的error应该也是一个数组吧,我只是猜猜,还有,不要在意我的error数组写对了没有,我也是个新手,哈哈
楼上的请问认真看过么,去掉‘/search/’,表单就会提交给当前使用的url。而你访问表单的url就是http://ip:port/search ,所以去掉‘/search/’无影响。
action=''之后得再127.0.0.1:8000/search访问了 在从127.0.0.1:8000/search-form访问就会出现提交内容后还是这个页面的问题,所以修改后 就直接通过search访问 views.py里的search_form 也可以删掉了 urls.py里的search_form 路径配置也可以删掉了 只通过 search 访问 但是 search_form.html需要保留
编写Contact表单
虽然我们一直使用书籍搜索的示例表单,并将起改进的很完美,但是这还是相当的简陋: 只包含一个字段,q。这简单的例子,我们不需要使用Django表单库来处理。 但是复杂一点的表单就需要多方面的处理,我们现在来一下一个较为复杂的例子: 站点联系表单。
这个表单包括用户提交的反馈信息,一个可选的e-mail回信地址。 当这个表单提交并且数据通过验证后,系统将自动发送一封包含题用户提交的信息的e-mail给站点工作人员。
我们从contact_form.html模板入手:
<html>
<head>
<title>Contact us</title>
</head>
<body>
<h1>Contact us</h1>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="/contact/" method="post">
<p>Subject: <input type="text" name="subject"></p>
<p>Your e-mail (optional): <input type="text" name="email"></p>
<p>Message: <textarea name="message" rows="10" cols="50"></textarea></p>
<input type="submit" value="Submit">
</form>
</body>
</html>
出现Forbidden (403)“CSRF verification failed. Request aborted.”的情况,需要添加{% csrf_token %}到form中去,<form action="/contact/" method="post">{% csrf_token %}<p>...</p>...</form> 之后在contact()方法的views.py中添加from django.template import RequestContext并且最后一句改为return render_to_response('contact_form.html',{'errors': errors}, context_instance=RequestContext(request))即可
楼上威武! 我再来总结一下,共三点。 1.在template中添加{%csrf_token%} 2.在veiws中添加from django.template import RequestContext 3.在views中在render_to_response的最后添加context_instance=RequestContext(request)
楼上威武! 我再来总结一下,共三点。 1.在template中添加{%csrf_token%} \n 2.在veiws中添加from django.template import RequestContext \n 3.在views中在render_to_response的最后添加context_instance=RequestContext(request) \n
http://www.cnblogs.com/Rocky_/p/6140362.html django 1.10 直接使用render(),,才能正确填充token。。。。。
不用这么麻烦,加上{% csrf token %}后,在settings里面,把'django.middleware.csrf.CsrfViewMiddleware'注释掉就行了
研究了半天,这个Your email也许应该作为邮件正文的一部分发给收信邮箱,以告诉网站管理人员发送者的地址以便他们反馈,否则那个输入框只能填django里面设置的邮箱,填其他邮箱会引发553错误
我们定义了三个字段: 主题,e-mail和反馈信息。 除了e-mail字段为可选,其他两个字段都是必填项。 注意,这里我们使用method=”post”而非method=”get”,因为这个表单会有一个服务器端的操作:发送一封e-mail。 并且,我们复制了前一个模板search_form.html中错误信息显示的代码。
如果我们顺着上一节编写search()视图的思路,那么一个contact()视图代码应该像这样:
from django.core.mail import send_mail
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
def contact(request):
errors = []
if request.method == 'POST':
if not request.POST.get('subject', ''):
errors.append('Enter a subject.')
if not request.POST.get('message', ''):
errors.append('Enter a message.')
if request.POST.get('email') and '@' not in request.POST['email']:
errors.append('Enter a valid e-mail address.')
if not errors:
send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get('email', 'noreply@example.com'),
['siteowner@example.com'],
)
return HttpResponseRedirect('/contact/thanks/')
return render_to_response('contact_form.html',
{'errors': errors})
报错 CSRF token missing or incorrect settings.py 修改 MIDDLEWARE_CLASSES = () 在最后加上 一条 'django.middleware.csrf.CsrfResponseMiddleware', 模板里面 form 标签后面 贴上 {% csrf_token %}
需要应该是这样:如果提交的email为空,则使用默认的noreply@example.com发送,但如果用户填的是自己的邮箱呢,如何知道用户的邮箱服务器,地址等信息以用来发送。需求是这样吗
return render_to_response('contact_form.html',{'errors':errors},context_instance=RequestContext(request))
回一楼,我的python2.6, django1.4.1 在模板的<form>下加{% csrf_token %} 就可以解决前面的问题了。 而在setting.py里加了那句就会出错。
@keroro django1.4.1 不需要{% csrf_token %} ,不需要django.middleware.csrf.CsrfViewMiddleware 。只要在settings.py 中配置好EMAIL_HOST,EMAIL_PORT, EMAIL_HOST_USER ,EMAIL_HOST_PASSWORD ,EMAIL_USE_TLS,即可。亲测,邮件可以发送。
关于:errors 是 List结构 django报错,unhashable type: 'list', 这该怎么破? 我自己找到问题了,这里代码块结尾不应是:{'errors',errors},而应该写成['errors',errors],因为默认是字典传输形式list是可变的不能作为字典的value
使用django1.4遇到CRSF报错的,注释掉settings.py文件MIDDLEWARE_CLASSES里面的django.middleware.csrf.CsrfViewMiddleware试试。
注释settings.py中的#'django.middleware.csrf.CsrfResponseMiddleware',和#'django.middleware.csrf.CsrfViewMiddleware', 在模板中的form里加入的{% csrf_token %}也去掉 1.4.5的解决方法
if request.POST.get('email') and '@' not in request.POST['email']: 这里应该是if not request.POST.get('email') or '@' not in request.POST['email']:-----判断email输入出错的情况
需要对setting.py进行配置,如下: EMAIL_HOST= 'smtp.126.com' EMAIL_PORT= 25 EMAIL_HOST_USER = '@126.com'#你的邮箱 EMAIL_HOST_PASSWORD = '' #你邮箱的密码 #我试过用不存在的邮箱,不行 EMAIL_USE_TLS = True SERVER_EMAIL = EMAIL_HOST_USER DEFAULT_FROM_EMAIL = EMAIL_HOST_USER 在调用send_mail()时,用全参数,默认是用你setting.py中配置的主机邮箱发送邮件。
修改send_mail(cd['subject'], cd['message'], '你配置发送的邮箱@xxx.com', [cd.get('email', '如果页面没有提交接收有限就会默认发到此邮箱@xxx.com')],)
修改send_mail(cd['subject'], cd['message'], '你配置的发送邮箱@xxx.com', [cd.get('email', '如果页面没有提交邮箱就会默认发到此邮箱@xxx.com')],)
浏览器默认是发送GET请求,大家怎样用火狐浏览器发送POST请求来测试这段代码的?用的插件吗?我知道Poster可以发送POST请求,但是返回的只有是一个消息体内容,并不是网页形式,
有没有人出现这个错误的:A server error occurred. Please contact the administrator.而且没法重定向到contact/thanks啊
CSRF token missing or incorrect -- 1 在 templete 中, 为每个 POST form 增加一个 {% csrf_token %} tag. 如下: <form> {% csrf_token %} </form> 2 在 view 中, 使用 django.template.RequestContext 而不是 Context. render_to_response, 默认使用 Context. 需要改成 RequestContext. 导入 class: from django.template import RequestContext 给 render_to_response 增加一个参数: def your_view(request): ... return render_to_response('template.html', your_data, context_instance=RequestContext(request) )
我觉得这行不对啊,request.POST.get('email', 'noreply@example.com'), 应该直接写死用系统设置邮箱,而把email取值放到邮件正文?反正这个业务理解不了,让用户随便填的话会报错,非法用户。
equest.POST.get('email') 发送人地址,一般是需要的,一般需要和smtp验证地址一样。否则smtp服务器报错。电子邮件本来的设计应该是smtp验证和发送人不关联。
wdmyong 2013-06-29 17:07:55 需要对setting.py进行配置,如下: EMAIL_HOST= 'smtp.126.com' EMAIL_PORT= 25 EMAIL_HOST_USER = '@126.com'#你的邮箱 EMAIL_HOST_PASSWORD = '' #你邮箱的密码 #我试过用不存在的邮箱,不行 EMAIL_USE_TLS = True SERVER_EMAIL = EMAIL_HOST_USER DEFAULT_FROM_EMAIL = EMAIL_HOST_USER 在调用send_mail()时,用全参数,默认是用你setting.py中配置的主机邮箱发送邮件。 这位同学说的对!!我就是按照这个配置方法弄成功的 ,我用的QQ邮箱 ,把‘EMAIL_USE_TLS=TRUE’这句删掉了。 这个发送邮件的功能是, 用户提交表单,如果有邮箱,那么就使用用户的邮箱发送给站点管理人员。 如果用户没有提交邮箱,那么,使用站点邮箱服务器发送邮件给管理人员 。 至于,用户提交的邮箱,为什么不需要密码, ,我也不知道。。毕竟我只是在我的笔记本尝试的,没有邮箱服务器唉。。
邮件发给管理人员??管理人员是谁啊,siteowner@example.com要改么? 已经试验成功,settings.py里必须要有以下几项: EMAIL_HOST= 'smtp.163.com' EMAIL_PORT= 25 EMAIL_HOST_USER = 'xxxxx@163.com' EMAIL_HOST_PASSWORD = 'xxxxxx' EMAIL_USE_TLS = True DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
1.就是and不是or 2.选填的邮箱地址其实就是发送地址,而不是有的人说的接收地址,亲测填settings.py里配置好账号密码的邮箱才能发送成功,或者什么都不填则用settings里默认的邮箱发送,也能成功。填其它邮箱都不会成功,会报SMTPSenderRefused at /contact/错误
[siteowner@example.com],这个邮箱是接收邮件的邮箱,可以是任意一个邮箱。但是,你的发送邮箱即表单页面填写的邮箱则必须要在settings.py中设置好的那个,不然不能发送。如果在settings.py中有DEFAULT_FROM_EMAIL = EMAIL_HOST_USER,则表单页面不填邮箱时也能成功,这时发送邮箱默认为settings.py中设置的邮箱。
CSRF token missing or incorrect. http://www.cppblog.com/momoxiao/archive/2011/10/03/157443.html
1.在setting文件中加入对应的mail配置文件(邮件名,stmp,密码等)EMAIL_HOST= 'smtp.126.com' EMAIL_PORT= 25 EMAIL_HOST_USER = '(你的邮箱)' EMAIL_HOST_PASSWORD = '(你的密码)' EMAIL_USE_TLS = True SERVER_EMAIL = EMAIL_HOST_USER DEFAULT_FROM_EMAIL = EMAIL_HOST_USER; 2.在setting文件中middleware_classes注销掉其中的#'django.middleware.csrf.CsrfViewMiddleware'; 3.contact代码中先要导入from django.core.mail import send_mail,其次send_mail函数书写有误,应该为send_mail(request.POST['subject'], request.POST['message'], '(你的邮箱)', [request.POST.get('email')],);修改完后邮件发送成功。
1、setting.py设置: EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.qq.com' EMAIL_PORT = 25 EMAIL_HOST_USER='****@qq.com' EMAIL_HOST_PASSWORD='*****' DEFAULT_FROM_EMAIL = EMAIL_HOST_USER 2、视图代码: send_mail( request.POST['subject'], request.POST['message'], '111@qq.com', [request.POST.get('email')],'111@qq.com') 3、urls.py: (r'^contact/$', contact), (r'^contact/thanks/$', contact), 4、contact_form.html里面加{% csrf_token %}: <form action="/contact/" method="post">{% csrf_token %} <p>Subject: <input type="text" name="subject"></p>
https://rayed.com/wordpress/?p=1445 always use render , not render_to_response
你在settings.py里边配置的是request.POST.get('email', 'noreply@example.com'),这一句的邮件发送服务器,而不是你的站点管理人员的。也就是说,你这里设置的东西是提供给用户用来发邮件用的,但是如果别人留下了自己的的邮箱名又会出现“'mail from address must be same as authorization user' ”的错误,因为别人的邮箱肯定和你settings.py里边设置的不匹配(不要误解使用了用户填的邮箱发送,使用的是你设置的邮件服务器)。后边的siteowner@example.com是可以随便填的接收邮箱,与设置无关。 所以我想这么解决,你随便申请个163邮箱用来给人发邮件,然后第三个参数就默认填这个邮箱,也就是settings.py里边需要设置的。然后把用户留下的email添加到邮件正文里边给你发过去,最后的siteowner@example.com是你用来接受用户反馈的邮箱。 我看到评注里边有说三四参数颠倒的,应该是没有明白settings.py设置的是给用户提供的一个邮件服务器用的,你要是颠倒了的话就让别人随意使用你的邮箱发送了。 .邮箱设置问题。 163默认是开通了smtp的,但是qq没有开通,你需要到qq邮箱账户设置里边开通才可以,要不会出现Authentication failed, please open smtp flag first!'或者 Connection refused等错误。 http://blog.csdn.net/pegasuswang_/article/details/43154535
总结一下:1 首先确保163或者qq邮箱的smtp协议开了;2 settings.py里comment掉CSRF(MIDDLEWARE里);3 EMAIL的配置参考楼上,仅注意port如果设置成465(如果你是按照qq邮箱中的方法走,他会建议你设置成465,这样增加安全性),EMAIL_USE_SSL=True. 如果设置成587,EMAIL_USE_TLS=True.
发送邮件的正确写法应该是。send_mail(request.POST['subject'],request.POST['message'],'siteowner@example.com(这是在setting里面配置过的邮箱,用于发送邮件)',[request.POST('email'), 'receive@example.com'](这两个都是接收邮件的地址,用户填的地址也会收到在表单里面填的内容,有点像是回执。后面一个邮件就是我们用来接收用户所填信息的邮箱),
if request.POST.get('email') and '@' not in request.POST['email']: 这里应该用“and”吧,因为: email=0 and not @=0 结果=0,即email不填时,@肯定为空,此时不执行语句 email=1 and not @=1 结果=0,即email不空且@存在,此时不执行语句 email=1 and not @=0 结果=1,即email不空且@不存在,此时执行语句
卡了半天 还是用163邮箱发送成功了(QQ邮箱连接收都接收不了..)。 send_mail( request.POST['subject'], request.POST['message'], request.POST['email'], ['接收信息的邮箱@example.com'], ) EMAIL_USE_SSL = True EMAIL_HOST = 'smtp.example.com' EMAIL_PORT = 465 <- 这个要看你用哪个邮箱 EMAIL_HOST_USER = '发送邮件的邮箱@example.com' EMAIL_HOST_PASSWORD = 'asd123456' <-这个是你开启stmp时候的码(QQ是随机的,163是自定义) DEFAULT_FROM_EMAIL = EMAIL_HOST_USER 墙裂推荐用两个163邮箱收发 记得开启stmp
包10061的错,配置setting后开始报错“gaierror at /contact/thanks/ [Errno 11001] getaddrinfo failed”求解
[WinError 10061] 由于目标计算机积极拒绝,无法连接。删掉了csrf,成功提交后变成这个了,求大佬解答该怎么解决,环境python3.6,django2.0.3
(如果按照书中的示例做下来,这这里可能乎产生一个疑问:contact()视图是否要放在books/views.py这个文件里。 但是contact()视图与books应用没有任何关联,那么这个视图应该可以放在别的地方? 这毫无紧要,只要在URLconf里正确设置URL与视图之间的映射,Django会正确处理的。 笔者个人喜欢创建一个contact的文件夹,与books文件夹同级。这个文件夹中包括空的__init__.py和views.py两个文件。
@ls ,报错了No module named contact.views,contact文件夹和books和mysite/mysite在同一级,在mysite/下
@ls 你在调用的时候不要弄错了 (r'^search/$', books.views.search), (r'^contact/thanks/$',contact.views.contact) 两个Views调用里面的函数时,加上文件路径啊
@dagger 检查你添加的__init__.py是否拼错,我的写成了__int__.py,怎么排错都不行,差点要疯掉了。。。 Solution: Add an empty __init__.py file under the contact folder 原因: The __init__.py files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur later on the module search path. In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all__ variable, described later. (See: http://docs.python.org/tutorial/modules.html)
from django.conf.urls import patterns, include, url from mysite.views import current_datetime from books.views import * from contact.views import * from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', # Examples: # url(r'^$', 'mysite.views.home', name='home'), # url(r'^blog/', include('blog.urls')), url(r'^admin/', include(admin.site.urls)), url(r'^time/?$', current_datetime), # url(r'^books/$', include(books.urls)), ) urlpatterns += patterns('books.views', url(r'^request/?$', display_meta), url(r'^search_form/?$', search_form), url(r'^search/?$', search), ) urlpatterns += patterns('contact.views', url(r'^contact/?$', contact), ) django1.6
views同名了 会被覆盖 我是用起别名解决的 from books import views as views_1 from contact import views as views_2
新建文件夹后直接这样引入就行了,如下: from books.views import search, search_form from contact.views import contact
from django.views.decorators.csrf import csrf_exempt @csrf_exempt def contact(request):
此处如果还想在新建的contact文件夹下设置一个新的templates文件夹,并且将contact_form.html挪到contact/templates下,需要在settings中的INSTALLED_APPS下添加contact条目。否则django无法查找到你新建的templates文件夹下的模板
现在来分析一下以上的代码:
确认request.method的值是’POST’。用户浏览表单时这个值并不存在,当且仅当表单被提交时这个值才出现。 (在后面的例子中,request.method将会设置为’GET’,因为在普通的网页浏览中,浏览器都使用GET,而非POST)。判断request.method的值很好地帮助我们将表单显示与表单处理隔离开来。
我们使用request.POST代替request.GET来获取提交过来的数据。 这是必须的,因为contact_form.html里表单使用的是method=”post”。如果在视图里通过POST获取数据,那么request.GET将为空。
这里,有两个必填项,subject 和 message,所以需要对这两个进行验证。 注意,我们使用request.POST.get()方法,并提供一个空的字符串作为默认值;这个方法很好的解决了键丢失与空数据问题。
关于request.post.get(),参考此页面 http://groups.google.com/group/django-users/browse_thread/thread/870acaba1172e468
使用request.POST.get()方法 可以获取表单指定键的值内容 并可以在获取表单内容的同时提供一个默认值防止该键对应的值为空.例如从post中取得数据,如果不存在则默认值为1 pageNumber = request.POST.get('pageNumber',1)
使用request.POST.get()方法 可以获取表单指定键的值内容 并可以在获取表单内容的同时提供一个默认值防止该键对应的值为空.例如从post中取得数据,如果不存在则默认值为1 pageNumber = request.POST.get('pageNumber',1)
虽然email非必填项,但如果有提交她的值则我们也需进行验证。 我们的验证算法相当的薄弱,仅验证值是否包含@字符。 在实际应用中,需要更为健壮的验证机制(Django提供这些验证机制,稍候我们就会看到)。
发送邮件配置(仅供新手参考):在settings.py最后添加EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.qq.com' EMAIL_PORT = 25 EMAIL_HOST_USER = '你的QQ邮箱账号' EMAIL_HOST_PASSWORD = '你的QQ邮箱密码'
Page not found (404) Request Method: GET Request URL: http://localhost:1643/contact/thanks/ 这错误要怎么解决啊
'mail from address must be same as authorization user'的意思是,SMTP服务器要求你发送人与发送邮箱要一致。也就是说,不能用自己邮箱的SMTP来发送不以自己EMAIL地址为发送人的邮件。很拗口,琢磨一下。
我们使用了django.core.mail.send_mail函数来发送e-mail。 这个函数有四个必选参数: 主题,正文,寄信人和收件人列表。 send_mail是Django的EmailMessage类的一个方便的包装,EmailMessage类提供了更高级的方法,比如附件,多部分邮件,以及对于邮件头部的完整控制。
注意,若要使用send_mail()函数来发送邮件,那么服务器需要配置成能够对外发送邮件,并且在Django中设置出站服务器地址。 参见规范:http://docs.djangoproject.com/en/dev/topics/email/
当邮件发送成功之后,我们使用HttpResponseRedirect对象将网页重定向至一个包含成功信息的页面。 包含成功信息的页面这里留给读者去编写(很简单 一个视图/URL映射/一份模板即可),但是我们要解释一下为何重定向至新的页面,而不是在模板中直接调用render_to_response()来输出。
很简单 一个视图/URL映射/一份模板即可 ---- shui xie le ? ...how to code ? who can give an eg. ...
添加一个模板,名字叫thanks.html,内容是<h1>Congratulations, e-mail sent successfully!!!</h1> 然后添加一个视图函数,在原来contact下的views.py里面加上如下内容:def thanks(request): return render_to_response('thanks.html',{}). 配置url,添加(r'^contact/thanks/$',thanks),记得导入thanks这个函数,然后发送邮件成功后就会跳转到你指定的页面
from django.views.generic import TemplateView 可以直接在url.py指向Template啊 url(r'^contact/thanks/$',TemplateView.as_view(template_name='thanks.html'))
原因就是: 若用户刷新一个包含POST表单的页面,那么请求将会重新发送造成重复。 这通常会造成非期望的结果,比如说重复的数据库记录;在我们的例子中,将导致发送两封同样的邮件。 如果用户在POST表单之后被重定向至另外的页面,就不会造成重复的请求了。
我们应每次都给成功的POST请求做重定向。 这就是web开发的最佳实践。
contact()视图可以正常工作,但是她的验证功能有些复杂。 想象一下假如一个表单包含一打字段,我们真的将必须去编写每个域对应的if判断语句?
另外一个问题是表单的重新显示。若数据验证失败后,返回客户端的表单中各字段最好是填有原来提交的数据,以便用户查看哪里出现错误(用户也不需再次填写正确的字段值)。 我们可以手动地将原来的提交数据返回给模板,并且必须编辑HTML里的各字段来填充原来的值。
# views.py
def contact(request):
errors = []
if request.method == 'POST':
if not request.POST.get('subject', ''):
errors.append('Enter a subject.')
if not request.POST.get('message', ''):
errors.append('Enter a message.')
if request.POST.get('email') and '@' not in request.POST['email']:
errors.append('Enter a valid e-mail address.')
if not errors:
send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get('email', `'noreply@example.com`_'),
[`'siteowner@example.com`_'],
)
return HttpResponseRedirect('/contact/thanks/')
return render_to_response('contact_form.html', {
'errors': errors,
**'subject': request.POST.get('subject', ''),**
**'message': request.POST.get('message', ''),**
**'email': request.POST.get('email', ''),**
})
# contact_form.html
<html>
<head>
<title>Contact us</title>
</head>
<body>
<h1>Contact us</h1>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="/contact/" method="post">
<p>Subject: <input type="text" name="subject" **value="{{ subject }}"** ></p>
<p>Your e-mail (optional): <input type="text" name="email" **value="{{ email }}"** ></p>
<p>Message: <textarea name="message" rows="10" cols="50">**{{ message }}**</textarea></p>
<input type="submit" value="Submit">
</form>
</body>
</html>
`'noreply@example.com`_' --> 'noreply@example.com' 应该是这样吧 还有这个 `'siteowner@example.com`_'
使用post会报错如下,使用GET就正常了,为什么? Forbidden (403) CSRF verification failed. Request aborted. Help Reason given for failure: CSRF token missing or incorrect. In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django's CSRF mechanism has not been used correctly. For POST forms, you need to ensure: The view function uses RequestContext for the template, instead of Context. In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL. If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data. You're seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed. You can customize this page using the CSRF_FAILURE_VIEW setting.
在网上找了一些解决方案,其中比较方便的一个是在settings.py里面的MIDDLEWARE_CLASSES 中加入'django.middleware.csrf.CsrfResponseMiddleware', 错误就消除了。
@wynner 解决了,需要在setting.py里添加: 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfResponseMiddleware',
@wynner你的django什么版本的,我在settings.py里的MIDDLEWARE_CLASSES 中加入'django.middleware.csrf.CsrfResponseMiddleware', 会引发server problem,网上搜说django1.3以后没有这个类。可是Forbidden (403) CSRF verification failed. Request aborted. 的问题还是一直在的。有没有解决方案?
'django.middleware.csrf.CsrfResponseMiddleware',---在setting里注释掉这个---然后会出错时因为文章的代码有逻辑错误: if request.POST.get('email') and '@' not in request.POST['email']: 这里应该是if not request.POST.get('email') or '@' not in request.POST['email']:-----判断email输入出错的情况
额,我建议碰到错误比较多的章节,可以看英文版的代码,看中文版的解释,或者直接全看英文版(这样速度会慢点)我就是用前者,没遇到什么问题,对楼上各位深表同情,加油!
Forbidden (403) CSRF verification failed. django 1.4下, 1,修改template文件 <form action="/contact/" method="post">{% csrf_token %} 2,修改view文件 a,import RequestContext from django.template import RequestContext b,最后一行改成 return render_to_response('contact_form.html',{ 'errors': errors, 'subject': request.POST.get('subject',''), 'message': request.POST.get('message',''), 'email': request.POST.get('email',''), }, context_instance=RequestContext(request))
我用的是1.6版本,CSRF 卡了我一个中午,最终是看这篇博文解决的http://blog.csdn.net/tr1ue/article/details/20654943
如果是403,注释此项即可 #'django.middleware.csrf.CsrfViewMiddleware', > 最终的如下 MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', #'django.middleware.csrf.CsrfViewMiddleware', # 'django.middleware.csrf.CsrfResponseMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', )
@dexter 是对的。 搞了半天才发现不能写{...}.update(csrf(request)) 必须c={...}然后c.update(csrf(request)) 大概是因为update()不返回引用
Σ( ° △ °|||)︴快被这几段代码搞疯时,注释掉 'django.middleware.common.CommonMiddleware',和 'django.middleware.csrf.CsrfViewMiddleware',就成功了,……(╯‵□′)╯︵┻━┻
1.7版本中会提示使用四种方法去解决这个问题:分别是在表单中添加{% csrf_token %},......最重要的是在render_to_response()的时候加上一个requestContext参数,如: return render_to_response('back.html', {'have': have, 'error1': error1, }, context_instance=RequestContext(request))
orbidden (403) CSRF verification failed. django 1.4下, 1,修改template文件 <form action="/contact/" method="post">{% csrf_token %} 2,修改view文件 a,import RequestContext from django.template import RequestContext b,最后一行改成 return render_to_response('contact_form.html',{ 'errors': errors, 'subject': request.POST.get('subject',''), 'message': request.POST.get('message',''), 'email': request.POST.get('email',''), }, context_instance=RequestContext(request))
if request.POST.get('email') and '@' not in request.POST['email']: 我的理解,email不为空且没有@,if为True error执行。 如果用or,request.POST.get('email'),email不为空,if为True,error为空,也就是说只要你输入就会error,显然是不合理的。
python3 django2 def contact(request): error = Error() if request.method == 'POST': if not request.POST.get('subject',''): error.set_su_value("Enter a subject") if not request.POST.get('message', ''): error.set_mess_value("Enter a message.") if not request.POST.get('email') or '@' not in request.POST['email']: error.set_email_value("Enter a valid e-mail address.") if error.mess_value and error.su_value and error.email_value is None: send_mail( request.POST['subject'], request.POST['message'], EMAIL_HOST_USER, [request.POST['email'],], ) return HttpResponseRedirect('/contact/thanks/') else: return render_to_response('template/contact_form.html', {'errors': error, 'subject':request.POST.get('subject',''), 'message': request.POST.get('message',''), 'email': request.POST.get('email',''), },) else: return render_to_response('template/contact_form.html',{'errors': error}) <!DOCTYPE html> <html lang="en"> <head> <title>Contact us</title> </head> <body> <h1>Contact us</h1> <form action="/contact/" method="post"> {% csrf_token %} <p>Subject: <input type="text" name="subject" value={{subject}}> {% if errors.su_value%} {{errors.su_value}} {% endif %} </p> <p>Your e-mail (optional): <input type="text" name="email" value={{ email }}> {% if errors.email_value%} {{errors.email_value}} {% endif %} </p> <p>Message: <textarea name="message" rows="10" cols="60">{{message}}</textarea> {% if errors.mess_value%} {{errors.mess_value}} {% endif %} </p> <i
这看起来杂乱,且写的时候容易出错。 希望你开始明白使用高级库的用意——负责处理表单及相关校验任务。
第一个Form类
Django带有一个form库,称为django.forms,这个库可以处理我们本章所提到的包括HTML表单显示以及验证。 接下来我们来深入了解一下form库,并使用她来重写contact表单应用。
Django的newforms库
在Django社区上会经常看到django.newforms这个词语。当人们讨论django.newforms,其实就是我们本章里面介绍的django.forms。
��һ�ҷ��Ϣ�. DZ��һ�ҷ��Ϣ�. ��һ�ҷ��Ϣ�. ����ҷ��Ϣ�. ��һ�ҷ��Ϣ�. ����ҷ��Ϣ�. ���һ�ҷ��Ϣ�. �����Ϣ�. ʮ�һ�ҷ��Ϣ�. ��һ�ҷ��Ϣ�. ���һ�ҷ��Ϣ�. �Ƹ���ҷ��Ϣ�. ���һ�ҷ��Ϣ�. ���һ�ҷ��Ϣ�. ���һ�ҷ��Ϣ�. �ˮһ�ҷ��Ϣ�. ̨�һ�ҷ��Ϣ�. ���һ�ҷ��Ϣ�. �ɽһ�ҷ��Ϣ�. ����ҷ��Ϣ�. ����ҷ��Ϣ�. ��һ�ҷ��Ϣ�. ���һ�ҷ��Ϣ�. ��һ�ҷ��Ϣ�. �����ҷ��Ϣ�. ���һ�ҷ��Ϣ����Ҫ�Ŀ������������������ת����ְ���Ƹ�������ѵ���ҽ̡��������������ά���װ������������ҽ�����顢�����������������������ҵ����ȡ� ��ӭ���ѷ��Ϣ����ѹ� http://www.1jfl.com http://www.1jfl.cn http://www.1jfl.com.cn һ�ҷ��Ϣ� 5130
改名其实有历史原因的。 当Django一次向公众发行时,它有一个复杂难懂的表单系统:django.forms。后来它被完全重写了,新的版本改叫作:django.newforms,这样人们还可以通过名称,使用旧版本。 当Django 1.0发布时,旧版本django.forms就不再使用了,而django.newforms也终于可以名正言顺的叫做:django.forms。
表单框架最主要的用法是,为每一个将要处理的HTML的<Form> 定义一个Form类。 在这个例子中,我们只有一个<Form> ,因此我们只需定义一个Form类。 这个类可以存在于任何地方,甚至直接写在views.py 文件里也行,但是社区的惯例是把Form类都放到一个文件中:forms.py。在存放views.py 的目录中,创建这个文件,然后输入:
一开始也没看懂,但是联系上文,明白说的是/contact目录下,我是直接新建了一个contact app,然后在里面的views.py添加了def contact方法,同理在contact目录下,新建forms.py类
使用forms框架是为你处理的每一个html <form>标签定义一个Form类。在本例中,我们只有一个<form>,所以只定义一个Form类就行了。只要你愿意,这个类你放哪都成————比如说view.py文件里。但是社区的惯例是把Form类放到一个单独的文件中:forms.py。在存放view.py的目录创建这个文件,然后输入:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField()
email = forms.EmailField(required=False)
message = forms.CharField()
这看上去简单易懂,并且很像在模块中使用的语法。 表单中的每一个字段(域)作为Form类的属性,被展现成Field类。这里只用到CharField和EmailField类型。 每一个字段都默认是必填。要使email成为可选项,我们需要指定required=False。
[quote]按照惯例,把form类都放在一个文件中[quote] 这里只是说放在一个文件(froms.py)中,没有强调是一个单独的文件夹。所以出现的问题就是下面的import无法执行。如果还是处于books目录下的话。(从第一章看到第七章的用户),应该执行:from mysite.books.forms import ContactForm
>>> f = ContactForm({subject': Hello, email: adrian@example.com, message: Nice site!}) >>> f.is_valid() True >>> f.cleaned_data {message': uNice site!, email: uadrian@example.com, subject: uHello} ContactForm({subject': Hello, email: adrian@example.com, message: Nice site!})中缺少一个单引号.
>>> f = ContactForm({subject': Hello, email: adrian@example.com, message: Nice site!}) >>> f.is_valid() True >>> f.cleaned_data {message': uNice site!, email: uadrian@example.com, subject: uHello} ContactForm({subject': Hello, email: adrian@example.com, message: Nice site!})中缺少很多个单引号.
让我们钻研到Python解释器里面看看这个类做了些什么。 它做的第一件事是将自己显示成HTML:
>>> from contact.forms import ContactForm
>>> f = ContactForm()
>>> print f
<tr><th><label for="id_subject">Subject:</label></th><td><input type="text" name="subject" id="id_subject" /></td></tr>
<tr><th><label for="id_email">Email:</label></th><td><input type="text" name="email" id="id_email" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
chrome 代码显示不全,应该如下: >>> from contact.forms import ContactForm >>> f = ContactForm() >>> print f <tr> <th><label for="id_subject">Subject:</label></th> <td><input type="text" name="subject" id="id_subject" /></td> </tr> <tr> <th><label for="id_email">Email:</label></th> <td><input type="text" name="email" id="id_email" /></td> </tr> <tr> <th><label for="id_message">Message:</label></th> <td><input type="text" name="message" id="id_message" /></td> </tr>
“让我们钻研到Python解释器里面看看这个类做了些什么” 请问是则么进入到Python解释器里执行呢? 我是在doc下进入该项目文件夹,然后执行 >>from contact.forms import ContactForm 显示有错误。
应该是直接在python IDE中定义class ContactForm() 然后f=ContactForm() print f 在manage.py shell下从form.py中import ContactForm失败
我知道了,如果在contact的文件夹下新建一个空白的文件__init__.py,那么就不会出现导入出错的错误了(ImportError: No module named contact.forms)
新建了一个APP:contact 在Pycharm终端里面输入: D:\python\mysite>python manage.py shell Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from contact.forms import ContactForm >>> f = ContactForm() >>> print f <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" name="subject" type="text" /></td></tr> <tr><th><label for="id_email">Email:</label></th><td><input id="id_email" name="email" type="text" /></td></tr> <tr><th><label for="id_message">Message:</label></th><td><input id="id_message" name="message" type="text" /></td></tr> >>>
如果是把forms.py放在books文件夹下的话,直接from books.forms import ContactForm即可,因为是在mysite这个文件夹下进入shell的。
如果是把forms.py放到一个文件夹里面,那么想访问的时候文件夹里面一定要有__init__.py文件 这样才可以使用 from 文件夹名.forms import ContactForm
为了便于访问,Django用<label> 标志,为每一个字段添加了标签。 这个做法使默认行为尽可能合适。
默认输出按照HTML的<table >格式,另外有一些其它格式的输出:
>>> print f.as_ul()
<li><label for="id_subject">Subject:</label> <input type="text" name="subject" id="id_subject" /></li>
<li><label for="id_email">Email:</label> <input type="text" name="email" id="id_email" /></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>
>>> print f.as_p()
<p><label for="id_subject">Subject:</label> <input type="text" name="subject" id="id_subject" /></p>
<p><label for="id_email">Email:</label> <input type="text" name="email" id="id_email" /></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>
请注意,标签