Django个人博客搭建7-对用户登陆注册等需求的实现

目录
Django个人博客搭建1-创建Django项目和第一个App
Django个人博客搭建2-编写文章Model模型,View视图
Django个人博客搭建3-创建superuser并向数据库中添加数据并改写视图
Django个人博客搭建4-配置使用 Bootstrap 4 改写模板文件
Django个人博客搭建5-编写文章详情页面并支持markdown语法
Django个人博客搭建6-对文章进行增删查改
Django个人博客搭建7-对用户登陆注册等需求的实现
Django个人博客搭建8-优化文章模块
Django个人博客搭建9-增加文章评论模块
1. 用户的登陆和退出
首先命令行cd进manage.py同级目录下创建用户app

python manage.py startapp userprofile
Microsoft Windows [版本 10.0.17763.253]
(c) 2018 Microsoft Corporation。保留所有权利。

F:\Desktop\myblog>python manage.py startapp userprofile

F:\Desktop\myblog>

没有反馈,我们看一下目录
在这里插入图片描述
多了一个文件夹,这就是我们创建的用户app
用户登陆时需要填写用户名和密码等,因此需要用到Form表单类
userprofile中新建表单类文件froms.py:

from django import forms

from django.contrib.auth.models import User

class UserLoginForm(forms.Form):
   username = forms.CharField()
   password = forms.CharField()

接着编写视图,在userprofile/views.py中写视图函数:

from django.contrib.auth import authenticate, login
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
from userprofile.froms import UserLoginForm


def user_login(request):
   if request.method == "POST":
       user_login_form = UserLoginForm(data=request.POST)
       if user_login_form.is_valid():
           # .cleaned_data洗出合法数据
           data = user_login_form.cleaned_data
           # 检验账号、密码是否正确匹配数据库中的某个用户
           # 如果均匹配则返回这个user对象
           user = authenticate(username=data['username'], password=data['password'])
           # authenticate()方法验证用户名称和密码是否匹配,如果是,则将这个用户数据返回。
           if user:
               # 将用户数据保存在session中,实现的登陆动作
               login(request, user)
               return redirect("article:article_list")
           else:
               return HttpResponse("账号或密码输入有误,请重新输入")


       else:
           return HttpResponse("账号或密码输入不合法")


   elif request.method =="GET":
       user_login_form = UserLoginForm()
       context = {'from': user_login_form}
       return render(request, 'userprofile/losin.html', context)

   else:
       return HttpResponse("请使用GET或POST请求数据")

接着来编写登陆模板:/userprofile/login.html

{% extends "base.html" %}
{% load staticfiles %}
{% block title %}
   登陆
{% endblock title %}
{% block content %}
   <div class="container">
       <div class="row">
           <div class="col-12">
               <br>
               <form method="post" action=".">
                   {% csrf_token %}
                   <div class="form-group">
                       <label for="username">账号</label>
                       <input type="text" class="form-control" id="username" name="username">
                   </div>

                   <div class="form-group">
                       <label for="password">账号</label>
                       <input type="password" class="form-control" id="password" name="password">
                   </div>
                   <button type="submit" class="btn btn-primary">登陆</button>
               </form>
           </div>
       </div>
   </div>
{% endblock content %}

接下来在header.html中增加登陆按钮

<!-- 定义导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
 <div class="container">
   <!-- 导航栏商标 -->
   <a class="navbar-brand" href="#">我的博客</a>
   <!-- 导航入口 -->
   <div>
     <ul class="navbar-nav">
       <!-- 条目 -->
       <li class="nav-item">
         <a class="nav-link" href="{% url 'article:article_list' %}">文章</a>
       </li>
         <li class="nav-item">
             <a class="nav-link" href="{% url 'article:article_create' %}">写文章</a>
         </li>
         {% if user.is_authenticated %}
           <!-- 如果用户已经登录,则显示用户名下拉框 -->
           <li class="nav-item dropdown">
               <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                 {{ user.username }}
               </a>
               <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                 <a class="dropdown-item" href="#">退出登录</a>
               </div>
           </li>
           <!-- 如果用户未登录,则显示 “登录” -->
           {% else %}
               <li class="nav-item">
                   <a class="nav-link" href="{% url 'userprofile:login' %}">登录</a>
               </li>
           <!-- if 语句在这里结束 -->
         {% endif %}
     </ul>
   </div>
 </div>
</nav>

接着创建配置userprofile/urls.py

from django.urls import path
from . import views

app_name = 'userprofile'

urlpatterns=[
   path('login/', views.user_login, name='login'),
]

配置my_blog/urls.py:

path('userprofile/', include('userprofile.urls', namespace='userprofile')),

接着配置my_blog/settings.py中注册app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'article',
    'userprofile',
]

再接着运行服务器
在这里插入图片描述
可以看见登陆界面正常显示了,我们点击登陆:
在这里插入图片描述
可以看见文章列表右上角显示了用户名,
2. 用户的退出
接下来继续实现退出登陆逻辑, 在userprofile/views.py中新增user_logout函数:

from django.contrib.auth import authenticate, login, logout
# 用户退出
def user_logout(request):
    logout(request)
    return redirect("article:article_list")

配置userprofile/urls.py

path('logout/', views.user_logout, name='logout'),

接下来改动header.html中退出登陆的href链接指向

<!-- 定义导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container">
    <!-- 导航栏商标 -->
    <a class="navbar-brand" href="#">我的博客</a>
    <!-- 导航入口 -->
   ...
                <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                  <a class="dropdown-item" href="{% url "userprofile:logout" %}">退出登录</a>
                </div>
            </li>
           ...
</nav>

完成后点击退出登陆
在这里插入图片描述
可以看见成功退出登陆了
在这里插入图片描述

3. 用户的注册
首先在userprofile/views.py中增加用户注册函数

def user_register(request):
   if request.method == 'POST':
       user_register_form = UserRegisterForm(data=request.POST)
       if user_register_form.is_valid():
           new_user = user_register_form.save(commit=False)
           # 设置密码
           new_user.set_password(user_register_form.cleaned_data['password'])
           new_user.save()
           # 保存好数据后立即登录并返回博客列表页面
           login(request, new_user)
           return redirect("article:article_list")
       else:
           return HttpResponse("注册表单输入有误。请重新输入~")
   elif request.method == 'GET':
       user_register_form = UserRegisterForm()
       context = { 'form': user_register_form }
       return render(request, 'userprofile/register.html', context)
   else:
       return HttpResponse("请使用GET或POST请求数据")

接下来编写模板和 urls.py
userprofile/register.html

{% extends "base.html" %}
{% load staticfiles %}
{% block title %}
   注册
{% endblock title %}
{% block content %}
   <div class="container">
       <div class="row">
           <div class="col-12">
               <br>
           <form method="post" action=".">
               {% csrf_token %}
               <!-- 账号 -->
               <div class="form-group col-md-4">
                   <label for="username">昵称</label>
                   <input type="text" class="form-control" id="username" name="username" required>
               </div>
               <!-- 邮箱 -->
               <div class="form-group col-md-4">
                   <label for="email">Email</label>
                   <input type="text" class="form-control" id="email" name="email">
               </div>
               <!-- 密码 -->
               <div class="form-group col-md-4">
                   <label for="password">设置密码</label>
                   <input type="password" class="form-control" id="password" name="password" required>
               </div>
               <!-- 确认密码 -->
               <div class="form-group col-md-4">
                   <label for="password2">确认密码</label>
                   <input type="password" class="form-control" id="password2" name="password2" required>
               </div>
               <!-- 提交按钮 -->
               <button type="submit" class="btn btn-primary">提交</button>
           </form>
{#                #}
           </div>
       </div>
   </div>
{% endblock content %}

接下来在登陆界面中增加注册入口:
login.html:

{% extends "base.html" %}
{% load staticfiles %}
{% block title %}
   登陆
{% endblock title %}
{% block content %}
   <div class="container">
       <div class="row">
           <div class="col-12">

               <br>
               <form method="post" action=".">
                   {% csrf_token %}
                   <div class="form-group">
                       <label for="username">账号</label>
                       <input type="text" class="form-control" id="username" name="username">
                   </div>

                   <div class="form-group">
                       <label for="password">账号</label>
                       <input type="password" class="form-control" id="password" name="password">
                   </div>
                   <button type="submit" class="btn btn-primary">登陆</button>

               </form>
               
               <br>
               <!-- 增加这里 -->
               <h5>还没有账号?</h5>
               <h5>点击<a href='{% url "userprofile:register" %}'>注册账号</a>加入我们吧!</h5>
               <br>
                <!-- 增加这里 -->
           </div>
       </div>
   </div>
{% endblock content %}

最后在userprofile/urls.py中新增注册路由:

path('register/', views.user_register, name="register"),

然后我们重启服务器打开登陆界面:
在这里插入图片描述
点击注册账号
在这里插入图片描述接着填写注册信息
点击提交
在这里插入图片描述
可以看见成功注册并且自动登陆了

4. 用户的删除
编写userprofile/views.py

from django.contrib.auth.models import User
# 引入验证登录的装饰器
from django.contrib.auth.decorators import login_required
...
@login_required(login_url='/userprofile/login/')
def user_delete(request, id):
    user = User.objects.get(id=id)
    # 验证登录用户、待删除用户是否相同
    if request.user == user:
        #退出登录,删除数据并返回博客列表
        logout(request)
        user.delete()
        return redirect("article:article_list")
    else:
        return HttpResponse("你没有删除操作的权限。")

@login_required是一个装饰器,就是 @login_required要求调用user_delete()函数时,用户必须登陆如果不登陆则不执行函数并且将重定向到**/userprofile/login/**地址去。
关于@login_required的详细用法。

然后改写templates/header.html,新增删除用户入口,并且添加弹窗组件的代码

...
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
                  <a class="dropdown-item" href="{% url "userprofile:logout" %}">退出登录</a>
                  <a class="dropdown-item" href="#" onclick="user_delete()">删除用户</a>
                </div>
...
</nav>

{% if user.is_authenticated %}
    <script>
        function user_delete() {
            layer.open({
                title:"确认删除",
                content:"确认删除用户资料吗?",
                yes: function (index, layero) {
                    location.href='{% url "userprofile:delete" user.id %}'
                }
            })
        }
    </script>
{% endif %}

注意到**user_delete()**函数是用if模板语句包裹起来的。因为用户未登录时页面对象中是没有user.id属性的,但是函数中却又包含了 user.id,Django在解析模板时就会报错。if语句确保了只有在用户登录时才对这段JavaScript代码进行解析,回避了这个问题。
最后修改userprofile/urls.py:

path('delete/<int:id>/', views.user_delete, name='delete'),

运行服务器看一下效果:
在这里插入图片描述点击删除用户:
在这里插入图片描述
点击确定:
在这里插入图片描述
用户已经退出登陆了
检查数据库的用户表,发现admin用户信息已经没有了
在这里插入图片描述
就证明用户信息已经删除了

5. 重置用户密码
业务流程分析如下:

向用户邮箱发送包含重置密码地址的邮件。邮件的地址需要动态生成,防止不怀好意的用户从中捣乱;
向网站用户展示一条发送邮件成功的信息;
用户点击邮箱中的地址后,转入重置密码的页面;
向用户展示一条重置成功的信息。
Django已经为我们写好了重置密码功能了:Django验证系统
首先我们需要下载一个第三方库:Django-password-reset:
打开cmd输入:

pip install -U django-password-reset

-U参数的作用

第三方库也是一个app,因此应该在settings.py中注册app,

INSTALLED_APPS = [
    ...
    'userprofile',
    # ------
    'password_reset',
    # ------
]

在my_glog/urls.py中添加app的地址:

path('password-reset/', include('password_reset.urls')),

修改 userporfile/login.html,提供重置密码的入口:

<button type="submit" class="btn btn-primary">登陆</button>

                </form>
                <!-- 新增这里 -->
                <br>
                <h5>
                    忘记密码了?
                </h5>
                <h5>
                    点击<a href="{% url "password_reset_recover" %}">这里</a>重置密码
                </h5>
                <!-- 新增这里结束 -->
                <br>
                <h5>还没有账号?</h5>
                <h5>点击<a href='{% url "userprofile:register" %}'>注册账号</a>加入我们吧!</h5>
                <br>
            </div>
        </div>
    </div>
{% endblock content %}

接下来配置发件邮箱的账号密码以及发送邮件的端口,发件人等信息
配置my_blog/settings.py



# SMTP服务器
EMAIL_HOST = 'smtp.qq.com'
# 邮箱名
EMAIL_HOST_USER = '[email protected]'
# 授权码
EMAIL_HOST_PASSWORD = '*********'
# 发送邮件的端口
EMAIL_PORT = 25
# 是否使用 TLS
EMAIL_USE_TLS = True
# 默认的发件人
DEFAULT_FROM_EMAIL = 'wei.guo <[email protected]>'

**EMAIL_HOST_PASSWORD **应该输入的的邮箱的授权码
点击重置密码输入邮箱后:
在这里插入图片描述
打开邮箱可以看见邮件
在这里插入图片描述
我们点击链接:
在这里插入图片描述
输入新密码提交后重新登陆
在这里插入图片描述
可以看见重新登陆了

6. 扩展用户信息
编写userprofile/models.py

from django.contrib.auth.models import User
from django.db import models

# Create your models here.
from django.db.models.signals import post_save
from django.dispatch import receiver


class Profile(models.Model):
   # 与User模型构成一对一的关系
   user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
   # 电话号码字段:
   phone = models.CharField(max_length=20, blank=True)
   # 头像
   avatar = models.ImageField(upload_to='avatar/%Y%m%d/', blank=True)
   # 个人简介
   bio = models.TextField(max_length=500, blank=True)

   def __str__(self):
       return 'user{}'.format(self.user.username)


# 信号接受函数,每当新建User实例时自动调用
@receiver(post_save, sender=User)
def create_user_profile(sender,instance, created, **kwargs):
   if created:
       Profile.objects.create(user=instance)



# 信号更新函数,每当更新User实例时自动调用
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
   instance.profile.save()

每个Profile模型对应唯一的一个User模型,形成了对User的外接扩展,因此你可以在Profile添加任何想要的字段。这种方法的好处是不需要对User进行任何改动,从而拥有完全自定义的数据表。模型本身没有什么新的知识,比较神奇的是用到的信号机制。

Django包含一个“信号调度程序”,它可以在框架中的某些位置发生操作时,通知其他应用程序。简而言之,信号允许某些发送者通知一组接收器已经发生了某个动作。当许多代码可能对同一事件感兴趣时,信号就特别有用。

这里引入的post_save就是一个内置信号,它可以在模型调用save()方法后发出信号。

有了信号之后还需要定义接收器,告诉Django应该把信号发给谁。装饰器receiver就起到接收器的作用。每当User有更新时,就发送一个信号启动post_save相关的函数。

通过信号的传递,实现了每当User创建/更新时,Profile也会自动的创建/更新。

接下来重建数据库, 由于avatar是图像字段,需要安装pillow来支持
在命令行输入:

pip install Pillow

然后迁移数据

F:\Desktop\myblog>python manage.py makemigrations
System check identified some issues:

WARNINGS:
article.ArticlePost.created: (fields.W161) Fixed default value provided.
        HINT: It seems you set a fixed date / time / datetime value as default for this field. This may not be what you want. If you want to have the current date as default, use `django.utils.timezone.now`
Migrations for 'article':
  article\migrations\0002_auto_20190209_1224.py
    - Alter field created on articlepost
Migrations for 'userprofile':
  userprofile\migrations\0001_initial.py
    - Create model Profile

F:\Desktop\myblog>
F:\Desktop\myblog>python manage.py migrate
System check identified some issues:

WARNINGS:
article.ArticlePost.created: (fields.W161) Fixed default value provided.
        HINT: It seems you set a fixed date / time / datetime value as default for this field. This may not be what you want. If you want to have the current date as default, use `django.utils.timezone.now`
Operations to perform:
  Apply all migrations: admin, article, auth, contenttypes, sessions, userprofile
Running migrations:
  Applying article.0002_auto_20190209_1224... OK
  Applying userprofile.0001_initial... OK

F:\Desktop\myblog>

成功迁移数据库了.
如果你试图登陆用户,会得到报错,这是因为之前创建的User数据都没有对应的Profile模型,违背了现有的模型。一种解决办法就是干脆删除旧的数据,因此就需要用到Django的shell命令。
在虚拟环境中输入python manage.py shell:

F:\Desktop\myblog>python manage.py shell
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]:

接下来输入:

In [1]: from django.contrib.auth.models import User

In [2]: User.objects.all().delete()
Out[2]:
(13,
 {'admin.LogEntry': 8,
  'auth.User_groups': 0,
  'auth.User_user_permissions': 0,
  'article.ArticlePost': 3,
  'userprofile.Profile': 0,
  'auth.User': 2})

In [3]:

注意因为前面写的article模型中,与User的外键也采用了models.CASCADE级联删除模式,因此随着User的删除,相关的文章也一并删除了。

输入exit()退出shell,输入指令python manage.py createsuperuser,重新创建管理员账户。
接下来为Profile模型新建一个表单类userprofile/forms.py去编辑它的内容:

from .models import Profile
...
class ProfileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ('phone', 'avatar', 'bio')

接下来在userprofile/views.py中填写处理用户信息的函数profile_edit

from .forms import ProfileForm
from .models import Profile
...
# 编辑用户信息
@login_required(login_url='/userprofile/login/')
def profile_edit(request, id):
    user = User.objects.get(id=id)
    # user_id是OneToOneField自动生成的字段
    profile = Profile.objects.get(user_id=id)

    if request.method == 'POST':
        # 验证修改数据者是否为本人
        if request.user != user:
            return HttpResponse("你没有权限修改次用户信息")

        profile_form = ProfileForm(data=request.POST)
        if profile_form.is_valid():
            # 取得清洗后的合法数据
            profile_cleaned_data = profile_form.cleaned_data
            profile.phone = profile_cleaned_data['phone']
            profile.bio = profile_cleaned_data['bio']
            profile.save()
            return redirect("userprofile:edit", id=id)

        else:
            return HttpResponse("注册表单输入有误,请重新输入")

    elif request.method == "GET":
        profile_form = ProfileForm()
        context = {'profile_form': profile_form, 'profile': profile, 'user': user}
        return render(request, 'userprofile/edit.html', context)
    else:
        return HttpResponse("请使用GET或POST请求数据")

接下来在templates/userprofile中新建模板文件edit.html

{% extends "base.html" %}
{% load staticfiles %}
{% block title %}
用户信息
{% endblock title %}
{% block content %}
    <div class="container">
        <div class="row">
            <div class="col-12">
                <br>
                <div class="col-md-4">用户名{{ user.username }}</div>\
                <br>
                <form method="post" action=".">
                    {% csrf_token %}
                    <div class="form-group col-md-4">
                        <label for="phone">电话</label>
                        <input type="text" class="form-control" id="phone" name="phone" value="{{ profile.phone }}">
                    </div>

                    <div class="form-group col-md-4">
                        <label for="bio">简介</label>
                        <textarea type="text" class="form-control" id="bio" name="bio" rows="12">{{ profile.bio }}</textarea>
                    </div>
                    <button type="submit" class="btn btn-primary">提交</button>
                </form>
            </div>
        </div>
    </div>
{% endblock content %}

最后一步配置userprofile/urls.py

path('edit/<int:id>/', views.profile_edit, name='edit'),

在浏览器地址栏输入http://127.0.0.1:8000/userprofile/edit/4/(找不到用户id可以新建一个用户)
在这里插入图片描述
接下来给人个信息添加一个入口
修改templates/header.html

 <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                  <a class="dropdown-item" href="{% url "userprofile:edit" user.id %}">个人信息</a>
                  <a class="dropdown-item" href="#" onclick="user_delete()">删除用户</a>
                  <a class="dropdown-item" href="{% url "userprofile:logout" %}">退出登录</a>
 </div>

接下来修改article/views.py视图, 前面为了简单就把所有文章的作者全部绑定为用户id为1的用户,还没有对用户登陆状态进行检查:

...
from django.contrib.auth.decorators import login_required 
...
# 检查登陆
@login_required(login_url='/userprofile/login')
def article_create(request):
    ...
            new_article = article_post_form.save(commit=False)
            new_article.author = User.objects.get(id=request.user.id)
            new_article.save()
           ...

重启服务器:
在这里插入图片描述
点击用户信息:
在这里插入图片描述
修改用户信息就完成了
我们应该也在用户删除更新文章时同样进行登陆状态检查,在views.py中每个函数前面加上:**#
@login_required(login_url=’/userprofile/login’)**即可

7. 配置Admin
在admin中将User profile合并为一张完整的表格:修改userprofile/admin.py

from django.contrib import admin

# Register your models here.
from userprofile.models import Profile
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
class ProfileInline(admin.StackedInline):
   model = Profile
   can_delete = False
   verbose_name_plural = 'UserProfile'


# 将Profile关联到User中
class UserAdmin(BaseUserAdmin):
   inlines = (ProfileInline, )


# 重新注册User
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

打开admin中User表,可以看见Profile的数据已经堆叠在底部了
在这里插入图片描述

8. 上传用户头像
图片属于一种媒体文件,需要设置一个统一的目录,便于集中存储和访问
修改/my_blog/settings.py,在最后加上:


# MEDIA_ROOT和MEDIA_URL是用户上传文件保存、访问的位置
MEDIAL_URL = '/media/'
MEDIAL_ROOT = os.path.join(BASE_DIR, 'media/')

在my_blog/urls.py添加下面的语句:

...
from django.conf.urls.static import static
from django.conf import settings

...

urlpatterns += static(settings.MEDIAL_URL, document_root = settings.MEDIAL_ROOT)

为以后上传的图片配置好了URL路径
编写MTV修改视图使之能够对图片进行处理,修改userprofile/views.py:

# 编辑用户信息
@login_required(login_url='/userprofile/login/')
def profile_edit(request, id):
    ...
        profile_form = ProfileForm(request.POST, request.FILES)
        if profile_form.is_valid():
            # 取得清洗后的合法数据
            ...
            profile.bio = profile_cleaned_data['bio']
            if 'avatar' in request.FILES:
                profile.avatar = profile_cleaned_data['avatar']
            profile.save()
...

修改模板文件,添加代码显示,处理用户的头像:

{% extends "base.html" %}
{% load staticfiles %}
{% block title %}
用户信息
{% endblock title %}
{% block content %}
    <div class="container">
        <div class="row">
            <div class="col-12">
                <br>
                <div class="col-md-4">用户名{{ user.username }}</div>
                <br>
                {% if profile.avatar %}
                    <div class="col-md-4">头像</div>
                    <img src="{{ profile.avatar.url }}" style="max-width: 20%; border-radius:15%" class="col-md-4">
                {% else %}
                    <h5 class="col-md-4">暂无头像</h5>
                {% endif %}

                <br>
                <br>

                <form method="post" action="." enctype="multipart/form-data">
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="avatar">上传头像</label>
                        <input type="file" class="form-control-file" name="avatar" id="avatar">
                    </div>
...
{% endblock content %}

模板语法{% if … %}判断用户是否上传头像。
标签用于显示图片内容;在style属性中规定了图片的最大宽度并带有一点的圆角。
注意,表单必须设置enctype="multipart/form-data"属性,才能够正确上传图片等文件。
添加<input type=“file” …>标签用于上传图片。

重启服务器点击选择文件,然后点击提交:
在这里插入图片描述
头像已经成功上传了,查看目录发现图片已经在media/avatar中了

目录
Django个人博客搭建1-创建Django项目和第一个App
Django个人博客搭建2-编写文章Model模型,View视图
Django个人博客搭建3-创建superuser并向数据库中添加数据并改写视图
Django个人博客搭建4-配置使用 Bootstrap 4 改写模板文件
Django个人博客搭建5-编写文章详情页面并支持markdown语法
Django个人博客搭建6-对文章进行增删查改
Django个人博客搭建7-对用户登陆注册等需求的实现
Django个人博客搭建8-优化文章模块
Django个人博客搭建9-增加文章评论模块

猜你喜欢

转载自blog.csdn.net/weixin_43249914/article/details/86772432