初始方法一:
疑虑:model使用upload_to自定义路径方法失效,指定路径也失效。最后以Views中指定MEDIA_URL和MEDIA_ROOT做拼接,并且自行判断并建立文件夹,手动chunk保存。不完美的解决。
model: 看avatar字段的upload_to
import uuid
from django.db import models
from django.contrib.auth.models import AbstractUser
class SysUser(AbstractUser):
"""
用户扩展表,用于替换auth的user类,并添加头像和昵称。
"""
uid = models.BigAutoField(primary_key=True)
nickname = models.CharField(max_length=32, verbose_name="昵称")
# 使用 models.ImageField 需要安装 Pillow ,cmd命令:pip install Pillow
avatar = models.ImageField(verbose_name="头像", upload_to="avatar11")
class Meta:
# 数据库显示的表名
db_table = "sys_user"
form:
from django.forms import Form
from django.forms import widgets
from django.forms import fields
class EditUserForm(Form):
"""..."""
e_avatar = fields.ImageField(
required=False,
widget=widgets.FileInput(attrs={"hidden": "hidden"})
)
"""..."""
Views:
def upload_user_avatar(request):
"""用户上传头像操作"""
if request.method == "POST":
user_id = request.user.uid # 只是当前登录用户
ret = {'state': True, 'error': None, 'data': None}
# user_id = request.POST.get("uid", None)
avatar = request.FILES.get("avatar", None)
# 如果文件夹不存在,则建立
dir_path = os.path.join(MEDIA_ROOT, "users", "{}_{}".format(request.user.uid, request.user.username), "avatar")
if not os.path.exists(os.path.join(dir_path)):
os.makedirs(os.path.join(dir_path))
avatar_path = os.path.join(dir_path, avatar.name)
try:
with open(avatar_path, 'wb+') as img:
for chunk in avatar.chunks():
img.write(chunk)
print("文件接收成功")
img_url = os.path.join("users", "{}_{}".format(request.user.uid, request.user.username), "avatar")
SysUser.objects.filter(uid=user_id).update(avatar=img_url)
ret["data"] = "头像更新成功"
except Exception as e:
ret["state"] = False
ret["data"] = "头像更新失败,请确认图片格式"
print(e)
return HttpResponse(json.dumps(ret))
html:
<div class="form-group text-center">
<label>
{# request.user.avatar 是读取实例的avatar字段,也就是不加MEDIA_URL前缀的 #}
{# request.user.avatar.url 是读取实例的avatar字段了,并且添加MEDIA_URL作为前缀的 #}
<img id="avatar-img" src="{{ request.user.avatar.url }}" width="200px" height="200px">
{{ edit_user_form.e_avatar }}
</label>
</div>
- SysUser.objects.filter(uid=user_id).update(avatar=img_url)
- 这句话执行结束后,只保存文件名,无法使用upload_to指定的路径。
- 页面加载时,直接使用拼接后路径。
初始方法二:
TMD,一个保存方式的问题,竟然能啰啰嗦嗦这么久。以后以此方法操作。
model:不变
views:
def upload_user_avatar(request):
"""用户上传头像操作"""
if request.method == "POST":
user_id = request.user.uid # 只是当前登录用户
ret = {'state': True, 'error': None, 'data': None}
avatar = request.FILES.get("avatar", None)
try:
user_obj = SysUser.objects.filter(uid=user_id).first()
user_obj.avatar = avatar
user_obj.save()
ret["data"] = "头像更新成功"
except Exception as e:
ret["state"] = False
ret["data"] = "头像更新失败,请确认图片格式"
print(e)
return HttpResponse(json.dumps(ret))
- user_obj.save()
- 使用这种保存方式的好处:
- 会自动保存文件路径到数据库
- 会自动保存文件到指定路径
- 必要时会自动创建目录。
目前最终方法:
model:
import uuid
import os
from django.db import models
from django.contrib.auth.models import AbstractUser
"""
python-cmd : makemigrations
python-cmd : migrate
"""
def user_directory_path(instance, filename):
"""用户上传文件时的自定义操作"""
ext = filename.split('.')[-1] # 获取上传的文件名的后缀
filename = '{0}.{1}'.format(uuid.uuid4().hex[:8], ext) # 修改文件名,谨防重名。(文档说重名后,会自动增加后缀)
sub_folder = 'avatar'
# 日后如果对上传文件的类型进行归档时可以COPY这个做参考。
# if ext.lower() in ["jpg", "png", "gif", "ico"]:
# sub_folder = "avatar"
# if ext.lower() in ["pdf", "docx"]:
# sub_folder = "document"
return os.path.join("users", "%s-%s" % (instance.user.uid, instance.user.username), sub_folder, filename)
class SysUser(AbstractUser):
"""
用户扩展表,用于替换auth的user类,并添加头像和昵称。
"""
uid = models.BigAutoField(primary_key=True)
nickname = models.CharField(max_length=32, verbose_name="昵称")
# 使用 models.ImageField 需要安装 Pillow ,cmd命令:pip install Pillow
avatar = models.ImageField(verbose_name="头像", upload_to=user_directory_path)
class Meta:
# 数据库显示的表名
db_table = "sys_user"
form:
from django.forms import Form
from django.forms import widgets
from django.forms import fields
class EditUserForm(Form):
"""..."""
e_avatar = fields.ImageField(
required=False,
widget=widgets.FileInput(attrs={"hidden": "hidden"})
)
"""页面操作该元素时,使用的ajax方式,所以仅对此项进行操作。未使用EditUserForm做验证等操作。"""
"""..."""
views:
def upload_user_avatar(request):
"""用户上传头像操作"""
if request.method == "POST":
user_id = request.user.uid # 只是当前登录用户
ret = {'state': True, 'error': None, 'data': None}
# user_id = request.POST.get("uid", None) # 如果以后有需求修改其他人的信息时,再从页面获取。
avatar = request.FILES.get("avatar", None)
try:
user_obj = SysUser.objects.filter(uid=user_id).first()
user_obj.avatar = avatar
user_obj.save()
ret["data"] = "头像更新成功"
except Exception as e:
ret["state"] = False
ret["data"] = "头像更新失败,请确认图片格式"
print(e)
return HttpResponse(json.dumps(ret))
html:
<div class="form-group text-center">
<label>
{# request.user.avatar 是读取实例的avatar字段,也就是不加MEDIA_URL前缀的 #}
{# request.user.avatar.url 是读取实例的avatar字段了,并且添加MEDIA_URL作为前缀的 #}
<img id="avatar-img" src="{{ request.user.avatar.url }}" width="200px" height="200px">
{{ edit_user_form.e_avatar }}
</label>
</div>
JS:
// 上传文件按钮(label里的图片)点击事件
$('#id_e_avatar').on('change',function () {
// 文件上传之前在本地加载预览。
{
// 获取用户最后一次选择的图片
var choose_file=$(this)[0].files[0];
// 创建一个新的FileReader对象,用来读取文件信息
var reader=new FileReader();
// 读取用户上传的图片的路径
reader.readAsDataURL(choose_file);
// 读取完毕之后,将图片的src属性修改成用户上传的图片的本地路径
reader.onload=function () {
$("#avatar-img").attr("src",reader.result)
}
}
// 使用FormData格式,将文件上传至服务器
formdata = new FormData();
//formdata.append('uid',$('#id_e_id').val());
formdata.append("avatar",$("#id_e_avatar")[0].files[0]);
$.ajax({
type: "POST",
url: "/account/upload_user_avatar",
data:formdata,
processData:false, //上传文件时需要如此设置1
contentType:false, //上传文件时需要如此设置2
success:function (data) {
data = JSON.parse(data);
console.log(data);
alert(data.data) // 偷懒了,哈哈
}
});
});
media_url映射:
from django.urls import path, re_path, include
from django.views.generic.base import RedirectView
from django.views.static import serve # 映射media_root路径,可以通过浏览器访问的操作-1
from django.contrib.auth.decorators import login_required
from WXH_DJANGOWEB import settings
from utils.required import required
urlpatterns = [
path('account/', include("account.urls")),
path('blog/', include("blog.urls")),
re_path(r"^media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT, })
# 映射media_url,可以通过浏览器访问的操作-2
]
至此,记录完毕。回家吃饭。坑爹的保存方式…… 愿同志们绕坑而行,用不遇坑。