第19章 用户账户

19.1 让用户能够输入数据

1、添加新主题

与前面创建网页基本一样:定义一个URL,编写一个视图函数,编写一个模版。主要差别是需要导入包含表单的模块forms.py

1、用于添加主题的表单

让用户输入并提交信息的页面都是表单,哪怕它看起来不像表单。

用户输入信息时,我们需要进行验证,确认数据非恶意,正确。然后再对信息进行处理,将其保存到数据库合适的地方。

创建表单最简单的方式是使用ModelForm。创建文件forms.py将其保存到models.py的目录下。

from django import forms from .models import Topic class TopicForm(forms.ModelForm): #继承ModelForm class Meta: #ModelForm里面包含一个内嵌的Meta类 model = Topic #根据Topic模型创建表单 fields = ['text'] #包含text字段---可能是Topic模型中的text属性。 labels = {'text':''} #不要为text字段生成标签

2、URL模式new_topic

当用户要新建主题时跳转到该URL上。在urls.py中创建链接,

url(r'^new_topic/$', views.new_topic, name='new_topic')

3、视图函数new_topic()

在视图文件中创建new_topic()函数,该函数需要处理两种情形:刚进入时显示一个空表单;对提交表单数据处理,并且重新定向到topics网页。

from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse from .forms import TopicForm def new_topic(request): if request.method != 'POST': #初次请求时,非POST请求,判断条件成立。 form = TopicForm() #创建一个空表单,存储在form中,再通过后面的上下文context,发送到模版中。 else: #条件判断请求方式为POST请求。 form = TopicForm(request.POST) #使用用户输入的数据(即实参request.POST),创建实例,保存到form中。 if form.is_valid(): #函数is_valid()核实用户是否填写了所有必要字段(表单字段默认为都是必要的),且字段类型有效。 form.save() #调用save()函数,将表单数据写入数据库。 #reverse()函数获取页面topics的URL,将其传递给HttpResponseRedirect(),字面翻译http响应重新定位,将重新定位到topics页面。 return HttpResponseRedirect(reverse('learning_logs:topics')) context = {'form':form} #上下文,将form传递给form模版。 return render(request, 'learning_logs/new_topic.html', context)

4、GET请求和POST请求(针对上述代码做的解释)

对于只服务器读取数据的页面,使用GET请求;对于需要通过表单提交信息时,使用POST请求。处理表单时,使用POST请求。

用户初次请求时,浏览器发送GET请求;用户填写表单并提交时,浏览器发送POST请求。

5、模版new_topic

这里有一个来回的情况,先是get请求通过url地址或者topics页面点击进入new_topic页面,里面的表单提示以post请求将表单数据提交给回new_topic,视图函数new_topic接受post请求将form储存,并返回到topics页面,并跳出函数。

{% extends 'learning_logs/base.html' %} {% block content %} <p>Add a new topic:</p> #form定义一个表单,实参action告诉服务器将表单数据提交去哪里。实参method表明提交数据的请求方式为POST。 <form action="{% url 'learning_logs:new_topic' %}" method='post'> {% csrf_token %} #防止攻击者利用表单来获得对服务器未经授权的访问----跨站请求伪造。 {{ form.as_p }} #as_p修饰符让Django以段落格式渲染所有表单元素。 <button name="submit">add topic</button> #创建一个按钮用于提交表单。 </form> {% endblock content %}

6、topics.html

在topics页面中添加一个页面new_topic的链接。

<a href="{% url 'learning_logs:new_topic' %}">Add a new topic</a>

2、添加新条目

1、用于添加新条目的表单

from .models import Topic, Entry class EntryForm(form.ModelForm): class Meta: model = Entry fields = ['text'] labels = {'text':''} #定义属性widgets(小部件)是html表单元素,通过让django使用forms.Textarea()函数,定义文本宽度为80列。 widgets = {'text': forms.Textarea(attrs={'cols': 80})}

2、URL模式new_entry

用于添加新条目的页面URL模式中,需要包含实参topic_id,因为条目必须与特定的主题相关联。

url(r'^new_entry/(?P<topic_id>\d+)/$', views.new_entry, name='new_entry')

3、视图函数new_entry()

from .forms import TopicForm, EntryForm #导入刚创建的EntryForm模块。 def new_entry(request, topic_id): #包含形参topic_id,用于存储从URL获得的值。 topic = Topic.objects.get(topic_id) #渲染页面和处理表单时获取正确的主题。 '''检查请求是不是post,如果不是建立一个空的EntryForm实例;如果是,data用request中的post数据来填充---从下面的模版中能了解到,并 且如果有效就设置条目对象的属性topic,再将条目对象存储到数据库中。''' if request.method != 'POST': form = EntryForm() else: form = EntryForm(data=request.POST) if form.is_valid(): new_entry = form.save(commit=False) #传递了实参commit=False,也就是不发送,意思是将django把数据保存在new_entry中,但不发送到数据库中。 new_entry.topic = topic #确定该条目的主题。 new_entry.save() #完成上述之后,再将条目数据保存到数据库中。 return HttpResponseRedirect(reverse('learning_logs:topic', args=[topic_id])) #topic的URL模式有参数,所以需要后面的args来传递所有实参。 context = {'topic':topic, 'form':form} return render(request, 'learning_logs/new_entry.html', context)

4、模板new_entry

注意一下,下述代码中,对于topic的id用的是topic.id这种表达方式,有机会的话研究一下,这里的topic指的是什么,肯定不是变量。

{% extends 'learning_logs/base.html' %} {% block content %} <p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></p> <p>Add a new entry</p> <form action="{% url 'leaning_logs:new_entry' topic.id %}" method='post'> {% csrf_token %} {{ form.as_p }} <button name='submit'>add entry</button> </form> {% endblock content %}

5、链接到页面new_entry

在topic模板中添加链接到new_entry页面的链接。

<p> <a href="{% url 'learning_logs:new_entry' topic.id %}">add new entry</a> </p>

3、编辑条目

创建一个页面,让用户可以编辑既有条目。

1、URL模式edit_entry

在URL中传递的ID存储在形参entry_id中,将匹配的请求发送给视图函数edit_entry()

url(r'^edit_entry/(?P<entry_id>\d+)/$', views.edit_entry, name='edit_entry')

2、视图函数edit_entry()

页面收到get请求,返回一个对应entry条目的页面,可以进行编辑;页面收到post请求,将修改后的文本保存。

from .models import Topic, Entry def edit_entry(request, entry_id): entry = Entry.objects.get(entry_id) #获取需要修改的条目对象。 topic = entry.topic #这是反过来获取该条目对应的主题。 if request.method != 'POST': form = EntryForm(instance=entry) #instance=entry创建一个EntryForm实例,让django用既有条目信息填充表单。 else: form = EntryForm(instance=entry, data=request.POST) #两个参数,第一个让django用既有条目信息创建表单,第二个用post数据进行修改。 if form.is_valid(): form.save() return HttpResponseRedirect(reverse('learning_logs:topic', args=[topic.id]))#重新定向页面,注意要有topic的id参数,而且是个列表。 context = {'entry': entry, 'topic': topic, 'form': form} #这里context定义了三个键值对,应该是用于在模板中直接调用这三个变量。 return render(request,'learning_logs/edit_entry.html', context)

3、模板edit_entry

{% extends "learning_logs/base.html" %} {% block content %} <p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></p> <p>Edit entry:</p> <form action="{% url 'learning_logs:edit_entry' entry.id %}" method='post'> #将条目id作为一个实参,让视图对象正确修改条目对象。 {% csrf_token %} {{ form.as_p }} <button name='submit'>save changes</button> </form> {% endblock content %}

4、链接到页面edit_entry

在topic.html中,在遍历每个条目的for循环中,给每个条目添加页面edit_entry的链接

<p> <a href="{% url 'learning_logs:edit_entry' entry.id %}">edit entry</a> #注意仍然需要用entry.id来确定URL,明确 </p>

19.2 创建用户账户

1、应用程序users

创建一个users的应用程序

(ll_env)learning_logs$ python3 manage.py startapp users

1、将应用程序users添加到settings.py中

这样,django将把应用程序users包含到项目中。

2、包含应用程序users的URL

在项目的urls.py中加入users的URL,根目录为users/,包含users.urls中的URL 地址。

2、登录页面

在learning_log/users/目录下新建urls.py文件。

from django.conf.urls import url from django.contrib.auth.views import login #导入默认视图login,在升级后的django里,修改为导入类的模式,而不是导入函数。变成LoginView from . import views urlpatterns = [ url(r'^login/$', login, {'template_name':'users/login.html'}, name='login') ]

1、模板login.html

在learning_log/users/目录下创建templates目录,并在其中创建一个users的目录。在里面创建模板。继承base.html模板格式。

{% extends 'learning_logs/base.html' %} {% block content %} {% if form.errors %} <p>Your username and password didn't match. Please try again.</p> {% endif %} <form method='post' action="{% url 'users:login' %}"> #登录视图,将一个表单发送给模板, {% csrf_token %} {{ form.as_p }} <button name="submit">log in</button> <input type="hidden" name="next" value="{% url 'learning_logs:index' %}" /> #实参value告诉django在成功登录后定向为主页。 </form> {% endblock content %}

2、链接到登录页面

在base.html中添加到登录页面的链接,让所有的页面都包含它。对于已登录用户,不想显示这个链接,故嵌套在一个if标签中。

<a href="{% url 'learning_logs:topic' %}">Topics</a> - {% if user.is_authenticated %} #django身份验证系统下每个模板都可以使用user变量,其中有一个is_authenticated属性。 Hello, {{ user.username }} {% else %} <a href="{% url 'users:login' %}">log in</a> #对于else条件,定向至登录链接。 {% endif %}

3、使用登录页面

3、注销

1、注销URL

不创建用于注销的页面,而让用户只需单击一个链接就能注销并返回主页。为此,我们定义一个URL模式,编写一个视图函数,并在base.html中添加注销链接。

在users/urls.py中添加如下链接

url(r'^logout/$', views.logout_view, name='logout'),

2、视图函数logout_view()

导入django函数logout(),并且调用它,再重新定向到主页。在users/views.py中添加。

from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse #新版本改成from django.urls import reverse from django.contrib.auth import logout #导入函数logout,估计新版本会有变化。 def logout_view(request): logout(request) #以request为参数,调用logout return HttpResponseRedirect(reverse('learning_logs:index')) #定向到主页。

3、链接到注销视图

在base.html的if条件判断中,加入如果已登录的,显示注销链接。

<a href="{% url 'users:logout' %}">log out</a>

4、注册页面

创建一个能够让用户注册的页面。使用django的表单UserCreationForm

1、注册页面的URL模式,在users/urls.py中添加。

url(r'^register/$', views.register, name='register')

该代码与URL:http://localhost:8000/users/register/相匹配。

2、视图函数register()

需要显示一个空的注册表单,在用户提交时进行处理,如果注册成功还要自动登录。在views.py文件中新建register函数。

from django.shortcuts import render from django.contrib.auth import login,logout,authenticate #authenticate:证……是真的。 #导入函数login为了在注册成功时自动登录。 from django.contrib.auth.forms import UserCreationForm #导入默认用户注册表单。 def register(request): if request.method != 'POST': form = UserCreationForm() else: form = UserCreationForm(data=request.POST) if form.is_valid(): new_user = form.save() #如输入有效,将用户名和密码的散列值(即哈希函数)保存到数据库。 ## authenticate()函数将返回一个通过了身份验证的对象,并将其存储在authenticated_user中。 authenticated_user = authenticate(username=new_user.username, password=request.POST['password1']) #注册时密码输入了两次,这里随便拿一次。 login(request, authenticated_user) #这个不太懂,为什么要传递请求和被验证对象两个参数。 return HttpResponseRedirect(reverse('learning_logs:index')) context = {'form': form} return render(request, 'users/register.html', context)

3、注册模板

在login.html同一个目录中创建register.html

{% extends 'learning_logs/base.html' %} {% block content %} <form method="post" action="{% url 'users:register' %}"> {% csrf_token %} {{ form.as_p }} <button name="submit">register</button> <input type="hidden" name="next" value="{% url 'learning_logs:index' %}" /> </form> {% endblock content %}

4、链接到注册页面

在base.html中添加,添加在if判断里面,如果是已登录用户,则不用看到此链接,非用户可以看到此链接。

<a href="{% url 'users:register' %}">register</a>

19.3 让用户拥有自己的数据

本节将修改模型Topic,让每个主题都归属于特定的用户。也将影响条目,每个条目属于不同的主题。

1、使用@login_required限制访问

1、限制对topics页面的访问

只允许已登录用户访问topics页面。在learning_logs/views.py中添加。

from django.contrib.auth.decorators import login_required #decorators:装饰器,导入login_required @login_required #让python在运行topics()代码前先运行login_required()代码。 def topics(request): ......

login_required()代码检查用户是否登录,只有登录时django才继续运行topics(),如果未登录,则重新定向到主页。需要继续在settings.py中修改,让django知道去哪里查找登录页面。settings.py中。

LOGIN_URL = '/users/login/'

2、全面限制对项目“学习笔记”的访问

在views.py中,除了主页,其他页面都需要限制,即加上装饰器。不能点击到相关的视图链接,也不能通过URL来直接链接,都会重新定向到登录页面。

2、将数据关联到用户

最高层归属用户,低层的会自动归属。本例中,主题归属用户,条目自动会归属用户。

1、修改模型Topic,增加一个外键关联。

from django.contrib.auth.models import User owner = models.ForeignKey(User)

2、确定当前有哪些用户

我们在迁移数据库时,将存储主题和用户之间进行关联。

用shell先查询到User.objects.all()下有几个id,进行遍历print每个的username和id,知道每个用户的序号。

通过makemigrations learning_logs迁移数据库。django会要求对每个topic提供默认值,即提供相应的user编号。

接下来django使用该值来迁移数据库,并生成迁移文件,在模型topic中添加字段owner。

在虚拟环境下执行迁移,migrate

PS:截止到目前,我们把所有的topic都归属为admin用户了,但是这并没有限制其他用户对其访问,因为login_required的要求是需要登录之后才能显示页面,并没有限制必须登录相应的owner才能看到相应的页面。

3、只允许用户访问自己的主题

在views.py中对函数topics()进行修改

topics = Topic.objects.filter(owner=request.user).order_by('date_added') #加入一个过滤,注意这个表达方式,filter是夹在原来代码的当中的。

4、保护用户的主题

截止目前,我们还没有限制对显示单个主题页面的访问,如果是已登录用户只要直接输入相应的topic_id的URL还是能访问相应的主题。

为修复该问题,在视图函数topic()获取请求的条目前执行检查。

from django.http import HttpResponseRedirect, Http404 #确认请求的主题属于当前用户 if topic.owner != request.user: raise Http404 #raise是用来引发异常处理的响应的,具体的还要再研究。

5、保护页面edit_entry (这里对于修改条目要保护,其他的url就还有新增主题和新增条目,这两项不用保护,具体还是查看urls.py中的需求。)

跟上面一样的代码,写在edit_entry()函数下。

6、将新主题关联到当前用户

当前新增主题时,会因为缺失owner字段而报错。因此将新主题关联到当前用户中。

new_topic.owner = request.user

PS:本章学完了,但是不能进行删除啊,没有删除功能啊。找一下删除功能怎么体现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不贰笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值