Django搭建个人博客:扩展用户信息

4536阅读 · 41评论 · 2018/12/06发布   前往评论

可能你已经发现了,Django自带的User模型非常实用,以至于我们没有写用户管理相关的任何模型。

但是自带的User毕竟可用的字段较少。比方说非常重要的电话号码、头像等都没有。解决的方法有很多,你可以不使用User,自己从零写用户模型;也可以对User模型进行扩展。

博客网站的用户信息并不复杂,因此扩展User就足够了。

扩展User模型

扩展User模型又有不同的方法。在大多数情况下,使用模型一对一链接的方法是比较适合的。

编写userprofile/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进行任何改动,从而拥有完全自定义的数据表。模型本身没有什么新的知识,比较神奇的是用到的信号机制

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

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

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

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

当然你也可以不使用信号来自动创建Profile表,而是采用手动方式实现。

为什么删除User表不需要信号?答案是两者的关系采用了models.CASCADE级联删除,已经带有关联删除的功能了。

avatar字段用来存放头像,暂且不管它,下一章讲解。

重建数据库

前面讲过,每次改动模型后都需要进行数据的迁移。由于avatar字段为图像字段,需要安装第三方库Pillow来支持:

(env) E:\django_project\my_blog> pip install Pillow

安装成功后,通过makemigrationsmigrate迁移数据:

(env) E:\django_project\my_blog>python manage.py makemigrations

Migrations for 'userprofile':
  userprofile\migrations\0001_initial.py
    - Create model Profile
(env) E:\django_project\my_blog>python manage.py migrate

Operations to perform:
  Apply all migrations: admin, article, auth, contenttypes, sessions, userprofile
Running migrations:
  Applying userprofile.0001_initial... OK

迁移好数据后,如果你试图登录用户,会得到报错。这是因为之前创建的User数据都没有对应的Profile模型,违背了现有的模型。一种解决办法就是干脆删除旧的数据,因此就需要用到Django的shell命令。

shell是Django提供的互动解释器,你可以在这个指令模式中试验代码是否能够正确执行,是相当方便的工具。

在虚拟环境中输入python manage.py shell就可以进入shell:

(env) E:\django_project\my_blog>python manage.py shell
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

看到>>>表示成功进入shell。

输入下面两行指令就可以轻松删除User数据库:

>>> from django.contrib.auth.models import User
>>> User.objects.all().delete()

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

输入exit()退出shell,输入指令python manage.py createsuperuser重新创建管理员账户

对新手来说,修改数据库经常会导致各种头疼的问题,比如说字段失效、新字段为null、赋值错误、外键链接出错等等,最终导致整个业务逻辑报错。因此我的建议是,在设计数据库时尽量考虑周全,避免频繁修改模型。

如果实在要修改,并且已经导致数据库混乱了,不妨删除掉/app/migrations/目录下最新的几个文件,清空相关数据库,重新迁移数据。

接下来编写MTV模式的剩余部分。

表单、视图和模板

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

userprofile/forms.py

...
# 引入 Profile 模型
from .models import Profile

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

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

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请求数据")

2019/05/13更新:实际上GET方法中不需要将profile_form这个表单对象传递到模板中去,因为模板中已经用Bootstrap写好了表单,所以profile_form并没有用到。感谢读者YipCyun指正。

业务逻辑与以前写的处理表单的视图非常相似(还记得吗),就不赘述了。

需要注意下面几个小地方:

  • user_id是外键自动生成的字段,用来表征两个数据表的关联。你可以在SQLiteStudio中查看它。
  • 留意redirect()是如何携带参数传递的。

然后就是新建模板文件/templates/userprofile/edit.html

/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 %}
  • 留意模板中如何分别调用UserProfile对象的
  • 行内文本通过value属性设置了初始值,而多行文本则直接设置{{ profile.bio }}

最后配置熟悉的userprofile/urls.py

userprofile/urls.py

...
urlpatterns = [
    ...
    # 用户信息
    path('edit/<int:id>/', views.profile_edit, name='edit'),
]

启动服务器,输入地址查看功能是否正常。注意旧的用户都删除了(id=1的用户已经没有了),这里的/<int:id>必须为新创建的用户的id

页面虽然简陋,但是方法是类似的。可以在这个基础上,扩展为一个美观、详细的用户信息页面。

当然最好再给个人信息添加一个入口。修改/templates/header.html

/templates/header.html

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

修改article视图

在前面新建article的章节中,由于没有用户管理的知识,存在一些问题:

  • new_article.author = User.objects.get(id=1)强行把作者指定为id=1的用户,这显然是不对的。
  • 没有对用户的登录状态进行检查。

因此稍加修改def article_create()

/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)
    ...

重启服务器,文章正确匹配到登录的用户,又可以愉快的写文章了。

实际上,删除文章article_delete()、更新文章article_update()都应该对用户身份进行检查。就请读者尝试修改吧。

配置admin

前面我们已经尝试过将article配置到admin后台,方法是非常简单的,直接在admin.py中写入admin.site.register(Profile)就可以了。但是这样写会导致UserProfile是两个分开的表,不方便不说,强迫症的你怎么能受得了。

我们希望能够在admin中将UserProfile合并为一张完整的表格。方法如下:

/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 .models import Profile

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

打开admin中的User表,发现Profile的数据已经堆叠在底部了:

本章勘误

2019/06/19 新增:感谢读者 @shenhanlin 对本问题的反馈。

本章中用到了信号来实现UserProfile的同步创建,但是也产生了一个BUG:在后台中创建User时如果填写了Profile任何内容,则系统报错且保存不成功;其他情况下均正常。

BUG产生原因:在后台中创建并保存User时调用了信号接收函数,创建了Profile表;但如果此时管理员填写了内联的Profile表,会导致此表也会被创建并保存。最终结果就是同时创建了两个具有相同UserProfile表,违背了”一对一“外键的原则。

解决的办法也不难。因为博客项目的需求较简单,其实没有必要用到信号。

修改model,把两个信号接收函数去除:

userprofile/models.py

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

class Profile(models.Model):
    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)

# 下面的信号接收函数全部注释掉
...

修改view,使得Profile表根据是否已经存在而动态的创建、获取:

userprofile/views.py

...

# 编辑用户信息
@login_required(login_url='/userprofile/login/')
def profile_edit(request, id):
    user = User.objects.get(id=id)

    # 旧代码
    # profile = Profile.objects.get(user_id=id)
    # 修改后的代码
    if Profile.objects.filter(user_id=id).exists():
        profile = Profile.objects.get(user_id=id)
    else:
        profile = Profile.objects.create(user=user)

    ...

即如果Profile已经存在了就获取它,如果不存在就创建一个新的。这样修改后应该就可以顺利的创建新用户了。

除了上面的方法,还有别的手段解决此问题:

  • 取消Profile在后台的内联,避免创建User的同时创建此表。
  • 覆写User表的save()方法,跳过后台的自动保存。(不推荐)

虽然博主做了不正确的示范,但是信号确实是很重要的概念,就当蜻蜓点水的介绍给大家。

总结

本章使用一对一链接的方式,扩展并更新了用户信息。读者可以根据自身需求,添加任何需要的字段内容。

下一章将学习对图片的简单处理。




本文作者: 杜赛
发布时间: 2018年12月06日 - 11:51
最后更新: 2019年06月19日 - 22:56
知识共享许可协议   转载请保留原文链接及作者


登录 后回复

共有41条评论

avatar
zpz 么么哒! 3

博主您好,我先请问为什么我在虚拟环境里安装的包,它们都存储到了python的环境中去了surprise

7个月前 回复


avatar
杜赛 [博主] zpz 么么哒! 3

您确定是进入到虚拟环境了吗?

进去虚拟环境后,提示符开头会有一对括号,包着环境名称

7个月前 回复


avatar
YipCyun 么么哒! 3

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请求数据")

您好,有一个疑问想请教一下,就是这里其实不实例化一个ProfileForm也是ok的,像这样:

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

那么传递profile_form到模板中的作用是什么呢 或者这里实例化一个ProfileForm并传递的作用是什么呢,谢谢老师!

6个月前 回复


avatar
杜赛 [博主] YipCyun 么么哒! 3

同学你看得很仔细啊laugh

你说得对,这里确实是我疏忽了,profile_form传递到模板中的作用是提供一个未绑定数据的空白表单,方便在模板中使用。教程中是用Bootstrap写的表单,profile_form实际并没有用到。

感谢提醒,一会儿我在文章中注明一下。

6个月前 回复


avatar
pidada 杜赛 [博主] 么么哒! 3

博主,profile_form不能传递到模板中吧;我带上了这个参数报错了:

Exception Value:	
ModelForm has no model class specified.

然后我去掉它,正常运行了

6个月前 回复


avatar
杜赛 [博主] pidada 么么哒! 4

可以传,只是没有用到而已。

你这个报错是别的问题,比如表单类写得不对。

6个月前 回复


avatar
Juneryang 么么哒! 4

博主你好。我按照你的博文操作,在这一章节前都是OK的。

这一章按照你的来,刚开始自己写的,后来是粘贴复制的你的代码。

还没有测试这一节的扩展用户信息,在测试 写文章 的时候就报错了。以下是报错信息

DoesNotExist at /article/article-create/
User matching query does not exist.
Request Method:    POST
Request URL:    http://39.97.108.114:8000/article/article-create/
Django Version:    2.2.2
Exception Type:    DoesNotExist
Exception Value:    
User matching query does not exist.
Exception Location:    /usr/local/python3/lib/python3.7/site-packages/django/db/models/query.py in get, line 408
Python Executable:    /bin/python3
Python Version:    3.7.3
Python Path:    
['/home/django_project',
 '/usr/local/python3/lib/python37.zip',
 '/usr/local/python3/lib/python3.7',
 '/usr/local/python3/lib/python3.7/lib-dynload',
 '/usr/local/python3/lib/python3.7/site-packages']
Server time:    Sat, 15 Jun 2019 10:50:03 +0000

调试了好几次,查了文档后没找到合适的解决方案。

回退到上一章节所有功能都OK。

本人小白,希望博主指点以下

5个月前 回复


avatar
杜赛 [博主] Juneryang 么么哒! 3

这个报错的意思是没有找到对应的User数据。

报错页面往下翻一翻,看看bug是从你的代码的哪一句抛出的?

5个月前 回复


avatar
Juneryang 杜赛 [博主] 么么哒! 3

嗯嗯。找到问题了。是在测试的时候没仔细看你的说明。代码是没问题的。谢谢~

5个月前 回复


avatar
shenhanlin 么么哒! 3

博主您好,我在后台添加User的时候报这个错误怎么办,百度+调试了也还是没解决,博主能帮忙看下吗,谢谢啦

IntegrityError at /admin/auth/user/add/

UNIQUE constraint failed: userprofile_profile.user_id
Request Method:POSTRequest URL:http://127.0.0.1:8000/admin/auth/user/add/Django Version:2.2.2Exception Type:IntegrityErrorException Value:

UNIQUE constraint failed: userprofile_profile.user_id
Exception Location:D:\PycharmProjects\my_blog\venv\lib\site-packages\django\db\backends\sqlite3\base.py in execute, line 383Python Executable:D:\PycharmProjects\my_blog\venv\Scripts\python.exePython Version:3.6.7Python Path:

['D:\\PycharmProjects\\my_blog',
 'D:\\PycharmProjects\\my_blog',
 'D:\\PycharmProjects\\my_blog\\venv\\Scripts\\python36.zip',
 'D:\\Python36\\DLLs',
 'D:\\Python36\\lib',
 'D:\\Python36',
 'D:\\PycharmProjects\\my_blog\\venv',
 'D:\\PycharmProjects\\my_blog\\venv\\lib\\site-packages',
 'D:\\PycharmProjects\\my_blog\\venv\\lib\\site-packages\\setuptools-39.1.0-py3.6.egg',
 'D:\\PycharmProjects\\my_blog\\venv\\lib\\site-packages\\pip-10.0.1-py3.6.egg',
 'D:\\PyCharm 2018.2.4\\helpers\\pycharm_matplotlib_backend']
Server time:Tue, 18 Jun 2019 09:37:07 +0000

 

5个月前 回复


avatar
杜赛 [博主] shenhanlin 么么哒! 3

这个报错意思是你的数据库中已经有了正在添加的用户了(id相同),添加相同用户违背了唯一性。

检查一下你的model的主键字段,是否保证每次生成值的唯一性。

或者可能绕过了正常流程创建了用户。删除数据库后重新迁移试试。

5个月前 回复


avatar
shenhanlin 杜赛 [博主] 么么哒! 3

我试了下删除数据库重新迁移了,但是还是报同样的错误,model类的话,我copy了博主你的,也还是报同样的错误。

5个月前 回复


avatar
杜赛 [博主] shenhanlin 么么哒! 3

model下面两个信号接收函数分别注释掉,看看是哪个引起的bug

在引起bug的那个函数里再加一个if语句,判断现有数据库中是否已经存在该用户。如果存在则什么也不干;如果不存在才正常创建新数据。

5个月前 回复


avatar
shenhanlin 杜赛 [博主] 么么哒! 3

我把两个信号接收函数注释掉就可以正常增加用户了,这是什么原因呢。

5个月前 回复


avatar
杜赛 [博主] shenhanlin 么么哒! 3

这两个函数是用来创建Profile这个model的,注释掉当然就没问题了。但是也相当于这一章的内容没有完成。

我还不太确定问题在哪,建议你按我说的,还是加一个if语句判断一下是否应该创建新数据,把功能先实现了再说。

5个月前 回复


avatar
shenhanlin 杜赛 [博主] 么么哒! 3

做了判断还是有问题,还是报同样的错误

# 信号接收函数,每当新建 User 实例时自动调用
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    ids = []
    for id in Profile.objects.all():
        ids.append(id.id)

    if instance.id not in ids:
        Profile.objects.create(user=instance)


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

弄了好久还是解决不了,博主能说下具体的解决方法吗?

5个月前 回复


avatar
杜赛 [博主] shenhanlin 么么哒! 3

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created and not Profile.objects.filter(user=instance).exists():
        Profile.objects.create(user=instance)

大概意思是这样,不过我没测试,你试一下能不能行。

你试着在函数里用这个:

print(instance.id)

在命令行里看看,打印出来的id和你的User表里的id是否能对得上。

5个月前 回复


avatar
杜赛 [博主] shenhanlin 么么哒! 4

去测试了一下,是教程的代码有问题,实在抱歉!

bug现象:在后台中添加Profile会导致报错,但是在前台中添加Profile则不会。

bug原因:django后台自己的逻辑会创建一个Profile,而model中传递出来的信号会再次尝试创建一个相同的Profile,最终导致bug。

很不好意思,我写文章时测试不够全面,给您造成了困扰。我会尽快更正这个错误。

5个月前 回复


avatar
shenhanlin 杜赛 [博主] 么么哒! 3

我试了博主你那个方法,发现还是不行,还是报同样的错误。

5个月前 回复


avatar
杜赛 [博主] shenhanlin 么么哒! 3

怪我测试不够全面broken heart

晚上我更正好了通知你。

5个月前 回复


avatar
shenhanlin 杜赛 [博主] 么么哒! 3

嗯好的,麻烦博主你了smiley

5个月前 回复


avatar
杜赛 [博主] shenhanlin 么么哒! 5

不麻烦,感谢反馈

5个月前 回复


avatar
杜赛 [博主] shenhanlin 么么哒! 5

教程末尾“本章勘误”中已更正了此bug

5个月前 回复


avatar
南霁姑娘 么么哒! 2

[04/Sep/2019 19:15:22] "GET /userprofile/edit/1 HTTP/1.1" 200 4182
Not Found: /userprofile/edit/
[04/Sep/2019 19:15:33] "POST /userprofile/edit/ HTTP/1.1" 404 3031

 

博主你好,我经常遇到这种问题,提交的没有进去if request.method=='POST'那个分支,有时候没有做任何改动,突然就好了。但是在编辑用户信息这里,一直报错。

点击提交按钮后,页面url跳转为http://127.0.0.1:8000/userprofile/edit/

编辑视图如下,开始我自己写的,后面报错复制你的,还是不行

# 编辑用户信息
@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请求数据")

2个月前 回复


avatar
杜赛 [博主] 南霁姑娘 么么哒! 2

表单的 method 是否设置的 post?

报错的内容是什么?

2个月前 回复


avatar
南霁姑娘 杜赛 [博主] 么么哒! 2

表单的method是post,我也是直接复制你的html,报错内容就是我评论前三行显示的那样

[04/Sep/2019 19:15:22] "GET /userprofile/edit/1 HTTP/1.1" 200 4182
Not Found: /userprofile/edit/
[04/Sep/2019 19:15:33] "POST /userprofile/edit/ HTTP/1.1" 404 3031

 

我再详细说下我的操作过程,刚打开个人信息页面,url是userprofile/edit/1,页面正常显示form,我填好电话和简介后点击提交,url跳转到userprofile/edit,页面意思就是url错误(userprofile/edit后面还应该有id),然后我看后台就显示

[04/Sep/2019 19:15:22] "GET /userprofile/edit/1 HTTP/1.1" 200 4182
Not Found: /userprofile/edit/
[04/Sep/2019 19:15:33] "POST /userprofile/edit/ HTTP/1.1" 404 3031

 

主要是我点提交应该进入views函数中POST分支,但是根本就没进去。

而且,我之前真的遇到过这种问题,这次是第三次了,上次突然就好了,我没做任何改动,好诡异,我真的不知道是啥原因crying

2个月前 回复


avatar
杜赛 [博主] 南霁姑娘 么么哒! 2

你这个已经进到 POST 语句了吧,第3个请求 是 post 请求啊,只不过失败了而已。

看样子是 id 没有传递到 redirect 的 url 中去,仔细检查一下。

另外你这个虽然报了 404 错误,但表单数据是上传成功的吗?

2个月前 回复


avatar
南霁姑娘 杜赛 [博主] 么么哒! 1

如果进去POST分支的话,我在if request.method == 'POST':后面先打印个信息比如print('11111')后台应该能显示的呀,现在是没有

表单数据也没上传成功,表里没有数据

2个月前 回复


avatar
南霁姑娘 杜赛 [博主] 么么哒! 1

我现在把之前的代码全删掉,然后重新写,又正常了,我先不删评论,如果有网友遇到同样问题可以解答我一下smiley

2个月前 回复


avatar
南霁姑娘 杜赛 [博主] 么么哒! 1

还有博主,前面注册那里讲到用.get['password']的写法,不然如果用户输入空值会报错,那这里我试了填写空值,也能正常保存空值到数据库,这两个地方有什么区别吗?

profile.phone = profile_cd['phone']
profile.bio = profile_cd['bio']

2个月前 回复


avatar
杜赛 [博主] 南霁姑娘 么么哒! 1

这里是这样的。查询字典的某个不存在的键,用 .get() 不会报错并返回一个 None,用 dict[] 则会报错,程序就进行不下去了

2个月前 回复


avatar
南霁姑娘 杜赛 [博主] 么么哒! 1

所以我还是没太懂,这里用的dict[],怎么不报错了呢?为什么前面注册那里要用get(),这里可用用dict[]

2个月前 回复


avatar
杜赛 [博主] 南霁姑娘 么么哒! 1

要查不存在的键才会报错,你这里查的什么?您可以百度一下它两的区别

2个月前 回复


avatar
zhangbig 南霁姑娘 么么哒! 3

我刚才也出现了此问题,和你一模一样。

真正的bug不在userprofile/views里而在useprofile/urls里

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

BUG是因为edit/<int:id>,而正确的代码是这样edit/<int:id>/,所以导致了没有正确重定向,定向到了userprofile/edit里。

2个月前 回复


avatar
杜赛 [博主] zhangbig 么么哒! 1

感谢指出!给其他人参考

2个月前 回复


avatar
StanleyGao2018 么么哒! 1
本回复已被 StanleyGao2018 删除

1个月前 回复


avatar
fcmxmk 么么哒! 1

老师, 发现了2个问题.

1. 您博客的用户注册功能有问题, 一直提示"两次密码不一致", 换了几个浏览器都一样;

2. 在django用户管理后台 http://127.0.0.1:8000/admin/auth/user/xx/change/ , 一直显示 Profile: #1, 这里的 return 不生效, 也没报错, 大概是什么原因?

userprofile/models.py
...

class Profile(models.Model):
    ...
    def __str__(self):
        return 'user {}'.format(self.user.username)

22天前 回复


avatar
杜赛 [博主] fcmxmk 么么哒! 1

问题一:

感谢你的反馈,我会抽空去测试。不过已有不少本地用户成功注册了,没得到其他人反馈此问题。是不是你的密码里有特殊字符?

问题二:

方法执行了没有?print 看一下

21天前 回复


avatar
杜赛 [博主] fcmxmk 么么哒! 1

刚去测试了一下,确认在最新版 Chrome 浏览器中,使用 大小写英文字符 + 数字 可以顺利注册并登陆账号。你的问题是否如下:

  • 密码使用了特殊字符?
  • 两次输入大小写不一致?

21天前 回复


avatar
fcmxmk 杜赛 [博主] 么么哒! 1

1. 一直提示"两次密码不一致"的问题大概解决了, 是因为带了特殊字符'+(英文加号)', 测试了不带特殊字符也不能注册, 没报错, 就是一直在重复加载注册页面, 后面发现是因为密码里面带了和用户名一样的字符, 比如: 用户名 testuser, 密码 testuser233, 就无法注册成功;

2. profile无法显示的问题还是找不到问题, 应该是方法没加载, 图片好像不能本地上传, 我直接发代码和日志吧

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

[31/Oct/2019 09:51:55] "GET /admin/auth/user/7/change/ HTTP/1.1" 200 19174
[31/Oct/2019 09:51:55] "GET /admin/jsi18n/ HTTP/1.1" 200 3223

21天前 回复


avatar
杜赛 [博主] fcmxmk 么么哒! 2

先跳过第二个问题吧,小毛病暂时不要去纠结,先把后面的重要的学了。

或者加入 qq 群来探讨。

20天前 回复