RelatedObjectDoesNotExist: 用户没有用户资料。

22

所以我已经通过以下方式将用户扩展了字段score

models.py:

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    score = models.IntegerField(default=0);

这似乎是沿用推荐的方法

接下来,我尝试在我的视图中访问用户的userprofile

views.py:

player = request.user.userprofile

这似乎也符合推荐的方式,但这就是我出错的地方:

RelatedObjectDoesNotExist
User has no userprofile.

如果我将userprofile更改为其他内容,就会出现另一个错误:

AttributeError
'User' object has no attribute 'flowerpot'

当我尝试以下代码时:

print request.user
print UserProfile.objects.all()

我获得了控制台输出:

post_test1
[]

编辑
我有两个超级用户,七个在扩展用户之前我创建的用户,和一个在扩展用户后我创建的用户(post_test1)。


编辑2

看起来我需要创建一个post_save处理程序,在每次创建User对象时都创建一个新的profile。

当我阅读它时,它似乎很简单,我转到链接的页面,那里是Django发送的所有信号的列表。 我查找了post_save,它说:

像pre_save一样,但在save()方法结束时发送。

好的,所以我查找了pre_save,它说:

这是在模型的save()方法开始时发送的。

我这样解释它:当我创建我的用户(在我的views.py中)时,应该调用save()方法,但直到现在还没有发生过,并且之后应该发送post_save,每当创建User对象时都会创建一个新的profile!

所以现在我准备开始查看示例,于是我谷歌搜索:

django post save example
这里看起来我应该添加类似于装饰器的东西@receiver(post_save, ...
这里看起来我应该更改多个文件并编写一个信号定义?
这个也似乎暗示了多个文件(包括signals.py)。

看起来比我最初想的要复杂得多。 这里是否有人可以解释我应该如何做或向我展示一些关于信号如何工作的好资源?

现在我的create_user视图如下:

def create_user(request):
    if request.method == "POST":
        form = UserCreationForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data["username"]
            password = form.cleaned_data["password1"]
            new_user = User.objects.create_user(username=username, password=password)
            return redirect('play')
    else:
        form = UserCreationForm()

    return render(request, 'antonymapp/create_user.html', {'form': form})

我应该在返回之前调用new_user.save()吗?如果是,为什么到现在为止都能正常工作?我在测试这个视图时创建了一堆用户。看起来还需要在这里添加post_save.connect(create_profile, sender=User)


1
RelatedObjectDoesNotExist 表示没有与 request.user 相关联的 userprofile,这似乎很明显,不是吗? - Shang Wang
我扩展了用户和用户资料,但仍然没有与请求用户相关的用户资料。所以是的,你说得对。这显然是我在这里询问的问题。 - MrJalapeno
你在设置文件中设置了用户配置文件吗?例如,如果你的应用程序名称是base,则需要在你的设置文件中添加以下内容。 AUTH_PROFILE_MODULE = 'base.UserProfile' - abhishekgarg
14个回答

26
你需要先为用户创建一个userprofile:
profile = UserProfile.objects.create(user=request.user)

在您的views.py中,您可以使用get_or_create,以便如果用户没有用户资料,则为用户创建一个userprofile

player, created = UserProfile.objects.get_or_create(user=request.user)

更新: 若要在创建新用户时自动创建用户配置文件,请使用信号(signal)。

myapp/signals.py中可以像这样实现:

@receiver(post_save, sender=User, dispatch_uid='save_new_user_profile')
def save_profile(sender, instance, created, **kwargs):
    user = instance
    if created:
        profile = UserProfile(user=user)
        profile.save()

谢谢你的回答! 这个该怎么做呢?我应该在create_user视图中完成吗?我可以在哪里阅读相关信息(例如,如何为管理员和之前创建的用户创建用户配置文件,但不想浪费您的时间问一堆后续问题=)? - MrJalapeno
1
你可以使用 get_or_create 来确保用户在访问页面时拥有个人资料,或者你可以创建一个 post_save 信号来在每次创建新用户时创建个人资料。 - v1k45
谢谢!那么我在views.py文件中不需要做任何事情吗?感觉我只是创建了一个函数(save_profile),我应该在某个地方调用它吗? - MrJalapeno
不需要在任何地方调用它。每当创建/更新用户时,它会自动执行。 - v1k45
让我们在聊天中继续这个讨论 - MrJalapeno

6
如果你即使尝试了上述建议仍然遇到这个错误,可能是由于你使用createsuperuser命令创建的第一个用户没有个人资料所致。
当我试图使用该用户登录时也遇到了这个错误。我通过以下方式解决了它:
-创建一个新用户。
-撤销更改(删除为Profile编写的代码或将其注释掉)。
-登录到超级用户。
-授权新创建的用户进行管理员操作。
现在你可以删除没有个人资料的第一个用户了。

3

您所做的任何事情都不会在创建User时强制创建UserProfile对象。处理这种情况有两种基本方法:

  1. 如果您始终希望存在UserProfile(因为您给score设置了default值),则创建一个post_save处理程序,每当创建User对象时创建一个新配置文件(但不是每次保存,因此请确保在处理程序中检查created参数)。

  2. 如果预期用户可能没有配置文件,则需要在尝试访问配置文件时捕获UserProfile.DoesNotExist异常。如果您经常这样做,请创建某种类型的辅助函数。

更新以回答信号问题

还看起来需要添加post_save.connect(create_profile, sender=User)

您需要定义一个名为create_profile的函数,并像您展示的那样将其连接起来。我通常会在包含sendermodels.py文件中直接执行此操作,但在这种情况下,由于发送方是内置的Django模型,并且您已经将该模型导入到定义UserProfile的文件中,因此这就是要执行此操作的地方。它应该长这样:

def create_profile(sender, instance, created, *args, **kwargs):
    # ignore if this is an existing User
    if not created:
        return
    UserProfile.objects.create(user=instance)
post_save.connect(create_profile, sender=User)

2
我遇到了同样的问题(即用户存在,但个人资料不存在,但从个人资料方面引入了OneToOne关系),因此无法登录管理站点。该应用程序已经在生产中,我无法删除数据库。
解决方法是创建一个临时视图函数fixme,不需要登录要求(但URL难以猜测),并访问一次。该视图查询所有用户对象,然后使用get_or_create()反向查询每个用户的个人资料。如果个人资料不存在,则会创建它。
打印用于通过日志监视成功。最后从生产应用程序中删除视图和URL。
def fixme(request):
    users = User.objects.all()
    for user in users:
        obj, created = Profile.objects.get_or_create(user=user)
        print(user.username,' : ',created)
    print("all done")
    return HttpResponse("It's done.")

1

我曾经遇到同样的错误,但是通过创建我的信号,我解决了问题,如下所示:

然后我按照以下方式编辑了我的apps.py:

`from django.apps import AppConfig

class BlogConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'app_name'

def ready(self):
    import app_name.signals`

1

如果尚未完成,请在admin.py中注册配置文件模型。

使用./manage.py createsuperuser创建新的超级用户。

使用新的超级用户登录。

从管理面板中点击“配置文件”,添加新条目并从用户下拉列表中选择用户,然后保存。


问题是,我无法使用超级用户登录,因为该配置文件不存在。 - Soren

1

我解决了这个问题(Django 3.8.3-Mongodb)。首先,我创建了signals.py,然后将apps.py添加到项目中。

signals.py

from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import Profile

@receiver(post_save,sender=User)
def update_user_profile(sender,instance,created,**kwargs):
    if created:
        profile = Profile.objects.create(user =instance)

apps.py

from django.apps import AppConfig


class ProfileConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'Profile'

    def ready(self):
        import Profile.signals

views.py

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.urls.base import reverse
from .forms import ProfileForm
from .models import Profile
from django.shortcuts import redirect, render,get_object_or_404
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm

login_required(login_url="user:login")
def dashboard(request):
    return render(request,"dashboard.html")


@login_required(login_url="user:login")
def get_profile(request):    
    
    profile = get_object_or_404(Profile,user=request.user)
    
    return render(request,"profile.html",{"profile":profile})

@login_required(login_url="user:login")
def update_profile(request):       
    profile = get_object_or_404(Profile, user=request.user)
    form = ProfileForm(instance=profile)
    if request.method=="POST":
        form = ProfileForm(request.POST,request.FILES,instance=request.user.user_profile)
        if form.is_valid():
            form.save()
            messages.success(request,"Profile is updated successfully")
            return HttpResponseRedirect(reverse("profile:profile"))   
        else:
            return render(request,"profile.html",{"form":form})                 
            
    return render(request,"edit.html",{"form":form})

0

进入您的视图,在create_user函数中执行以下操作

profile = userprofile()
profile.user = user
profile.save()

我曾经遇到过类似的问题,经过三天的研究,我仅用以上三行代码就解决了这个问题。


0

遇到了相同的错误。 在应用程序的models.py中添加以下代码解决了问题。

def create_profile(sender, instance, created, *args, **kwargs):
    if not created:      # if user already exits then ignore
        return
    UserProfile.objects.create(user=instance)
post_save.connect(create_profile, sender=User)

0

问题在于您的超级用户未连接到用户配置文件。 有许多方法可以解决这个问题,其中之一是从 shell 创建用户。

py manage.py shell

> from django.contrib.auth.models import User
> 
> user=User.objects.create_user('UserName', password='AdminPassword')
> user.is_superuser=True   
> user.is_staff=True   
> user.save()
> exit()

而另一种方法是,在Models.py中添加以下代码,以便您创建的超级用户被添加到UserProfile的范围内。

User.userprofile = property(lambda
u:Userprofile.objects.get_or_create(user=u)[0])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接