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/')
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)
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'),
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With