• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

Django写一个简单投票系统

Python框架 彭东稳 7年前 (2017-10-21) 18073次浏览 已收录 1个评论

一、简单投票系统

下面我们利用前面所学的知识,如下四个章节:

Django视图与URL

Django模型(Model)

Django模板(Template)

Django表单(Form)

来完成一个简单投票系统,前面的学习基本都是在围绕这个简单投票系统而进行的,下面只需要结合及完善起来即可。在Django模板(Template)章节我们已经可以把问题显示出来了,如下图所示:

Django写一个简单投票系统

我们接着这个要完成整个简单投票系统,工作流程图如下:

Django写一个简单投票系统

下面我们要完善问题详情页、投票功能、以及投票结果显示页面。

首先写投票详细信息模板(“polls/detail.html”),该模板包含一个HTML的<form>元素:

这个“{% csrf_token %}”标签是Django默认就有的插件,用在表单中,防止跨站攻击的,一般表单提交必须写这个标签。然后你可以在POST请求中看到这个标识。

定义detail view函数:

如果服务启动不报错的话,访问http://10.10.0.109:8000/polls/6/会得到如下页面:

Django写一个简单投票系统

Django get_object_or_404是django shortcuts模块里面一个比较简便的方法,特别是用django get来操作数据库的时候,可以帮我们少写一些代码,加快开发速度。

get_object_or_404的介绍: 我们原来调用django的get方法,如果查询的对象不存在的话,会抛出一个DoesNotExist的异常, 现在我们调用django get_object_or_404方法,它会默认的调用django的get方法, 如果查询的对象不存在的话,会抛出一个Http404的异常,存在则返回即可。这样对用户比较友好, 如果用户查询某个产品不存在的话,我们就显示404的页面给用户,比直接显示异常好。

get_object_or_404一般需要3个参数,modelname可以为一个mode,manage或query对象。而后面的*args,**kwargs则是查询的时候用到的参数。

下面用个例子看来下:

Question是要查询的model,后面的pk=6是查询条件(pk表示主键,会自动查找主键字段),你可以根据你需要查询的情况来写条件。

如果通过get_object_or_404方法获取不到对应的值呢?get_object_or_404会返回内置的404页面,如下:

Django写一个简单投票系统

投票详情页定义完了之后,接下来就要提供一个投票功能了,定义vote view函数。

我们这里判断了请求类型,如果访问视图不是一个POST请求,它将返回我们定义的一些内容,这是我们在第一次访问该URL时可以预期发生的情况。当我们投票时,打印出了request.POST信息,你在终端可以看到如下信息:

POST是一个QueryDict,类字典,而值是一个列表,所以我们使用request.POST.get(‘choice’, 0)方法可以用来获取用户投票选项,然后使用question.choice_set.get(pk=choice_id)反查前面获取到的投票选项ID。如果查到证明用户投票确实存在就可以往下走,如果查不到就抛出异常,只要使用get方法就要考虑异常处理,但是如果使用filter获取不到则为空,不会抛异常。

这里投票完了之后,我们使用HttpResponseRedirect重定向到了http://10.10.0.109:8000/polls/6/results/页面,防止重复提交。当然你也可以重定向到其它URL。

定义显示投票结果页面:

定义results view函数:

如果没有问题,那么我们投完票后将会重定向到http://10.10.0.109:8000/polls/6/results/页面,就会执行results函数,返回results.html页面,得到投票结果,如下:

Django写一个简单投票系统

到此为止,整个简单投票系统基本完成了。

最后回到开始哪张流程图:

Django写一个简单投票系统

从点击问题–>问题详情–>进行投票–>投票详细的完整流程链,现在已经完成了问题详情–>进行投票–>投票详细的完整链,下面只需要完成,点击问题–>问题详情的跳转,修改index.html文件,如下:

主要就是完成每个问题的超链接地址通过反解析得到其问题详情页。

二、HttpRequest对象

当请求一个页面时,Django创建一个HttpRequest对象。该对象包含request的元数据,然后Django调用相应的view函数(HttpRequest对象自动传递给该view函数<作为第一个参数>),每一个view负责返回一个HttpResponse对象。就像下面这个hello()函数:

下面解释HttpRequest和HttpResponse对象的API(属性),就是包含当前请求URL的一些信息:

HttpRequest.scheme

一个字符串,表示请求的方案(通常是http或https)。

HttpRequest.path

请求页面的全路径,不包括域名。例如”/hello/world”。

HttpRequest.body

一个字符串,表示原始HTTP请求的正文。它对于处理非HTML形式的数据非常有用:二进制图像、XML等。 如果要处理常规的表单数据,应该使用HttpRequest.POST,因为Django已经将这些POST数据处理成QueryDict,而不是在body中的一节字符串。

你也可以使用”类文件“形式的接口从HttpRequest中读取数据。

HttpRequest.content_type

一个字符串,根据content_type参数获取MIME类型,比如浏览器访问是text/html,API访问时application/json。开发时可以根据这个来判断是什么设备访问,然后返回不同的数据。

HttpRequest.encoding

一个字符串,表示提交的数据的编码方式(如果为None则表示使用DEFAULT_CHARSET设置)。这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。接下来对属性的任何访问(例如从GET或POST中读取数据)将使用新的encoding值。如果你知道表单数据的编码不在DEFAULT_CHARSET中,则使用它。

HttpRequest.method

请求中使用的HTTP方法的字符串表示。全大写表示。例如:

HttpRequest.GET

包含所有HTTP GET参数的类字典对象,参见QueryDict文档。

HttpRequest.POST

包含所有HTTP POST参数的类字典对象。参见QueryDict文档。服务器收到空的POST请求的情况也是有可能发生的。也就是说,表单form通过HTTP POST方法提交请求,但是表单中可以没有数据。因此,不能使用语句if request.POST来判断是否使用HTTP POST方法;应该使用if request.method == “POST” (参见本表的method属性)。注意: POST不包括file-upload信息,参见FILES属性。

HttpRequest.COOKIES

包含所有cookies的标准Python字典对象,Keys和values都是字符串。

HttpRequest.session

一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django启用会话的支持时才可用。

HttpRequest.FILES

用来上传文件,包含所有上传文件的类字典对象,FILES中的每个Key都是<input type=”file” name=”” />标签中name属性的值。FILES中的每个value同时也是一个标准Python字典对象,包含下面三个Keys:

  • filename:上传文件名,用Python字符串表示。
  • content-type:上传文件的Content type。
  • content:上传文件的原始内容。

注意:只有在请求方法是POST,并且请求页面中<form>有enctype=”multipart/form-data”属性时FILES才拥有数据。否则,FILES是一个空字典。

HttpRequest.META

元数据信息,一个标准的Python字典,包含所有的HTTP头部。具体的头部信息取决于客户端和服务器,下面是一些示例:

  • CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
  • CONTENT_TYPE —— 请求的正文的MIME 类型。
  • HTTP_ACCEPT —— 响应可接收的Content-Type。
  • HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
  • HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
  • HTTP_HOST —— 客服端发送的HTTP Host头部。
  • HTTP_REFERER —— Referring页面。
  • HTTP_USER_AGENT —— 客户端的user-agent字符串。
  • QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
  • REMOTE_ADDR —— 客户端的IP 地址。
  • REMOTE_HOST —— 客户端的主机名。
  • REMOTE_USER —— 服务器认证后的用户。
  • REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"
  • SERVER_NAME —— 服务器的主机名。
  • SERVER_PORT —— 服务器的端口(是一个字符串)。

从上面可以看到,除CONTENT_LENGTH和CONTENT_TYPE之外,请求中的任何HTTP头部转换为META的键时,都会将所有字母大写并将连接符替换为下划线最后加上HTTP_ 前缀。所以,一个叫做X-Bender的头部将转换成META中的HTTP_X_BENDER键。

HttpRequest.user

是一个django.contrib.auth.models.User对象,代表当前登录的用户。如果访问用户当前没有登录,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你可以通过user的is_authenticated()方法来辨别用户是否登录:

只有激活Django中的AuthenticationMiddleware时该属性才可用。

HttpRequest.session

唯一可读写的属性,代表当前会话的字典对象。只有激活Django中的session支持时该属性才可用。

三、QueryDict对象

HttpRequest对象中,属性GET和POST得到的都是django.http.QueryDict所创建的实例。这是一个django自定义的类似字典的类,用来处理同一个键带多个值的情况。

在Python原始的字典中,当一个键出现多个值的时候会发生冲突,只保留最后一个值。而在HTML表单中,通常会发生一个键有多个值的情况,例如<select multiple>(多选框)就是一个很常见情况。

Django提供了QueryDict API,可以用来测试这些方法,进入django shell环境即可:

下面我们来看这个类中有什么方法:

这是一个构造函数,其中query_string需要一个字符串,例如:

如果query_string没有传入,则获得一个空的对象。

你所遇到的QueryDict对象,特别是request.POST和request.GET得到的。如果你想自己实例化一个对象,可以传递mutable=True使你所实例化的对象可变。当然request.POST和request.GET是django创建的,也就是说除非改django源码,否则它们是不可变的。

对于设置的键和值,会从encoding转码成Unicode。也就是说,如果传入的字符串query_string是GBK或者是utf-8的编码,将会自动转码成Unicode,然后用做字典的键和值。如果encoding = None,也就是没有设定的话,将使用DEFAULT_CHARSET的值,默认为:’utf-8’。

QueryDict实现所有标准的字典方法,还包括一些特有的方法:

QueryDict.__getitem__(key)

返回给出的key的值。如果key具有多个值,__getitem__()返回最后(最新)的值。如果key不存在,则引发django.utils.datastructures.MultiValueDictKeyError。(它是Python标准KeyError的一个子类,所以你仍然可以坚持捕获KeyError。)

QueryDict.__setitem__(key, value)

设置给出的key的值为[value](一个Python列表,只有一个元素value)。注意:只有对象是可以改变的时候才能使用,例如通过.copy()方法创建的对象,或者设置为可变类型。

QueryDict.__contains__(key)

如果给出的key已经设置,则返回True。它让你可以做if “foo” in request.GET这样的操作。

QueryDict.get(key, default)

使用与上面__getitem__()相同的逻辑,但是当key不存在时返回一个默认值。

QueryDict.setdefault(key, default)

类似标准字典的setdefault()方法,只是它在内部使用的是__setitem__()。也就是说,当key已经存在时,返回其值,key不存在时,返回default,同时添加key和default到对象中。

QueryDict.update(other_dict)

接收一个QueryDict或标准字典。类似标准字典的update()方法,但是它附加到当前字典项的后面,而不是替换掉它们。

QueryDict.items()

类似标准字典的items()方法,返回一个迭代对象。但是它使用的是和__getitem__一样返回最新的值的逻辑。

QueryDict.lists()

类似QueryDict.iteritems(),返回一个包含键值对的元祖(key, value)迭代对象 ,value是一个包括所有key的值的列表。

QueryDict.values()

类似标准字典的values()方法,但是它使用的是和__getitem__一样返回最新的值的逻辑。也就是返回一个所有键对应的最新值的列表。

QueryDict.copy()

返回对象的副本,使用Python标准库中的copy.deepcopy()。此副本是可变的,即使原始对象是不可变的。

QueryDict.getlist(key, default)

以Python列表形式返回所请求的键的数据。如果键不存在并且没有提供默认值,则返回空列表。它保证返回的是某种类型的列表,除非默认值不是列表。

QueryDict.setlist(key, list_)

为给定的键设置list_(与__setitem__()不同),可以设置一个多元素的列表。

QueryDict.appendlist(key, item)

将项追加到内部与键相关联的列表中。

QueryDict.setlistdefault(key, default_list)

类似setdefault,除了它接受一个列表而不是单个值。

QueryDict.pop(key)

返回给定键的值的列表,并从字典中移除它们。如果键不存在,将引发KeyError。

QueryDict.popitem()

删除字典任意一个成员(因为没有顺序的概念),并返回二值元组,包含键和键的所有值的列表。在一个空的字典上调用时将引发KeyError。

QueryDict.dict()

返回QueryDict的dict表示形式。对于QueryDict中的每个(key, list)对 ,dict将有(key, item) 对,其中item是列表中的一个元素,使用与QueryDict.__getitem__()相同的逻辑,也就是最新的:

QueryDict.urlencode([safe])

从数据中返回查询字符串格式。

可选地,urlencode可以传递不需要编码的字符。(这意味着要进行url编码)

Django Request和Response对象


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (3)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!

(1)个小伙伴在吐槽