19-1 博客 新建一个Django项目,将其命名为Blog。在这个项目中,创建一个名为blogs 的应用程序,并在其中创建一个名为BlogPost 的模型。这个模型应包含title 、text 和date_added 等字段。为这个项目创建一个超级用户,并使用管理网站创建几个简短的帖子。创建一个主页,在其中按时间顺序显示所有的帖子。
创建两个表单,其中一个用于发布新帖子,另一个用于编辑既有的帖子。尝试填写这些表单,确认它们能够正确工作。
1 创建虚拟环境
Windows:
'cmd'
python -m venv ll_env
2 激活虚拟环境
ll_env\Scripts\activate
3 安装Django
(ll_env) Blog>pip install django
4 创建项目
(ll_env) Blog>django-admin startproject Blog .
5 创建数据库
(ll_env) Blog>python manage.py migrate
6 创建应用程序
(ll_env) Blog>python manage.py startapp blogs
7 创建模型
models.py
from django.db import models
class BlogPost(models.Modes):
""" 博客页面 """
title = models.CharField(max_length=200)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbase_name_plural = 'BlogPost'
def __str__(self):
""" 返回模型字符串表示 """
return self.text
8 激活模型
settings.py
INSTALLED_APPS = [
'blogs',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
python manage.py makemigrations blogs
python manage.py migrate
9 创建超级用户
(ll_env) D:\Blog>python manage.py createsuperuser
admin.py
from django.contrib import admin
from .models import BlogPost
admin.site.register(BlogPost)
10 表单
forms.py
from django import forms
from .models import BlogPost
class BlogPostForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['title','text']
widgets = {'text': forms.Textarea(attrs={'cols':80})}
class BlogForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['title','text']
labels = {'text':'','title':''}
widgets = {'text': forms.Textarea(attrs={'cols':80})}
11 视图
views.py
from django.shortcuts import render,redirect
from .models import BlogPost
from .forms import BlogPostForm,BlogForm
def index(request):
""" 主页,显示所有 """
blogposts = BlogPost.objects.order_by('-date_added')
context = {'blogposts':blogposts}
return render(request,'blogs/index.html',context)
def new_blog(request):
""" 添加新博客 """
if request.method != 'POST':
form = BlogPostForm()
else:
form = BlogPostForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:index')
context = {'form':form}
return render(request,'blogs/new_blog.html',context)
def edit_blog(request,blog_id):
""" 编辑博客 """
blogpost = BlogPost.objects.get(id=blog_id)
title = blogpost.title
if request.method != 'POST':
form = BlogPostForm(instance=blogpost)
else:
form = BlogPostForm(instance=blogpost,data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:index')
context = {'blogpost':blogpost,'title':title,'form':form}
return render(request,'blogs/edit_blog.html',context)
12 URL模式
- Blog -> urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include(('blogs.urls','blogs'),namespace='blogs')),
]
- blogs -> urls.py
from django.urls import path
from . import views
app_name = 'blogs'
urlpatterns = [
path('',views.index,name='index'),
path('new_blog/',views.new_blog,name='new_blog'),
path('edit_blog/<int:blog_id>/',views.edit_blog,name='edit_blog'),
]
13 base页面
Blog -> blogs -> templates -> blogs ->base.html
<p>
<a href="{% url 'blogs:index' %}">BLOG</a>
<a href="{% url 'blogs:new_blog' %}">发布新博客</a>
</p>
{% block content %}{% endblock content%}
14 index页面
Blog -> blogs -> templates -> blogs -> index.html
{%extends 'blogs/base.html'%}
{%block content%}
<p><h1>Blog 主页</h1></p>
<ul>
{%for blogpost in blogposts%}
<li>
<p>{{blogpost.date_added|date:'Y m d,H:i'}}</p>
</li>
<p>{{blogpost.title}}</p>
<p>{{blogpost|linebreaks}}</p>
<a href="{%url 'blogs:edit_blog' blogpost.id%}">修改博客</a>
{%empty%}
<p>Add</p>
{%endfor%}
</ul>
{%endblock content%}
15 new_blog页面
Blog -> blogs -> templates -> blogs -> new_blog.html
{%extends 'blogs/base.html'%}
{%block content%}
<p>发布一篇新博客:</p>
<form action="{% url 'blogs:new_blog'%}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">发布</button>
</form>
{%endblock content%}
16 edit_blog页面
Blog -> blogs -> templates -> blogs -> edit_blog.html
{%extends 'blogs/base.html'%}
{%block content%}
<p>{{ title }}</p>
<form action="{% url 'blogs:edit_blog' blogpost.id %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">修改</button>
</form>
{%endblock content%}
19-2 博客账户 在为完成练习19-1而开发的项目Blog中,添加用户身份验证和注册系统。向已登录的用户显示其用户名,向未注册的用户显示到注册页面的链接。
17 创建应用程序users
(ll_env) Blog>python manage.py startapp users
settings.py
INSTALLED_APPS = [
'blogs',
'users',
...
]
Blog -> Blog -> urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('user/',include('users.urls')),
path('',include(('blogs.urls','blogs'),namespace='blogs')),
]
Blog -> users-> 创建urls.py
from django.urls import path,include
from . import views
app_name = 'users'
urlpatterns = [
path('',include('django.contrib.auth.urls')),
path('register/',views.register,name='register'),
]
users -> views.py
from django.shortcuts import render,redirect
from django.contrib.auth import login
from django.contrib.auth.forms import UserCreationForm
def register(request):
""" 注册新用户 """
if request.method != 'POST':
form = UserCreationForm()
else:
form = UserCreationForm(data=request.POST)
if form.is_valid():
new_user = form.save()
login(request,new_user)
return redirect('blogs:index')
context = {'form': form}
return render(request,'registration/register.html',context)
blogs -> templates -> blogs -> base.html
<p>
<a href="{% url 'blogs:index' %}">BLOG</a>
<a href="{% url 'blogs:new_blog' %}">发布新博客</a>
{% if user.is_authenticated %}
Hello,{{ user.username }}.
<a href="{% url 'users:logout' %}">注销</a>
{% else %}
<a href="{% url 'users:register' %}">注册</a> -
<a href="{% url 'users:login' %}">登录</a>
{% endif %}
</p>
{% block content %}{% endblock content%}
18 登录页面
login.html
{% extends "blogs/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">登录</button>
<input type="hidden" name="next" value="{% url 'blogs:index' %}" />
</form>
{% endblock content %}
19 注销页面
logged_out.html
{% extends "blogs/base.html" %}
{% block content %}
<p>You have been logged out.Thank you for visiting!</p>
{% endblock content %}
20 注册页面
register.html
{% extends "blogs/base.html" %}
{% block content %}
<form method="post" action="{% url 'users:register' %}">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">注册</button>
<input type="hidden" name="next" value="{% url 'blogs:index' %}" />
</form>
{% endblock content %}
19-3 重构 在views.py中,我们在两个地方核实了主题关联到的用户为当前登录的用户。请将执行这种检查的代码放在函数check_topic_owner()中,并在这两个地方调用该函数。 blogs -> views.py,此处因为没有写更多功能,所以只调用一次
from django.shortcuts import render,redirect
from .models import BlogPost
from .forms import BlogPostForm,BlogForm
def index(request):
""" 主页,显示所有 """
blogposts = BlogPost.objects.order_by('-date_added')
context = {'blogposts':blogposts}
return render(request,'blogs/index.html',context)
def new_blog(request):
""" 添加新博客 """
if request.method != 'POST':
form = BlogPostForm()
else:
form = BlogPostForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:index')
context = {'form':form}
return render(request,'blogs/new_blog.html',context)
def edit_blog(request,blog_id):
""" 编辑博客 """
blogpost = BlogPost.objects.get(id=blog_id)
title = blogpost.title
check_topic_owner(blogpost,request)
if request.method != 'POST':
form = BlogPostForm(instance=blogpost)
else:
form = BlogPostForm(instance=blogpost,data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:index')
context = {'blogpost':blogpost,'title':title,'form':form}
return render(request,'blogs/edit_blog.html',context)
def check_topic_owner(blogpost,request):
if blogpost.owner != request.user:
raise Http404
19-4 保护页面new_entry 一个用户可在另一个用户的学习笔记中添加条目,方法是在URL中指定属于另一个用户的主题的ID。为防范这种攻击,请在保存新条目之前,核实它所属的主题归属于当前用户。
注意:先把19-5的代码写完才能运行19-4的步骤
- 修改模型 BlogPost
blogs -> models.py
from django.db import models
from django.contrib.auth.models import User
class BlogPost(models.Model):
""" 博客页面 """
title = models.CharField(max_length=200)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User,on_delete=models.CASCADE)
class Meta:
verbose_name_plural = 'BlogPost'
def __str__(self):
""" 返回模型字符串表示 """
return self.text
(ll_env) D:\Blog>python manage.py shell
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.auth.models import User
>>> User.objects.all()
<QuerySet [<User: ll_admin>, <User: aaa>]>
>>> for user in User.objects.all():
... print(user.username,user.id)
...
ll_admin 1
>>>
(ll_env) D:\Blog>python manage.py makemigrations blogs
It is impossible to add a non-nullable field 'owner' to blogpost without specifying a default. This is because 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 manually define a default value in models.py.
Select an option: 1
Please enter the default value as valid Python.
The datetime and django.utils.timezone modules are available, so it is possible to provide e.g. timezone.now as a value.
Type 'exit' to exit this prompt
>>> 1
Migrations for 'blogs':
blogs\migrations\0002_blogpost_owner.py
- Add field owner to blogpost
(ll_env) D:\Sublime Text\python_test\Blog>
- 执行迁移
(ll_env) D:\Sublime Text\python_test\Blog>python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blogs, contenttypes, sessions
Running migrations:
Applying blogs.0002_blogpost_owner... OK
(ll_env) D:\Sublime Text\python_test\Blog>
- 验证迁移符合预期
(ll_env) D:\Sublime Text\python_test\Blog>python manage.py shell
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from blogs.models import BlogPost
>>> for blogpost in BlogPost.objects.all():
... print(blogpost,blogpost.owner)
...
123 ll_admin
In the morning,I'm having eaten egg.
OK!!! ll_admin
19-5 受保护的博客 在你创建的项目Blog中,确保每篇博文都与特定用户相关联。确保任何用户都可访问所有的博文,但只有已登录的用户能够发表博文和编辑既有博文。在让用户编辑博文的视图中,在处理表单前确认用户编辑的是其自己发表的博文。
- Blog -> settings.py 最后一行加上
LOGIN_URL = 'users:login'
from django.shortcuts import render,redirect
from django.contrib.auth.decorators import login_required
from .models import BlogPost
from .forms import BlogPostForm,BlogForm
def index(request):
""" 主页,显示所有 """
blogposts = BlogPost.objects.order_by('-date_added')
context = {'blogposts':blogposts}
return render(request,'blogs/index.html',context)
@login_required
def new_blog(request):
""" 添加新博客 """
if request.method != 'POST':
form = BlogPostForm()
else:
form = BlogPostForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:index')
context = {'form':form}
return render(request,'blogs/new_blog.html',context)
@login_required
def edit_blog(request,blog_id):
""" 编辑博客 """
blogpost = BlogPost.objects.get(id=blog_id)
title = blogpost.title
check_topic_owner(blogpost,request)
if request.method != 'POST':
form = BlogPostForm(instance=blogpost)
else:
form = BlogPostForm(instance=blogpost,data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:index')
context = {'blogpost':blogpost,'title':title,'form':form}
return render(request,'blogs/edit_blog.html',context)
def check_topic_owner(blogpost,request):
if blogpost.owner != request.user:
raise Http404
|