A、让用户能够输入数据
一、用于添加主题的表单
Django中创建表单最简单的方式是ModelForm。创建一个名为forms.py的文件,位置同models.py
from django import formsfrom .models import Topicclass 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 renderfrom django.http import HttpResponseRedirectfrom django.urls import reversefrom .models import Topicfrom .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 formsfrom .models import Topic, Entryclass 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 renderfrom django.http import HttpResponseRedirectfrom django.urls import reversefrom .models import Topicfrom .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 renderfrom django.http import HttpResponseRedirectfrom django.urls import reversefrom .models import Topic, Entryfrom .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 urlfrom django.contrib.auth.views import LoginViewfrom . import viewsurlpatterns = [ # 登陆页面 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 renderfrom django.http import HttpResponseRedirectfrom django.urls import reversefrom 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 renderfrom django.http import HttpResponseRedirectfrom django.urls import reversefrom django.contrib.auth import logout, login, authenticatefrom 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 renderfrom django.http import HttpResponseRedirectfrom django.urls import reversefrom .models import Topic, Entryfrom .forms import TopicForm, EntryFormfrom 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 modelsfrom 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.textclass 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 renderfrom django.http import HttpResponseRedirect, Http404from django.urls import reversefrom .models import Topic, Entryfrom .forms import TopicForm, EntryFormfrom 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) |








浙公网安备 33010602011771号