IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python编程--Django入门:用户账户 -> 正文阅读

[Python知识库]Python编程--Django入门:用户账户

Python编程:用户账户

Web应用程序的核心是让任何用户都能够注册账户并能够使用它,不管用户身处何方。让用户能够添加主题和条目,以及编辑既有的条目。目标—实现一个用户身份验证系统。

1. 让用户能够输入数据

建立用于创建用户账户的身份验证系统之前,先来添加一个页面,让用户能够输入数据。让用户能够添加新主题、添加新条目以及编辑既有条目。使用Django的表单工具来创建让用户能够输入数据的页面。

1.1 添加新主题

工作流程是:定义一个URL------编写一个视图函数------编写一个模板。

1. 用于添加新主题的表单

在Django中,创建表单的最简单方式是使用ModelForm,创建一个名为forms.py的文件,将其存储到models.py所在的目录中,并在其中编写第一个表单:

forms.py

from django import forms

from .models import Topic


class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic        # 根据模型Topic创建一个表单
        fields = ['text']
        labels = {'text': ''}

2. URL模式new_topic

这个新网页的URL应简短而具有描述性,因此当用户要添加新主题时,切换到http://localhost:8000/new_topic/。将其添加到learning_logs/urls.py中。

urls.py

"""定义learning_logs的URL模式"""

from django.conf.urls import url

from . import views


urlpatterns = [
    # 主页
    url(r'^$', views.index, name='index'),

    # 显示所有的主题
    url(r'^topics/$', views.topics, name='topics'),

    # 显示特定主题
    url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),

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

这个URL模式将请求交给视图函数new_topic()。

3. 视图函数new_topic()

函数new_topic()需要处理两种情形:刚进入new_topic网页(在这种情况下, 它应显示一个空表单);对提交的表单数据进行处理,并将用户重定向到网页topics:

views.py

from django.shortcuts import render

from .models import Topic

from django.http import HttpResponseRedirect

from django.core.urlresolvers import reverse

from  . forms import TopicForm


# Create your views here.
def index(request):
    """学习笔记的主页"""
    return render(request, 'learning_logs/index.html')

def topics(request):
    """显示所有的主题"""
    topics = Topic.objects.order_by('date_added')
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)


def topic(request, topic_id):
    """显示单个主题机器所有的条目"""
    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
    context = {'topic': topic, 'entries': entries}
    return render(request, 'learning_logs/topic.html', context)

def new_topic(request):
    """添加新主题"""
    if request.method != 'POST':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        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。

4. GET请求和POST请求

创建Web应用程序时,将用到两种主要请求类型是GET请求和POST请求。对于只是从服务器读取数据的页面,使用GET请求;在用户需要通过表单提交信息时,通常使用POST请求。处理所有表单时,将指定使用POST方法。

5. 模板new_topic

创建新模板new_topic.html, 用于显示刚刚创建的表单:

new_topic.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>new_topic</title>
</head>
<body>
{% 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> #Django不会为表单创建提交按钮,因此我们定义了一个这样的按																																														钮。
</form>

{% endblock content %}}
</body>
</html>

这个模板继承了base.html,因此其基本结构与项目“学习笔记”的其他页面相同。Django使用模板标签{% csrf_token %}来防止攻击者利用表单来获对服务器未经授权的访问(这种攻击称为跨站请求伪造)。

6. 链接到页面new_topic

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

topics.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Topics</title>
</head>
<body>
{% 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 %}
</body>
</html>

这个链接放在了既有主题列表的后面,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dbkyNfzY-1655299103217)(https://tva1.sinaimg.cn/large/e6c9d24ely1h38xdazbmaj227c0i8q56.jpg)]

1.2 添加新条目

现在可以添加新主题了,还需要添加新条目。再次定义URL,编写视图函数和模板,并链接到添加新条目的网页。

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

创建一个与模型Entry相关联的表单,但这个表单的定制程度比TopicForm要高些:

forms.py

from django import forms

from .models import Topic, Entry 

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        fields = ['text']
        labels = {'text': ''}


class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        fields = ['text']
        labels = {'text':''} # 给字段'text'指定了空标签
        widgets = {'text': forms.Textarea(attrs={'cols': 80})} # widget(小部件)是一个HTML表单元																																素,如单行文本框、多行文本区域或下拉																																													列表。

对照之前forms.py中的import语句,除导入Topic外,还导入了Entry。新类EntryForm继承了forms.ModelForm。

2. URL模式new_entry

urls.py

"""定义learning_logs的URL模式"""

from django.conf.urls import url

from . import views


urlpatterns = [
    # 主页
    url(r'^$', views.index, name='index'),

    # 显示所有的主题
    url(r'^topics/$', views.topics, name='topics'),

    # 显示特定主题
    url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),

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

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

这个URL模式与形式为http://localhost:8000/new_entry/id/的URL匹配,其中id是一个与主题ID匹配的数字。

3. 视图函数new_entry()

视图函数new_entry()与函数new_topic()很像:

导入EntryForm,new_entry()的定义包含形参topic_id,用于存储从URL中获得得值。

views.py


from __future__ import unicode_literals

from django.shortcuts import render

from .models import Topic, Entry

from django.http import HttpResponseRedirect

from django.core.urlresolvers import reverse

from  . forms import TopicForm, EntryForm


# Create your views here.
def index(request):
    """学习笔记的主页"""
    return render(request, 'learning_logs/index.html')

def topics(request):
    """显示所有的主题"""
    topics = Topic.objects.order_by('date_added')
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)


def topic(request, topic_id):
    """显示单个主题机器所有的条目"""
    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
    context = {'topic': topic, 'entries': entries}
    return render(request, 'learning_logs/topic.html', context)

def new_topic(request):
    """添加新主题"""
    if request.method != 'POST':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))
    context = {'form':form}
    return render(request, 'learning_logs/new_topic.html', context)


def new_entry(request, topic_id):
    """在特定的主题中添加新条目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 未提交数据,创建一个空表单
        form = EntryForm()

    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(data=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)

4. 模板new_entry

模板new_entry类似于模板new_topic:

new_entry.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>new-entry</title>
</head>
<body>
{% 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 'learning_logs:new_entry' topic.id %}" method='post'>
    {% csrf_token %}
    {{ form.as_p }}
    <button name='submit'> add entry</button>
</form>

{% endblock content %}}
</body>
</html>

5.链接到页面new_entry

在显示特定主题的页面中添加到页面new_entry的链接:

topic.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>topic</title>
</head>
<body>
{% 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>       
    </li>
    {% empty %}
    <li>There are no entries for this topic yet.</li>
    {% endfor %}
</ul>

{% endblock content %}

</body>
</html>

在显示条目前添加链接,因为在这种页面中,执行最常见的操作是添加新条目。下图显示了页面new_entry。现在用户可以添加新主题,还可以在每个主题中添加任意数量的条目。尝试使用一下页面new_entry。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7WeB9Fyx-1655299103219)(https://tva1.sinaimg.cn/large/e6c9d24ely1h38yuq4dy8j227y0qiwi0.jpg)]

1.3 编辑条目

下面创建一个页面,让用户能够编辑既有的条目。

1. URL模式edit_entry

这个页面的URL需要传递要编辑的条目ID。修改后的learning_logs/urls.py如下:

urls.py

"""定义learning_logs的URL模式"""

from django.conf.urls import url

from . import views


urlpatterns = [
    # 主页
    url(r'^$', views.index, name='index'),

    # 显示所有的主题
    url(r'^topics/$', views.topics, name='topics'),

    # 显示特定主题
    url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),

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

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

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

]

2. 视图函数edit_entry()

页面edit_entry收到GET请求时,edit_entry()将返回一个表单,让用户能够对条目进行编辑。该页面收到POST请求(条目文本经过修订)时,它将修改后的文本保存到数据库中:

views.py

from django.shortcuts import render

from .models import Topic, Entry

from django.http import HttpResponseRedirect

from django.core.urlresolvers import reverse

from  . forms import TopicForm, EntryForm


# Create your views here.
def index(request):
    """学习笔记的主页"""
    return render(request, 'learning_logs/index.html')

def topics(request):
    """显示所有的主题"""
    topics = Topic.objects.order_by('date_added')
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)


def topic(request, topic_id):
    """显示单个主题机器所有的条目"""
    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
    context = {'topic': topic, 'entries': entries}
    return render(request, 'learning_logs/topic.html', context)

def new_topic(request):
    """添加新主题"""
    if request.method != 'POST':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))
    context = {'form':form}
    return render(request, 'learning_logs/new_topic.html', context)


def new_entry(request, topic_id):
    """在特定的主题中添加新条目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 未提交数据,创建一个空表单
        form = EntryForm()

    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(data=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)


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:
        # POST提交的数据,对数据进行处理
        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)

3. 模板edit_entry

下面是模板edit_entry.html,它与模板new_entry.html类似:

edit_entry.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>edit-entry</title>
</head>
<body>
{% 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 %}
</body>
</html>

4. 链接到页面edit_entry

在显示特定主题的页面中,需要给每个条目添加到页面edit_entry的链接:

topic.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>topic</title>
</head>
<body>
{% 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 %}

</body>
</html>

编辑链接放在每个条目的日期和文本后面。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-03AqoDOj-1655299103220)(https://tva1.sinaimg.cn/large/e6c9d24ely1h391nmhwpej21ot0u0gqg.jpg)]

2. 创建用户账户

建立一个用户注册和身份验证系统,让用户能够注册账户,进而登录和注销。创建一个新的应用程序,其中包含与处理用户账户相关的所有功能。

2.1 应用程序users

使用命令startapp来创建一个名为users的应用程序:

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py startapp users
(11_env) (base) liuxiaowei@MacBook-Air learning_log % ls
11_env          __init__.py     db.sqlite3      learning_log    learning_logs   manage.py       users
(11_env) (base) liuxiaowei@MacBook-Air learning_log % ls users
__init__.py     admin.py        apps.py         migrations      models.py       tests.py        views.py

这个命令新建一个名为users的目录,结构与应用程序learning_logs相同。

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

在settings.py中,将这个新的应用程序添加到INSTALLED_APPS中, 如下所示:

settings.py

"""
Django settings for learning_log project.

Generated by 'django-admin startproject' using Django 1.11.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = ')-lv47!$0c!j5-7th)l_-osv&ugkws7!6pv&+oe_0=p_xo(g@_'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # My app
    'learning_logs',
    'users',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'learning_log.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'learning_log.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = '/static/'

2. 包含应用程序users的URL

需要修改项目根目录中的urls.py,使其包含我们将为应用程序users定义的URL:

urls.py

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^users/', include('users.urls', namespace='users')),
    url(r'', include('learning_logs.urls', namespace='learning_logs')),
]

2.2 登录页面

在目录learning_log/users/中,新建一个名为urls.py的文件,并在其中添加如下代码:

urls.py

"""为应用程序users定义URL模式"""


from django.conf.urls import url

from django.contrib.auth.views import login 

from . import views

urlpatterns = [
    # 登录页面
    url(r'^login/$', login, {'template_name': 'users/login.html'},
        name='login'),
]

1. 模板login.html

用户请求登录页面时,Django使用其默认视图login。但我们依然需要为这个页面提供模板。为此,在目录learning_log/users/中,创建一个名为templates的目录,并在其中创建一个名为users的目录。将login.html模板存储到目录learning_log/users/templates/users/中:

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
{% 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' %}" />
</form>
{% endblock content %}


</body>
</html>

2. 链接到登录页面

在base.html中添加到登录页面的链接,让所有页面都包含它。用户已登录时,我们不想显示这个链接,因此将它嵌套在一个{% if %}标签中。

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Base</title>
</head>
<body>
<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 %}
</body>
</html>

3. 使用登录页面

前面建立了一个用户账户,下面来登录一下,请访问http://localhost:8000/admin/,如果依然是以管理员身份登录的,请在页眉上找到注销链接并单击它。注销后,访问http://localhost:8000/users/login/,看到如下图所示的登录页面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-43M9ejVn-1655299103221)(https://tva1.sinaimg.cn/large/e6c9d24ely1h393v4zw4ij22700mu76s.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MLOD6CuP-1655299103222)(https://tva1.sinaimg.cn/large/e6c9d24ely1h393wx2rdgj22700muju1.jpg)]

2.3 注销

1. 注销URL

下面代码为注销定义了URL模式,该模式与URL http://localhost:8000/users/logout/匹配。修改后的users/urls.py如下:

urls.py

"""为应用程序users定义URL模式"""


from django.conf.urls import url

from django.contrib.auth.views import login

from . import views

urlpatterns = [
    # 登录页面
    url(r'^login/$', login, {'template_name': 'users/login.html'},
        name='login'),

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

2. 视图函数logout_view()

函数logout_view()很简单:只是导入Django函数logout(),并调用它,再重定向到主页。打开users/views.py,并输入下面的代码:

views.py

from django.shortcuts import render

from django.http import HttpResponseRedirect

from django.core.urlresolvers import reverse

from django.contrib.auth import logout


# Create your views here.

def logout_view(request):
    """注销用户"""
    logout(request)
    return HttpResponseRedirect(reverse('learning_logs:index'))

3. 链接到注销视图

添加一个注销链接。在base.html中添加这种链接,让每个页面都包含它;将它放在标签{% if user.is_authenticated %}中,使得仅当用户登录后才能看到它:

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Base</title>
</head>
<body>
<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 %}
</body>
</html>

用户登录后看到的主页,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjOEyEEG-1655299103223)(https://tva1.sinaimg.cn/large/e6c9d24ely1h394g8c4u9j22700muwh7.jpg)]

2.4 注册页面

创建一个让新用户能够注册的页面。使用Django提供的表单UserCreationForm,编写自己的视图函数和模板。

1. 注册页面的URL模式

urls.py

"""为应用程序users定义URL模式"""


from django.conf.urls import url

from django.contrib.auth.views import login

from . import views

urlpatterns = [
    # 登录页面
    url(r'^login/$', login, {'template_name': 'users/login.html'},
        name='login'),

    # 注销
    url(r'^logout/$', views.logout_view, name='logout'),
    
    # 注册页面
    url(r'^regiester/$', views.register, name='register')
]

2. 视图函数register()

在users/views.py中添加如下代码:

views.py

from django.shortcuts import render

from django.http import HttpResponseRedirect

from django.core.urlresolvers import reverse

from django.contrib.auth import login, logout, authenticate

from django.contrib.auth.forms import UserCreationForm




# Create your views here.

def logout_view(request):
    """注销用户"""
    logout(request)
    return HttpResponseRedirect(reverse('learning_logs:index'))


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)

3. 注册模板

注册页面的模板与登录页面的模板类似,务必保存到login.html所在目录中:

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>register</title>
</head>
<body>
{% 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 %}

</body>
</html>

4. 链接到注册页面

base.htm

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Base</title>
</head>
<body>
<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 %}
</body>
</html>

下面尝试一下注册新用户,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dbMAL9WR-1655299103224)(https://tva1.sinaimg.cn/large/e6c9d24ely1h395naretmj22700muwh6.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ItP5fBqE-1655299103225)(https://tva1.sinaimg.cn/large/e6c9d24ely1h395ookmrij22700mudk2.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iRzr9M2p-1655299103227)(https://tva1.sinaimg.cn/large/e6c9d24ely1h395pvje6jj22700mutbg.jpg)]

注册成功!

3. 让用户拥有自己的数据

创建一个系统,确定各项数据所属的用户,再限制对页面的访问,让用户只能使用自己的数据。

3.1 使用@login_required限制访问

Django提供了装饰器@login_required,能够轻松地实现这样的目标:对于某些页面,只允许已登录的用户访问。

1. 限制对topics页面的访问

每个主题都归特定用户所有,因此应只允许已登录的用户请求topics页面。为此,在learning_logs/views.py中添加如下代码:

views.py

from django.shortcuts import render

from .models import Topic, Entry

from django.http import HttpResponseRedirect

from django.core.urlresolvers import reverse

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):
    """显示所有的主题"""
    topics = Topic.objects.order_by('date_added')
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)


def topic(request, topic_id):
    """显示单个主题机器所有的条目"""
    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
    context = {'topic': topic, 'entries': entries}
    return render(request, 'learning_logs/topic.html', context)

def new_topic(request):
    """添加新主题"""
    if request.method != 'POST':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))
    context = {'form':form}
    return render(request, 'learning_logs/new_topic.html', context)


def new_entry(request, topic_id):
    """在特定的主题中添加新条目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 未提交数据,创建一个空表单
        form = EntryForm()

    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(data=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)


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:
        # POST提交的数据,对数据进行处理
        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)

在settings.py末尾添加如下代码:

settings.py

"""
Django settings for learning_log project.

Generated by 'django-admin startproject' using Django 1.11.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = ')-lv47!$0c!j5-7th)l_-osv&ugkws7!6pv&+oe_0=p_xo(g@_'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # My app
    'learning_logs',
    'users',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'learning_log.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'learning_log.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = '/static/'

"""
项目learning_log的Django设置
"""

# 我的设置
LOGIN_URL = '/users/login/' 

要测试这个设置, 可注销并进入主页。然后,单击链接Topics,重定向到登录页面。使用自己的账户登录,再次单击主页中的Topics链接,看到topics页面。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-snMdYRLS-1655299103228)(https://tva1.sinaimg.cn/large/e6c9d24ely1h3966qqw1dj22700mu0vf.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fv9DlNvz-1655299103229)(https://tva1.sinaimg.cn/large/e6c9d24ely1h3967cnrwxj22700mudio.jpg)]

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

在项目“学习笔记”中,不限制对主页、注册页面和注销页面的访问,限制对其他所有页面的访问。

在learning_logs/views.py中,对除index()外对每个视图都应用了装饰器@login_required:

views.py

from django.shortcuts import render

from .models import Topic, Entry

from django.http import HttpResponseRedirect

from django.core.urlresolvers import reverse

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):
    """显示所有的主题"""
    topics = Topic.objects.order_by('date_added')
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)

@login_required
def topic(request, topic_id):
    """显示单个主题机器所有的条目"""
    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
    context = {'topic': topic, 'entries': entries}
    return render(request, 'learning_logs/topic.html', context)

@login_required
def new_topic(request):
    """添加新主题"""
    if request.method != 'POST':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))
    context = {'form':form}
    return render(request, 'learning_logs/new_topic.html', context)


@login_required
def new_entry(request, topic_id):
    """在特定的主题中添加新条目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 未提交数据,创建一个空表单
        form = EntryForm()

    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(data=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)


@login_required
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:
        # POST提交的数据,对数据进行处理
        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)

如果输入URL http://localhost:8000/new_topic/,将重定向到登录页面。对于所有与私有用户数据相关的URL,都应限制对它们的访问。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lo7mc0TC-1655299103229)(https://tva1.sinaimg.cn/large/e6c9d24ely1h396yvpv09j22700muwhc.jpg)]

3.2 将数据关联到用户

修改模型Topic,在其中添加一个关联到用户的外键。一定要对数据库进行迁移。然后必须对有些视图进行修改,使其只显示与当前登录的用户相关联的数据。

1. 修改模型Topic

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


    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] + '...'

2. 确定当前有哪些用户

当迁移数据库时,Django将对数据库进行修改,使其能够存储主题和用户之间的关联。为执行迁移,Django需要知道该将各个既有主题关联到哪个用户。最简单的办法是,将既有主题都关联到同一个用户,如超级用户。为此,需要知道该用户ID。查看已创建的所有用户ID,启动一个Django shell会话,执行如下命令:

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py shell
Python 3.9.12 (v3.9.12:b28265d7e6, Mar 23 2022, 18:17:11) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.auth.models import User
>>> User.objects.all()
<QuerySet [<User: lxw_admin>, <User: bruce_lee>, <User: ll>]>
>>> for user in User.objects.all():
...     print(user.username, user.id)
... 
lxw_admin 1
bruce_lee 2
ll 3

3. 迁移数据库

知道用户ID后,就可以i迁移数据库了。

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py makemigrations learning_logs
You are trying to add a non-nullable field 'owner' to topic without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> 1
Migrations for 'learning_logs':
  learning_logs/migrations/0003_topic_owner.py
    - Add field owner to topic

如上所示,将所有既有主题都关联到管理用户lxw_admin,输入了用户ID值1。

现在可以执行迁移了,在虚拟环境中执行如下命令:

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
  Applying learning_logs.0003_topic_owner... OK

为验证迁移符合预期,可在shell会话中如下操作:

>>> from learning_logs.models import Topic
>>> for topic in Topic.objects.all():
...     print(topic, topic.owner)
... 
Chess lxw_admin
Rock Climbing lxw_admin
dlsd lxw_admin
Opera lxw_admin
Opera lxw_admin
>>> 

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

现在,不管你以哪个用户身份登录,都能看到所有主题。现在改变这种情况,只向用户显示属于自己的主题。

在views.py中,对函数topics()做如下修改:

views.py

from django.shortcuts import render

from .models import Topic, Entry

from django.http import HttpResponseRedirect

from django.core.urlresolvers import reverse

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):
    """显示所有的主题"""
    topics = Topic.objects.filter(owner=request.user).order_by('date_added')  # 添加代码
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)

@login_required
def topic(request, topic_id):
    """显示单个主题机器所有的条目"""
    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
    context = {'topic': topic, 'entries': entries}
    return render(request, 'learning_logs/topic.html', context)

@login_required
def new_topic(request):
    """添加新主题"""
    if request.method != 'POST':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))
    context = {'form':form}
    return render(request, 'learning_logs/new_topic.html', context)


@login_required
def new_entry(request, topic_id):
    """在特定的主题中添加新条目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 未提交数据,创建一个空表单
        form = EntryForm()

    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(data=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)


@login_required
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:
        # POST提交的数据,对数据进行处理
        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)

3.4 保护用户的主题

目前还没有限制对显示单个主题的页面的访问,要修复这种问题,在视图函数topic()获取请求的条目前执行检查:

views.py

from django.shortcuts import render

from .models import Topic, Entry

from django.http import HttpResponseRedirect, Http404    # 添加代码

from django.core.urlresolvers import reverse

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):
    """显示所有的主题"""
    topics = Topic.objects.filter(owner=request.user).order_by('date_added')
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)

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

@login_required
def new_topic(request):
    """添加新主题"""
    if request.method != 'POST':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))
    context = {'form':form}
    return render(request, 'learning_logs/new_topic.html', context)


@login_required
def new_entry(request, topic_id):
    """在特定的主题中添加新条目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 未提交数据,创建一个空表单
        form = EntryForm()

    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(data=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)


@login_required
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:
        # POST提交的数据,对数据进行处理
        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)

3.5 保护页面edit_entry

页面edit_entry的URL为http://localhost:8000/edit_entry/entry_id/,其中entry_id是一个数字。禁止用户通过输入类似前面的URL来访问其他用户的条目,命令如下:

views.py

from django.shortcuts import render

from .models import Topic, Entry

from django.http import HttpResponseRedirect, Http404

from django.core.urlresolvers import reverse

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):
    """显示所有的主题"""
    topics = Topic.objects.filter(owner=request.user).order_by('date_added')
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)

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

@login_required
def new_topic(request):
    """添加新主题"""
    if request.method != 'POST':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))
    context = {'form':form}
    return render(request, 'learning_logs/new_topic.html', context)


@login_required
def new_entry(request, topic_id):
    """在特定的主题中添加新条目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 未提交数据,创建一个空表单
        form = EntryForm()

    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(data=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)


@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:
        # POST提交的数据,对数据进行处理
        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)

3.6 将新主题关联到当前用户

修改views.py代码:

views.py

from django.shortcuts import render

from .models import Topic, Entry

from django.http import HttpResponseRedirect, Http404

from django.core.urlresolvers import reverse

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):
    """显示所有的主题"""
    topics = Topic.objects.filter(owner=request.user).order_by('date_added')
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)

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

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


@login_required
def new_entry(request, topic_id):
    """在特定的主题中添加新条目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != 'POST':
        # 未提交数据,创建一个空表单
        form = EntryForm()

    else:
        # POST提交的数据,对数据进行处理
        form = EntryForm(data=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)


@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:
        # POST提交的数据,对数据进行处理
        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)
  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-06-20 22:59:42  更:2022-06-20 22:59:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 12:57:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码