Django做一个简单的博客系统(7)----扩展用户信息,上传用户头像

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_42567323/article/details/102718404
前言

    上一篇文章做了对用户的注册登录,这篇文章就来做一下用户信息的扩展以及用户头像的上传.
    感谢大神的教程:Django搭建个人博客.

一.扩展用户信息

    在上一篇文章做用户登录注册的时候,我们用的是Django自带的User模型,但是User模型的字段毕竟是少,如果有用户其他的比较重要的信息的话,比如用户头像或者用户电话号码等信息,那么User就不能胜任了.

    User模型自带的字段如下:

mysql> desc auth_user;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| password     | varchar(128) | NO   |     | NULL    |                |
| last_login   | datetime(6)  | YES  |     | NULL    |                |
| is_superuser | tinyint(1)   | NO   |     | NULL    |                |
| username     | varchar(150) | NO   | UNI | NULL    |                |
| first_name   | varchar(30)  | NO   |     | NULL    |                |
| last_name    | varchar(150) | NO   |     | NULL    |                |
| email        | varchar(254) | NO   |     | NULL    |                |
| is_staff     | tinyint(1)   | NO   |     | NULL    |                |
| is_active    | tinyint(1)   | NO   |     | NULL    |                |
| date_joined  | datetime(6)  | NO   |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+
11 rows in set (0.00 sec)

    可以看到,User中并没有电话号码,头像等字段名.
    对于这种情况的解决方法有很多,我们可以自己重新写一个user模型,然后写上所有的我们需要的字段名,当然也可以对User模型进行扩展.

1.扩展User模型

    对于User模型的扩展,又有很多不同的方法,大多数情况下,使用模型一对一链接的方法比较合适.
    编写models.py:
userprofile/models.py:

from django.db import models
from django.contrib.auth.models import User
# 引入内置信号
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进行任何改动,从而拥有完全自定义的数据表。

2.重建数据库

    由于我们对数据表做了修改,所以要对数据库进行重建:

python manager.py makemigrations
python manager.py migrate

    重建数据库后,还要做的一件事情就是将原有的数据删除,然后重新建立数据:

3.表单,视图,模板

    有了扩展的Profile模型后,需要新建一个表单类去编辑它的内容:
userprofile/forms.py

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

    然后在userprofile/views.py中写处理用户信息的视图函数:

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_cd = profile_form.cleaned_data
            profile.phone = profile_cd['phone']
            profile.bio = profile_cd['bio']
            profile.save()
            # 带参数的 redirect()
            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 %}
                <!-- phone -->
                <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>
                <!-- bio -->
                <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 %}

    配置url:
userprofile/url.py:

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

    修改header.html

<div class="dropdown-menu" aria-labelledby="navbarDropdown">
    <a class="dropdown-item" href='{% url "userprofile:edit" user.id %}'>个人信息</a>
    ...
</div>

    修改article视图:
/article/views.py

from django.contrib.auth.decorators import login_required
# 检查登录
@login_required(login_url='/userprofile/login/')
def article_create(request):
    # 指定目前登录的用户为作者
    new_article.author = User.objects.get(id=request.user.id)

    配置admin:
userprofile/admin.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from userprofile.models import Profile

# Register your models here.

# 定义一个行内admin
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)

二.上传用户头像

1.必要的设置

    图片属于一种媒体文件,它与静态文件类似,需要设置一个统一的目录,便于集中存储和访问。
    这类需要框架统一设置的参数,当然应该在settings.py中。在底部加上:

# 媒体文件地址
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

    当然了,要在博客下面新建一个media目录.

注:MEDIA_ROOT和MEDIA_URL是用户上传文件保存、访问的位置:
    在前面的Profile中我们设置了upload_to参数。有了这个参数,文件上传后将自动保存到项目根目录的media文件夹中。 os.path.join(MEDIA_ROOT, ‘media/’)指定了media文件夹的位置。
    MEDIA_URL代表用户通过URL来访问这个本地地址的URL。设置好这个参数后,用户就可以通过解析url,很方便的获取文件的地址。这样做的好处是避免的硬编码,让代码更容易维护。

    在与项目同名的目录下的urls.py中添加如下信息:
Personal_blog/urls.py

# 新引入的模块
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    ...
]

#添加这行
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

2.编写MVC

    修改视图:
userprofile/views.py:

@login_required(login_url='/userprofile/login/')
def profile_edit(request, id):
    ...

    # 修改本行
    # 上传的文件保存在 request.FILES 中,通过参数传递给表单类
    profile_form = ProfileForm(request.POST, request.FILES)

    if profile_form.is_valid():
        ...

        # 添加在 profile.bio = profile_cd['bio'] 后面
        # 如果 request.FILES 存在文件,则保存
        if 'avatar' in request.FILES:
            profile.avatar = profile_cd["avatar"]

        ...

    修改模板文件,添加代码显示、处理用户的头像:
/templates/userprofile/edit.html

{% 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 ... enctype="multipart/form-data">{% csrf_token %}
    <!-- avatar -->
    <div class="form-group">
        <label for="avatar">上传头像</label>
        <input type="file" class="form-control-file" name="avatar" id="avatar">
    </div>

写在最后

    本文是个人的一些学习笔记,如有侵权,请及时联系我进行删除,谢谢大家.

猜你喜欢

转载自blog.csdn.net/weixin_42567323/article/details/102718404