前言
上一篇文章做了对用户的注册登录,这篇文章就来做一下用户信息的扩展以及用户头像的上传.
感谢大神的教程: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>
写在最后
本文是个人的一些学习笔记,如有侵权,请及时联系我进行删除,谢谢大家.