“User”对象没有“get”属性。

4

我试图根据这个解决方案(链接)构建一个ModelForm,但是我遇到了这个错误:

'User' object has no attribute 'get'

这个想法是让ModelForm构建一个表单,当用户登录并提交表单时,更新条目。

models.py文件如下:

class UserDetailsForm(ModelForm):
    class Meta:
        model = UserProfile
        fields = ['mobile_phone']

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        return super(UserDetailsForm, self).__init__(*args, **kwargs)

    def save(self, *args, **kwargs):
        kwargs['commit']=False
        obj = super(UserDetailsForm, self)
        if self.request:
            obj.user = UserProfile.objects.get(user=self.request.user)
        obj.save()

我的models.py中的模型是:

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    mobile_phone = models.CharField(max_length=30,help_text='Max 30 characters.',blank=True)
    #have shortened this for simplicity
    def __unicode__(self):
        return self.mobile_phone

在此请求中,以下是来自views.py的问题追踪信息:
    userprofile = UserProfile.objects.get(user=request.user)
    if request.method == 'POST':
        form = UserDetailsForm(request.user, request.POST, request.FILES)
        if form.is_valid(): # it dies here
            form.save()
            return HttpResponseRedirect('/members-contact/')

你为什么要将 obj.user 设置为 UserProfile 的实例? - Joseph Victor Zammit
“UserProfile” 没有 “birth_date” 字段? - suhailvs
实际上我不知道,希望我可以通过这里解决这个问题。正如您从模型中看到的,我有一个OneToOneField对象来引用User对象。我正在尝试强制设置它。 - Alex Stewart
@suhail,我已经消除了歧义。我缩短了模型代码,但不小心留下了一些。谢谢。 - Alex Stewart
1
请发布完整的回溯信息。 - Pawel Furmaniak
嗨@uszywieloryba,请在上面找到views.py代码,我认为用户对象在这里没有连接到某个地方。 - Alex Stewart
5个回答

5

因为这个错误让我在一周内被咬了两次,所以写下这篇答案。 来到这个问题上,它对我解决问题没有任何帮助。 这段代码的问题在于你将一个对象request.user传递到了UserDetailsForm的init函数中。而你对于init的定义没有处理request.user会发生什么。

userprofile = UserProfile.objects.get(user=request.user)
if request.method == 'POST':
    ====>form = UserDetailsForm(request.user, request.POST, request.FILES)
    if form.is_valid(): # it dies here
        form.save()
        return HttpResponseRedirect('/members-contact/')

请看箭头。如果您将其与用户详细信息表单的__init__定义进行比较,您会发现init不期望该请求的user参数。

class UserDetailsForm(ModelForm):
   class Meta:
       model = UserProfile
       fields = ['mobile_phone']

  def __init__(self, *args, **kwargs):
      self.request = kwargs.pop('request', None)
      return super(UserDetailsForm, self).__init__(*args, **kwargs)

请注意,有合理的理由为什么要编写init以传递对象。
def __init__(self, some_object, *args, **kwargs):
     super(SomeFormClass, self).__init__(self, *args, **kwargs)
     self.fields['some_field'].queryset = SomeModel.objects.filter(some_field = some_object)

还请注意,modelform的默认__init__定义为__init__(self, *args, **kwargs)

上面的动态表单初始化是一个很好的例子。

看起来django将传入的变量在这种情况下视为some_field,并尝试调用一个名为“get”的方法,而“UserModel”没有该方法。如果您检查堆栈跟踪,您会注意到。下面的堆栈跟踪是一个模拟示例。

Traceback (most recent call last):
  File "/home/user/.local/lib/python3.5/site-packages/django/core/handlers/exception.py", line 39, in inner
response = get_response(request)
  return render(request, self.template_name, context)
  File "/home/user/.local/lib/python3.5/site-    packages/django/shortcuts.py", line 30, in render
  content = loader.render_to_string(template_name, context, request, using=using)
 ---
 ---
 ---
 packages/django/forms/forms.py", line 297, in non_field_errors
 return self.errors.get(NON_FIELD_ERRORS,   self.error_class(error_class='nonfield'))
 File "/home/user/.local/lib/python3.5/site-packages/django/forms/forms.py", line 161, in errors
 self.full_clean()
 ---
 ---
 ---
self._clean_fields()
File "/home/user/.local/lib/python3.5/site-packages/django/forms/forms.py", line 382, in _clean_fields
===>value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))<====
File "/home/sodara/.local/lib/python3.5/site-packages/django/forms/widgets.py", line 238, in value_from_datadict
====> return data.get(name) <====
AttributeError: 'SomeObject' object has no attribute 'get'

data.get是field.widget.value_from_data_dict方法调用的返回值。如果你注意到,这里将SomeObject视为数据,调用了它的get方法。

回答这个问题,可以定义init来处理request.user。

def __init__(self, user, *args, **kwargs):
    super(YourFormClass, self).__init__(*args, **kwargs):
    self.fields["some_field"].some_attr = user

或者可以不使用request.user来调用表单

    `form = YourFormClass(request.POST, request.FILES)`

如果你决定选择选项一,你需要记得在调用self.fields之前调用super。因为self.fields是由超类方法创建的。如果你没有这样做,你会遇到另一个属性错误,即没有名为fields的字段。
编辑
Django提供了一个方便的方法get_form_kwargs,用于向继承自django.views.generic.edit.ModelFormMixin的表单视图(例如FormView)的init添加属性。
class MyFormView(FormView):
    form_class = MyModelFormClass

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

class MyModelFormClass(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user') # Important to do this
        # If you dont, calling super will fail because the init does
        # not expect, user among the fields.
        super().__init__(*args, **kwargs)
        self.fields['some_field'].queryset = SomeModel.objects.filter(user=user)

3
您需要一些更简单的东西。将此作为您的模型表单:
class UserDetailsForm(ModelForm):
    class Meta:
        model = UserProfile
        fields = ['mobile_phone']

在您看来:

from django.core.urlresolvers import reverse_lazy
from django.views.generic import UpdateView
from .models import UserDetailsForm, UserProfile

class UpdateProfile(UpdateView):
     template_name = 'users/update_profile.html'
     form_class = UserDetailsForm
     model = UserProfile
     success_url = reverse_lazy('home')

     def get_object(self, queryset=None):
         '''This loads the profile of the currently logged in user'''

         return UserProfile.objects.get(user=self.request.user)

     def form_valid(self, form):
         '''Here is where you set the user for the new profile'''

         instance = form.instance # This is the new object being saved
         instance.user = self.request.user
         instance.save()

         return super(UpdateProfile, self).form_valid(form)

在您的urls.py中,您需要确保使用已登录的用户调用视图:
from django.contrib.auth.decorators import login_required
from django.views.generic TemplateView

from .views import UpdateProfile

urlpatterns = patterns('',
    (r'^profile/update/$', login_required(UpdateProfile.as_view())),
    (r'^$', TemplateView.as_view(template='index.html'), name='home'),
)

如果我使用这个方法,它会更新条目而不是添加新的吗? - Alex Stewart

2
form = UserDetailsForm(request.user, request.POST, request.FILES)

问题在于您将用户对象作为位置参数传递,而您的表单期望第一个位置参数是表单数据。同时,您的表单期望一个包含请求对象的关键字参数request,但您没有传递这样的参数。请将上面的行更改为:
form = UserDetailsForm(request.POST, request.FILES, request=request)

我看不到你的完整视图函数,但是对于简单的表单处理,您可能希望考虑使用基于Django的UpdateView 的类视图,就像 Burhan 建议的那样。


这解决了我的错误,但最终我还是手动处理了表单响应,这样做更容易。 - Alex Stewart

0

__init__ 中使用:

return super(UserDetailsForm, self).__init__(*args, **kwargs)

尝试:

forms.ModelForm.__init__(self, *args, **kwargs)

对我来说它有效。


0

对于使用get方法的访问者:

from django.contrib.auth.models import User
u = User.objects.all()[0]
print(u.get('username'))
# switch the line above to below
print(getattr(u, 'username'))

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