A、让用户能够输入数据
一、用于添加主题的表单
Django中创建表单最简单的方式是ModelForm。创建一个名为forms.py的文件,位置同models.py
from django import forms from .models import Topic class TopicForm(forms.ModelForm): class Meta: model = Topic fields = [ 'text' ] labels = { 'text' : ''} # 让Django不要为字段text生成标签 |
最简单ModelForm版本只包含一个内嵌的Meta类,它告诉Django根据哪个模型创建表单,以及表单中包含哪些字段。
二、URL模式new_topic
将new_topic的url添加到learning_logs文件夹下的urls.py中
# 用于添加新主题的网页 url(r '^new_topic/$' , views.new_topic, name = 'new_topic' ), |
三、视图函数new_topic()
函数new_topic()需要处理俩种情形:刚进入new_top网页(应显示空表单);对提交的表单数据进行处理,并将用户重定向到网页topics。
from django.shortcuts import render from django.http import HttpResponseRedirect from django.urls import reverse from .models import Topic from .forms import TopicForm # Create your views here. def index(request): """学习笔记的主页""" def topics(request): """显示所有的主题""" def topic(request, topic_id): """显示单个主题及其所有的条目""" def new_topic(request): """添加新主题""" if request.method ! = 'POST' : # 未提交数据:创建一个新表单 form = TopicForm() else : # POST提交的数据,对数据进行处理 form = TopicForm(request.POST) # 函数is_valid()核实用户填写了所有必不可少的字段,且输入的数据与要求的字段类型一致 if form.is_valid(): form.save() return HttpResponseRedirect(reverse( 'learning_logs:topics' )) context = { 'form' : form} return render(request, 'learning_logs/new_topic.html' , context) |
导入HttpResponseRedirect类,用户提交主题后我们将使用这个类将用户重定向到网页topics。函数reverse()根据指定的URL模型确定URL,这意味着Django将在页面被请求时生成URL。我们还导入了刚才创建的表单TopicForm。
三、GET请求和POST请求
对于只是从服务器读取数据的页面,使用GET请求;在用户需要通过表单提交信息时,通常使用POST请求。处理所有表单时,我们都将指定使用POST方法。
四、模版new_topic
{% extends "learning_logs/base.html" %} {% block content %} < p >Add a new topic:</ p > < form action = "{% url 'learning_logs:new_topic' %}" method = "post" > {% csrf_token %} {{ form.as_p }} < button name = "submit" >add topic</ button > </ form > {% endblock content %} |
Django使用模板标签{% csrf_token %}来防止攻击者利用表单来获取对服务器未经授权的访问(这种攻击被称为跨站请求伪装)。{{ form.as_p }}让Django自动创建显示表单所需的全部字段,修饰符as_p让Django以段落格式渲染所有表单元素
五、链接到页面new_topic
{% extends "learning_logs/base.html" %} {% block content %} < p >Topics</ p > < ul > {% for topic in topics %} < li > < a href = "{% url 'learning_logs:topic' topic.id %}" >{{ topic }}</ a > </ li > {% empty %} < li >No topics have been added yet.</ li > {% endfor %} </ ul > < a href = "{% url 'learning_logs:new_topic' %}" >Add a new topic:</ a > {% endblock content %} |
B、添加新条目
六、用于添加新条目的表单
from django import forms from .models import Topic, Entry class TopicForm(forms.ModelForm): class EntryForm(forms.ModelForm): class Meta: model = Entry fields = [ 'text' ] labels = { 'text' : ''} widgets = { 'text' : forms.Textarea(attrs = { 'cols' : 80 })} |
widget是一个HTML表单元素,如单行文本框、多行文本区域或下拉列表
七、URL模式new_entry
将new_entry的url添加到learning_logs文件夹下的urls.py中
# 用于添加新条目的页面 url(r 'new_entry/(?P<topic_id>\d+)/$' , views.new_entry, name = 'new_entry' ) |
八、视图函数new_entry()
from django.shortcuts import render from django.http import HttpResponseRedirect from django.urls import reverse from .models import Topic from .forms import TopicForm, EntryForm # Create your views here. ... def new_entry(request, topic_id): """在特定的主题中添加条目""" topic = Topic.objects.get( id = topic_id) if request.method ! = 'POST' : # 未提交数据:创建一个新表单 form = EntryForm() else : # POST提交的数据,对数据进行处理 form = EntryForm(request.POST) if form.is_valid(): new_entry = form.save(commit = False ) new_entry.topic = topic new_entry.save() return HttpResponseRedirect(reverse( 'learning_logs:topic' , args = [topic_id])) context = { 'topic' : topic, 'form' : form} return render(request, 'learning_logs/new_entry.html' , context) |
commit=False让Django创建一个新的条目对象,将其存储到new_entry中,但不保存到数据库。我们将new_entry的属性topic设置为在这个函数开头从数据库中获取的主题,然后调用save(),且不指定任何实参。这将把条目存储到数据库中,并将其与正确的主题相关联。列表args,其中包含要包含在URL中的所有实参
九、模版new_entry
{% extends "learning_logs/base.html" %} {% block content %} < p >< a href = "{% url " learning_logs:topic" topic.id %}">{{ topic }}</ a ></ p > < p >Add new entry:</ p > < form action = "{% url " learning_logs:new_entry" topic.id %}" , method = "post" > {% csrf_token %} {{ form.as_p }} < button name = "submit" >Add entry</ button > </ form > {% endblock content %} |
十、链接到页面new_entry
{% extends "learning_logs/base.html" %} {% block content %} < p >Topics</ p > < ul > {% for topic in topics %} < li > < a href = "{% url 'learning_logs:topic' topic.id %}" >{{ topic }}</ a > </ li > {% empty %} < li >No topics have been added yet.</ li > {% endfor %} </ ul > < a href = "{% url 'learning_logs:new_topic' %}" >Add a new topic:</ a > {% endblock content %} |
C、编辑条目
十、URL模式edit_entry
将edit_entry的url添加到learning_logs文件夹下的urls.py中
# 用于编辑条目的页面 url(r 'edit_entry/(?P<entry_id>\d+)/$' , views.edit_entry, name = 'edit_entry' ), |
十一、视图函数edit_entry()
from django.shortcuts import render from django.http import HttpResponseRedirect from django.urls import reverse from .models import Topic, Entry from .forms import TopicForm, EntryForm # Create your views here. ... def edit_entry(request, entry_id): """编辑已有条目""" entry = Entry.objects.get( id = entry_id) topic = entry.topic if request.method ! = 'POST' : # 初次请求,使用当前条目填充表单 form = EntryForm(instance = entry) else : form = EntryForm(instance = entry, data = request.POST) if form.is_valid(): form.save() return HttpResponseRedirect(reverse( 'learning_logs:topic' , args = [topic. id ])) context = { 'entry' : entry, 'topic' : topic, 'form' : form} return render(request, 'learning_logs/edit_entry.html' , context) |
instance=entry创建EntryForm的实例,这个实参让Django创建一个表单,并使用既有条目对象中的信息填充它。处理器POST请求时,我们传递实参instance=entry, data=request.POST,让Django根据既有条目对象创建一个表单实例,并根据request.POST中的相关数据对其进行修改。
十二、模版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" > {% csrf_token %} {{ form.as_p }} < button name = "submit" >save changes</ button > </ form > {% endblock content %} |
十三、链接到页面edit_entry
{% extends "learning_logs/base.html" %} {% block content %} < p >Topic: {{ topic }}</ p > < p >Entries:</ p > < p >< a href = "{% url " learning_logs:new_entry" topic.id %}">Add new entry</ a ></ p > < ul > {% for entry in entries %} < li > < p >{{ entry.date_added|date:'M d,Y H:i' }}</ p > < p >{{ entry.text|linebreaks }}</ p > < p >< a href = "{% url " learning_logs:edit_entry" entry.id %}">edit entry</ a ></ p > </ li > {% empty %} < li > There are no entries for this topic yet. </ li > {% endfor %} </ ul > {% endblock content %} |
D、创建应用程序users
十四、创建名为users的应用程序
python manage.py startapp users |
十五、将应用程序users添加到settings.py中
INSTALLED_APPS = [ 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , # My apps 'learning_logs' , 'users' , ] |
十六、包含应用程序users的URL
修改learning_log文件下的urls.py
urlpatterns = [ path( 'admin/' , admin.site.urls), url(r' ', include((' learning_logs.urls ', ' learning_logs '), namespace=' learning_logs')), url(r '^users/' , include(( 'users.urls' , 'users' ), namespace = 'users' )), ] |
E、登陆页面
十七、创建urls.py
在users文件夹下创建urls.py文件
"""为应用程序users定义URL模式""" from django.conf.urls import url from django.contrib.auth.views import LoginView from . import views urlpatterns = [ # 登陆页面 url(r '^login/$' , LoginView.as_view(template_name = 'users/login.html' ), name = 'login' ), ] |
十八、模版login.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 action = "{% url " users:login" %}" method = "post" > {% csrf_token %} {{ form.as_p }} < button name = "submit" >log in</ button > < input type = "hidden" name = "text" value = "{% url " learning_logs:index" %}"/> </ form > {% endblock content %} |
如果表单的errors属性被设置,我们就显示一条错误消息,指出输入的用户名-密码与数据库中存储的不一致。
十九、连接到登陆页面
< p > < a href = "{% url 'learning_logs:index' %}" >Learning Log</ a > < a href = "{% url 'learning_logs:topics' %}" >Topics</ a > {% if user.is_authenticated %} Hello,{{ user.username }}. {% else %} < a href = "{% url " users:login" %}">log in</ a > {% endif %} </ p > {% block content %}{% endblock content %} |
在Django身份验证系统中,每个模板都可使用变量user,这个变量有一个is_authenticated属性:如果用户已登陆,该属性将为true,否则为False
二十、使用登陆页面
F、注销
二十一、注销URL
修改users下的urls.py
# 注销 url(r '^logout/$' , views.logout_view, name = 'logout' ), |
二十二、视图函数logout_view()
编辑users文件下views.py
from django.shortcuts import render from django.http import HttpResponseRedirect from django.urls import reverse from django.contrib.auth import logout # Create your views here. def logout_view(request): """注销用户""" logout(request) return HttpResponseRedirect(reverse( 'learning_logs:index' )) |
二十三、链接到注销视图
< p > < a href = "{% url 'learning_logs:index' %}" >Learning Log</ a > < a href = "{% url 'learning_logs:topics' %}" >Topics</ a > {% if user.is_authenticated %} Hello,{{ user.username }}. < a href = "{% url " users:logout" %}">log out</ a > {% else %} < a href = "{% url " users:login" %}">log in</ a > {% endif %} </ p > {% block content %}{% endblock content %} |
G、注册页面
二十三、注册页面的URL模式
修改users下的urls.py
# 注册 url(r '^register/$' , views.register, name = 'register' ), |
二十四、视图函数register()
编辑users文件下views.py
from django.shortcuts import render from django.http import HttpResponseRedirect from django.urls import reverse from django.contrib.auth import logout, login, authenticate from django.contrib.auth.forms import UserCreationForm # Create your views here. def logout_view(request): """注销用户""" def register(request): """注册新用户""" if request.method ! = 'POST' : # 显示空的注册表 form = UserCreationForm() else : # 处理填好的表单 form = UserCreationForm(data = request.POST) if form.is_valid(): new_user = form.save() # 让用户自动登陆,再重定向到主页 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) |
如果相应的是POST请求,我们就根据提交的数据创建一个UserCreationForm实例,并检查这些数据是否有效(用户名为包含非法字符、输入的俩个密码相同、用户没有试图做恶意的事情)。
保存用户的信息后,我们让用户自动登陆:
1、调用authenticate(),并将实参new_user.username和密码传递给它。用户注册时,被要求输入密码俩次;由于表单有效的,我们知道输入的这俩个密码是相同的,因此可以使用其中任何一个。这里,从表单POST数据中获取与键“password1”相关联的值。用户名和密码无误,方法authenticate()将返回一个通过了身份验证的用户对象,将其存储在authrnticated_user中。
2、调用登陆login(),将对象request和suthenticated_user传递给它,这将为新用户创建有效的会话。最后,将用户重定向到主页。
二十五、注册模版
{% extends "learning_logs/base.html" %} {% block content %} < form action = "{% url " users:register" %}" method = "post" > {% csrf_token %} {{ form.as_p }} < button name = "submit" >register</ button > < input type = "hidden" name = "next" value = "{% url " learning_logs:index" %}"/> </ form > {% endblock content %} |
二十六、链接到注册页面
< p > < a href = "{% url 'learning_logs:index' %}" >Learning Log</ a > < a href = "{% url 'learning_logs:topics' %}" >Topics</ a > {% if user.is_authenticated %} Hello,{{ user.username }}. < a href = "{% url " users:logout" %}">log out</ a > {% else %} < a href = "{% url " users:register" %}">register</ a > < a href = "{% url " users:login" %}">log in</ a > {% endif %} </ p > {% block content %}{% endblock content %} |
H、让用户拥有自己的数据
二十七、使用@login_required限制访问
修改learning_logs下view.py
from django.shortcuts import render from django.http import HttpResponseRedirect from django.urls import reverse from .models import Topic, Entry from .forms import TopicForm, EntryForm from django.contrib.auth.decorators import login_required # Create your views here. def index(request): """学习笔记的主页""" return render(request, 'learning_logs/index.html' ) @login_required () def topics(request): """显示所有的主题""" @login_required () def topic(request, topic_id): """显示单个主题及其所有的条目""" @login_required () def new_topic(request): """添加新主题""" @login_required () def new_entry(request, topic_id): """在特定的主题中添加条目""" @login_required () def edit_entry(request, entry_id): """编辑已有条目""" |
# 我的设置 LOGIN_URL = '/users/login' |
Django提供了装饰器@login_required,检查用户是否已登陆。仅当用户已登陆时,Django才运行后续代码;如果用户未登陆,就重定向到登陆页面,即settings.py中的LOGIN_URL指定的URL。
I、将数据关联到用户
二十八、修改模型Topic
修改learning_logs下的models.py
from django.db import models from django.contrib.auth.models import User # Create your models here. class Topic(models.Model): """用户学习的主题""" text = models.CharField(max_length = 200 ) date_added = models.DateTimeField(auto_now_add = True ) owner = models.ForeignKey(User, on_delete = models.CASCADE) def __str__( self ): """返回模型的字符串表示""" return self .text class Entry(models.Model): """学到的有关某个主题的具体知识""" topic = models.ForeignKey(Topic, on_delete = models.CASCADE) text = models.TextField() date_added = models.DateTimeField(auto_now_add = True ) class Meta: verbose_name_plural = 'entries' def __str__( self ): """返回模型中的字符串表示""" return self .text[: 50 ] + '...' |
二十九、确定当前有哪些用户
二十九、迁移数据库
三十、只允许用户访问自己的主题
修改learning_logs下view.py的topics
@login_required () def topics(request): """显示所有的主题""" topics = Topic.objects. filter (owner = request.user).order_by( 'date_added' ) context = { 'topics' : topics} return render(request, 'learning_logs/topics.html' , context) |
三十一、保护用户的主题
修改learning_logs下view.py的topic
from django.shortcuts import render from django.http import HttpResponseRedirect, Http404 from django.urls import reverse from .models import Topic, Entry from .forms import TopicForm, EntryForm from django.contrib.auth.decorators import login_required # Create your views here. @login_required () def topic(request, topic_id): """显示单个主题及其所有的条目""" topic = Topic.objects.get( id = topic_id) # 确认请求的主题属于当前用户 if topic.owner ! = request.user: raise Http404 entries = topic.entry_set.order_by( '-date_added' ) context = { 'topic' : topic, 'entries' : entries} return render(request, 'learning_logs/topic.html' , context) |
三十二、保护页面edit_entry
修改learning_logs下view.py的edit_entry
@login_required () def edit_entry(request, entry_id): """编辑已有条目""" entry = Entry.objects.get( id = entry_id) topic = entry.topic if topic.owner ! = request.user: raise Http404 if request.method ! = 'POST' : # 初次请求,使用当前条目填充表单 form = EntryForm(instance = entry) else : form = EntryForm(instance = entry, data = request.POST) if form.is_valid(): form.save() return HttpResponseRedirect(reverse( 'learning_logs:topic' , args = [topic. id ])) context = { 'entry' : entry, 'topic' : topic, 'form' : form} return render(request, 'learning_logs/edit_entry.html' , context) |
三十三、将新主题关联到当前用户
修改learning_logs下view.py的new_topic
@login_required () def new_topic(request): """添加新主题""" if request.method ! = 'POST' : # 未提交数据:创建一个新表单 form = TopicForm() else : # POST提交的数据,对数据进行处理 form = TopicForm(request.POST) if form.is_valid(): new_topic = form.save(commit = False ) new_topic.owner = request.user new_topic.save() return HttpResponseRedirect(reverse( 'learning_logs:topics' )) context = { 'form' : form} return render(request, 'learning_logs/new_topic.html' , context) |