Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'User' object has no attribute 'get'

Tags:

python

django

I am trying to construct a ModelForm from this solution here, however i am getting this error:

'User' object has no attribute 'get'

The idea is to get ModelForm to construct a form that when submitted the user logged in updates the entry.

The models.py is:

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()

And my model in models.py is

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

At the request here is a traceback of the issue from 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/')
like image 595
Alex Stewart Avatar asked Jan 13 '23 16:01

Alex Stewart


2 Answers

Writing this answer because I was bitten twice in a single week by this error. Came to this question and it was no use in helping me figure out the problem. The problem with this code is that you have passed request.user an object into the init function of the UserDetailsForm. And your definition for init does not handle what happens with 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/')

See arrow. If you compare that with your definition for the __init__of user details form. You can see init is not expecting that request.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)

Note there are legitimate reasons why one would write the init to pass in an object.

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)

Also Note that default def of __init__ for modelform has __init__(self, *args, **kwargs)

The dynamic form initialisation here above is a good example.

It appears that django is treating the passed in variable in this case request.user as some_field and is trying to call a method called get which the 'UserModel' does not have. If you check the stack trace you will notice. The below stack trace is an example simulated.

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 is the return value the result of the method call field.widget.value_from_data_dict ... if you notice, the SomeObject is being treated as the data here whoes get method is being called.

To answer the question, either define init to handle the request.user

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

Or call the form without the request.user

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

If you decide to go with option one. You have to remember to call super before calling self.fields. Because self.fields is created by the super method. If you dont you will run into another attributeerror no field named fields.

Edit

Django provides a convenient method get_form_kwargs for adding attributes to the init of form views that inherit from django.views.generic.edit.ModelFormMixin such as FormView.

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)
like image 141
unlockme Avatar answered Jan 16 '23 02:01

unlockme


You need something a bit simpler. Have this as your model form:

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

In your view:

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)

In your urls.py, you need to make sure that the view is called with a logged in user:

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'),
)
like image 37
Burhan Khalid Avatar answered Jan 16 '23 00:01

Burhan Khalid