A、让用户能够输入数据

一、用于添加主题的表单

Django中创建表单最简单的方式是ModelForm。创建一个名为forms.py的文件,位置同models.py

forms.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中

urls.py
# 用于添加新主题的网页
url(r'^new_topic/$', views.new_topic, name='new_topic'),

三、视图函数new_topic()

函数new_topic()需要处理俩种情形:刚进入new_top网页(应显示空表单);对提交的表单数据进行处理,并将用户重定向到网页topics。

views.py
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

new_topic.html
{% 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

base.html
{% 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、添加新条目

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

forms.py
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中

urls.py
# 用于添加新条目的页面
url(r'new_entry/(?P<topic_id>\d+)/$', views.new_entry, name='new_entry')

八、视图函数new_entry()

views.py
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

new_entry.html
{% 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

base.html
{% 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中

urls.py
# 用于编辑条目的页面
url(r'edit_entry/(?P<entry_id>\d+)/$', views.edit_entry, name='edit_entry'),

十一、视图函数edit_entry()

views.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
 
 
# 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

edit_entry.html
{% 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

topic.html
{% 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中

setting.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

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文件

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

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属性被设置,我们就显示一条错误消息,指出输入的用户名-密码与数据库中存储的不一致。

十九、连接到登陆页面

base.html
<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

urls.py
# 注销
url(r'^logout/$', views.logout_view, name='logout'),

二十二、视图函数logout_view()

编辑users文件下views.py

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'))

二十三、链接到注销视图

base.html
<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

urls.py
# 注册
url(r'^register/$', views.register, name='register'),

二十四、视图函数register()

编辑users文件下views.py

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传递给它,这将为新用户创建有效的会话。最后,将用户重定向到主页。

二十五、注册模版

register.html
{% 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 %}

二十六、链接到注册页面

base.html
<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

views.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):
    """编辑已有条目"""
settings.py
# 我的设置
LOGIN_URL = '/users/login'

Django提供了装饰器@login_required,检查用户是否已登陆。仅当用户已登陆时,Django才运行后续代码;如果用户未登陆,就重定向到登陆页面,即settings.py中的LOGIN_URL指定的URL。

I、将数据关联到用户

二十八、修改模型Topic

修改learning_logs下的models.py

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

views.py
@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

views.py
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

views.py
@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

views.py
@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)

 

posted on 2020-07-13 11:57  Q同码  阅读(426)  评论(0编辑  收藏  举报