I bought and am Reading the Book Two Scoops of Django:Best Practices for Django 1.5 and in it has a example of Class based views. After this implementation I get the error after submitting the form.
ImproperlyConfigured at /NonProfitCreate/
No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model
Doing research I came along this problem Django - Class Based Generic View - "No URL to redirect to"
I want the get_absolute_url to work in my program
this is my forms.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# npp/forms.py
from django import forms
from .models import NonProfit
class NonProfitCreateForm(forms.ModelForm):
class Meta:
model = NonProfit
fields = ("name","contact_name","email","phone","address","image","tags",)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
return super(NonProfitCreateForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
kwargs['commit']=False
obj = super(NonProfitCreateForm, self).save(*args, **kwargs)
if self.request:
obj.user = self.request.user
obj.save()
class NonProfitUpdateForm(NonProfitCreateForm):
class Meta:
model = NonProfit
this is my models.py and views files
from django.db import models
from django.contrib.auth.models import User
from django.db.models import permalink
from django_extensions.db.fields import AutoSlugField
from django.contrib import admin
from django.core.urlresolvers import reverse
import tagging
from tagging.models import Tag
# Create your models here.
''' this is for the Non-Profit Proccess '''
class NonProfit (models.Model):
User = models.ForeignKey(User)
name = models.CharField(max_length=100)
contact_name = models.CharField(max_length=100)
email = models.EmailField(max_length=75)
phone = models.CharField(max_length=20)
address = models.TextField(max_length=3000)
image = models.ImageField(upload_to='photos/%Y/%m/%d',blank=True)
slug = models.SlugField(max_length=128)
slug = AutoSlugField(('slug'), max_length=128, unique=True, populate_from=('name',))
tags = tagging.fields.TagField()
def get_absolute_url(self):
return reverse("npp/nonprofit_detail", kwargs={"slug": self.slug})
def __unicode__(self):
return self.name
def get_tags(self):
return Tag.objects.get_for_object(self)
# Create your views here.
# Auction/npp/views.py
from Auction.views import ActionMixin
from django.contrib import messages
from django.views.generic import CreateView, UpdateView, DetailView
from braces.views import LoginRequiredMixin
from forms import NonProfitCreateForm,NonProfitUpdateForm
from models import NonProfit
class NonProfitCreateView(LoginRequiredMixin,ActionMixin,CreateView):
model = NonProfit
action = "created"
form_class = NonProfitCreateForm
class NonProfitUpdateView(LoginRequiredMixin,ActionMixin,UpdateView):
model = NonProfit
action = "updated"
form_class = NonProfitUpdateForm
class NonProfitDetailView(DetailView):
model = NonProfit
# Auction/views.py
class ActionMixin(object):
#property
def action(self):
msg = "{0} is missing action.".format(self.__class__)
raise NotImplementedError(msg)
def form_valid(self, form):
msg = "{0}!".format(self.action)
messages.info(self.request, msg)
return super(ActionMixin, self).form_valid(form)
urls.py
url(
regex=r'^NonProfitCreate/',
view=NonProfitCreateView.as_view(),
name='NonProfitCreate',
),
url(
regex=r'^NonProfit/(?P<slug>[-\w\d]+)/',
view=NonProfitDetailView.as_view(),
name='NonProfit'
)
this is my stacktrace, the django braces is highlighted, and
/home/talisman/projects/Auction/Auction/views.py in form_valid
return super(ActionMixin, self).form_valid(form)
EEnvironment:
Request Method: POST
Request URL: http://127.0.0.1:8000/NonProfitCreate/
Django Version: 1.5.1
Python Version: 2.7.4
Installed Applications:
('django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.admin',
'django.contrib.admindocs',
'django.contrib.comments',
'django.contrib.sitemaps',
'zinnia',
'tagging',
'mptt',
'south',
'misc',
'adm',
'registration',
'npp',
'blogs')
Installed Middleware:
('django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware')
Traceback:
File "/home/talisman/virt_env/Auction/local/lib/python2.7/site-packages/Django-1.5.1-py2.7.egg/django/core/handlers/base.py" in get_response
115. response = callback(request, *callback_args, **callback_kwargs)
File "/home/talisman/virt_env/Auction/local/lib/python2.7/site-packages/Django-1.5.1-py2.7.egg/django/views/generic/base.py" in view
68. return self.dispatch(request, *args, **kwargs)
File "/home/talisman/virt_env/Auction/local/lib/python2.7/site-packages/django_braces-1.0.0-py2.7.egg/braces/views.py" in dispatch
98. **kwargs)
File "/home/talisman/virt_env/Auction/local/lib/python2.7/site-packages/Django-1.5.1-py2.7.egg/django/views/generic/base.py" in dispatch
86. return handler(request, *args, **kwargs)
File "/home/talisman/virt_env/Auction/local/lib/python2.7/site-packages/Django-1.5.1-py2.7.egg/django/views/generic/edit.py" in post
199. return super(BaseCreateView, self).post(request, *args, **kwargs)
File "/home/talisman/virt_env/Auction/local/lib/python2.7/site-packages/Django-1.5.1-py2.7.egg/django/views/generic/edit.py" in post
165. return self.form_valid(form)
File "/home/talisman/projects/auction/Auction/views.py" in form_valid
54. return super(ActionMixin, self).form_valid(form)
File "/home/talisman/virt_env/Auction/local/lib/python2.7/site-packages/Django-1.5.1-py2.7.egg/django/views/generic/edit.py" in form_valid
128. return super(ModelFormMixin, self).form_valid(form)
File "/home/talisman/virt_env/Auction/local/lib/python2.7/site-packages/Django-1.5.1-py2.7.egg/django/views/generic/edit.py" in form_valid
65. return HttpResponseRedirect(self.get_success_url())
File "/home/talisman/virt_env/Auction/local/lib/python2.7/site-packages/Django-1.5.1-py2.7.egg/django/views/generic/edit.py" in get_success_url
119. "No URL to redirect to. Either provide a url or define"
Exception Type: ImproperlyConfigured at /NonProfitCreate/
Exception Value: No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model.
This exception is produced because self.object = None when attempting to redirect after a valid edit. Since the value of self.object is the result of a form.save() call, the most likely reason for this error is that you have overridden the save() method in NonProfitCreateForm, but forgotten to return the saved object.
The Form.save() method is expected to return the object that was saved and should not be None.
Your NonProfitCreateForm could be modified as shown below:
class NonProfitCreateForm(forms.ModelForm):
...
def save(self, *args, **kwargs):
kwargs['commit']=False
obj = super(NonProfitCreateForm, self).save(*args, **kwargs)
if self.request:
obj.user = self.request.user
obj.save()
return obj #<--- Return saved object to caller.
The first two lines of your save() method will create a model instance from the entered form data. But because commit=False, the object will not be saved to the database. If self.request is not present on your form instance, a the returned object will not have a database primary key, and get_absolute_url will still fail.
So, you want to ensure that a request parameter is always passed to your form when instantiated. This doesn't happen by default, so you need to arrange your view code to instantiate your form with a request parameter.
Looking through the code for FormMixin you can see that there is a get_form_kwargs function which determines the arguments to pass to any instantiated form. You need to pass request=self.request, so in your view override get_form_kwargs to add the required parameter, something like this:
class NonProfitCreateView(LoginRequiredMixin,ActionMixin,CreateView):
model = NonProfit
action = "created"
form_class = NonProfitCreateForm
def get_form_kwargs(self):
# Ensure the current `request` is provided to NonProfitCreateForm.
kwargs = super(NonProfitCreateView, self).get_form_kwargs()
kwargs.update({ 'request': self.request })
return kwargs
It would probably be a better idea to create a subclass of CreateView with the modified get_form_kwargs function, and have your NonProfitCreateView derive from the subclass.
When you use reverse, use the name of the url pattern you wish to reverse.
You wish to redirect to this url:
url(
regex=r'^NonProfit/(?P<slug>[-\w\d]+)/',
view=NonProfitDetailView.as_view(),
name='NonProfit'
)
Therefore your get_absolute_url method should be:
def get_absolute_url(self):
return reverse("NonProfit", kwargs={"slug": self.slug})
Try to remove the #permalink decorator from your get_absolute_url method. It cannot work together with reverse.
Also, the Django documentation states the following:
The permalink decorator is no longer recommended. You should use reverse() in the body of your get_absolute_url method instead.
You can override your class-based view's get_success_url function. Like this:
def get_success_url(self):
return reverse("NonProfit", kwargs={"slug": self.object.slug})
Solved the problem after reading the first sentence of Austin Phillips:
def form_valid(self, form):
article = form.save(commit=False)
article.author = self.request.user
self.object = article.save()
return super().form_valid(form)
the point is the save article.save()'s result back to self.object
Related
I am trying to upload image via django restframe work and return a custom response
views.py
class Imageuploadviewset(viewsets.ModelViewSet):
queryset = UploadModel.objects.all()
serializer_class = UploadSerializer
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response({'something': 'serializer'})
def list(self, request, *args, **kwargs):
return Response({'Model': 'Skin Disease Diagnosis'})
models.py
class UploadModel(models.Model):
name=models.CharField(max_length=50)
image=models.ImageField('images/')
urls.py
router = DefaultRouter()
router.register(r'UploadModel', views.Imageuploadviewset)
urlpatterns = [
path('',views.home,name='home'),
path('api/', include(router.urls)),
]
Serializer.py
class UploadSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model=UploadModel
fields='__all__'
Query is that how to play with name and image upload, run some functions on them and return a response. I am not sure how to get those.
Instead of retrieve and list i need the create function
def create(self, request):
img=request.FILES['image']
user=request.POST['name']
pred=somefunction(ai,img)
return Response({'user':user,'prediction':pred})
I have a ModelForm and I'm trying to display it with some of the fields disabled. In this case, process_id.
The important part of my Model looks like this:
models.py
class Process(models.Model):
related_processes = models.ManyToManyField('self', blank=True, symmetrical=False)
process_id = models.CharField(max_length=20, primary_key=True)
normal_field = models.CharField(max_length=20)
# a lot of fields here...
So basically I have a Process that can have zero or more related processes.
This is what I have on forms.py:
forms.py
class ProcessForm(ModelForm):
class Meta:
model = Process
fields = '__all__'
class EditProcessForm(ProcessForm):
readonly_fields = ('process_id', )
def __init__(self, *args, **kwargs):
super(EditProcessForm, self).__init__(*args, **kwargs)
for field in (field for name, field in self.fields.items() if name in self.readonly_fields):
field.widget.attrs['disabled'] = 'true'
field.required = False
def clean(self):
for f in self.readonly_fields:
self.cleaned_data.pop(f, None)
return super(EditProcessForm, self).clean()
class NewVersionProcessForm(EditProcessForm):
readonly_fields = ('process_id', )
def __init__(self, *args, **kwargs):
super(NewVersionProcessForm, self).__init__(*args, **kwargs)
for field in (field for name, field in self.fields.items() if name in self.readonly_fields):
# field.widget.attrs['disabled'] = 'true'
# Remember this line ^
field.required = False
The first time the client is filling the form, I want all fields to be editable, so I use ProcessForm. But when the client is editing the Process, I want some fields to be read-only. I found this nice solution here on stackoverflow (unfortunately I couldn't find it again) and it works perfectly when I'm editing a process.
The difference between EditProcessForm and NewVersionProcessForm is their Views and the commented line on __init__.
views.py
class ProcessFormView(FormView):
template_name = 'my_app/fill_form.html'
form_class = ProcessForm
def form_valid(self, form):
form.save()
return redirect('my_app:show_process_page', form.cleaned_data.get('process_id'))
class EditProcessView(UpdateView):
model = Process
form_class = EditProcessForm
template_name = 'my_app/edit_form.html'
pk_url_kwarg = 'process_id'
def post(self, request, process_id):
# This is a little hack I found here: https://stackoverflow.com/a/21262262/3773461
# to edit an immutable QueryDict.
mutable = request.POST._mutable
request.POST._mutable = True
request.POST['process_id'] = process_id
request.POST._mutable = mutable
return super().post(request)
def form_valid(self, form):
form.save()
return redirect('my_app:show_process_page', self.kwargs['process_id'])
class NewVersionProcessView(EditProcessView):
template_name = 'my_app/new_version_form.html'
form_class = NewVersionProcessForm
def get(self, request, process_id):
try:
Process.objects.get(process_id=process_id)
return redirect('my_app:process_already_exists_page')
except Process.DoesNotExist:
self.object = Process(process_id=process_id)
last_id = self.object.get_last_version_id()
last_process = Process.objects.get(process_id=last_id)
last_process_dict = last_process.__dict__
related_processes = last_process.related_processes.all()[:]
exclude = ['_state', 'process_id']
for key, value in last_process_dict.items():
if key not in exclude:
self.object.__dict__[key] = value
# self.object.related_processes.add(*related_processes)
# Remember this line too ^
form_class = self.get_form_class()
form = self.get_form(form_class)
return self.form_invalid(form)
def post(self, request, process_id):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return redirect('my_app:show_process_page', form.cleaned_data['process_id'])
The first view is a simple FormView with ProcessForm. The second view is an UpdateView. On post I just add the field that is disabled on the form (and therefore is not passed ahead normally). On form_valid I just save the form on its model (with the just added field).
The third view is my current problem. Keep in mind that I can do 3 things with a Process: create a new one, edit it, or create a new version of an existing process. This third view intends to do the third one.
On get, I check if I'm trying to create a process that already exists. If it doesn't, I create the Process and copy all useful data on the base process to it, except the ManyToMany field (the second "remember this line"), because I couldn't figure out a way to do that without saving the Model first.
On post, I'm checking if the form is valid, and on form_valid, I'm saving the process.
What works
I can create and edit processes just fine. The read-only fields when editing works perfectly. The model is saved as intended.
Also, I can create a new process using another existing process as a base (NewVersionProcessView) if the first "remember this line" is commented, that is, if all the fields are editable.
What doesn't work
If the first "remember this line" is uncommented, that is, if some of the fields are read-only, my view crashes on post. Most specifically, on form_invalid. The only thing I can think of is that somehow there's not a Process object linked to my NewVersionProcessView. I can't understand why though, because I always assign something to self.object on get. I cannot get the object from the database on post because it is not saved yet. Also, I cannot understand why a field being enabled or disabled affects self.object. Remember, the only thing that changes is that field.widget.attrs['disabled'] = 'true' line.
Here is the log:
Request Method: POST
Request URL: http://127.0.0.1:8000/my_app/new_version/999-11/
Django Version: 2.0.7
Python Version: 3.4.4
Installed Applications:
['my_app.apps.MyAppConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "C:\Python34\lib\site-packages\django\core\handlers\exception.py" in inner
35. response = get_response(request)
File "C:\Python34\lib\site-packages\django\core\handlers\base.py" in _get_response
128. response = self.process_exception_by_middleware(e, request)
File "C:\Python34\lib\site-packages\django\core\handlers\base.py" in _get_response
126. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Python34\lib\site-packages\django\views\generic\base.py" in view
69. return self.dispatch(request, *args, **kwargs)
File "C:\Python34\lib\site-packages\django\views\generic\base.py" in dispatch
89. return handler(request, *args, **kwargs)
File "C:\Users\***\Desktop\my_app\views.py" in post
124. return self.form_invalid(form)
File "C:\Python34\lib\site-packages\django\views\generic\edit.py" in form_invalid
61. return self.render_to_response(self.get_context_data(form=form))
File "C:\Python34\lib\site-packages\django\views\generic\edit.py" in get_context_data
67. return super().get_context_data(**kwargs)
File "C:\Python34\lib\site-packages\django\views\generic\detail.py" in get_context_data
93. if self.object:
Exception Type: AttributeError at /my_app/new_version/999-11/
Exception Value: 'NewVersionProcessView' object has no attribute 'object'
I can provide the urls.py and the htmls if needed.
Any help is appreciated.
following scenario: I do have a edit profile page which pulls the data from db so the user can change them. After submitting everything gets stored in the db but the reverse doesn't seem to work. Basically I don't know how to give the pk along to be able to call the same site with reverse.
views.py
class EditUserProfileView(UpdateView):
model = UserProfileInfo
form_class = UserProfileForm
template_name = "accounts/user_profile.html"
def get_object(self, *args, **kwargs):
user = get_object_or_404(User, pk=self.kwargs['pk'])
return user.userprofileinfo
def get_success_url(self, *args, **kwargs):
if 'pk' in self.kwargs:
pk = self.kwargs['pk']
else:
slug = 'main'
return reverse("accounts:edit")
urls.py
app_name = 'accounts'
urlpatterns=[
url(r'^edit/(?P<pk>\d+)/$', views.EditUserProfileView.as_view(), name="edit-user-profile"),
]
html from where I call the edit-user-profile
<li class="nav-item nav-link">Hello {{user.first_name}}</li>
Cheers
Both reverse and reverse_lazy have the keyword arguments args and kwargsthat you can pass a list or dict of view arguments:
return reverse("accounts:edit-user-profile", kwargs={'pk': pk})
views.py
class EditPost(UserPassesTestMixin, LoginRequiredMixin, UpdateView):
model = Posts
form_class = PostForm
template_name="posts/add_post.html"
def test_func(self):
x = self.request.user.pk
print (x)
y = Posts.objects.get(user='user')
print (y)
if x == y:
return True
else:
if self.request.user.is_authenticated():
raise Http404("You are not allowed to edit this Post")
models.py
class Posts(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
post = models.CharField(max_length=1200, blank=False)
How do i match the loggedin user and the user object of the Post
i could not find any solution since i am using class based views.
try this:
add dispatch to EditPost class
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
if obj.user != self.request.user:
raise Http404("You are not allowed to edit this Post")
return super(EditPost, self).dispatch(request, *args, **kwargs)
Doing the check in test_func is tricky. You need to fetch the object once in test_func to check whether the user is allowed to use it, and then the object is fetched again by the same fiew.
An easier approach is to override the get_queryset method. If the user is not the author of the post, they will get a 404.
class EditPost(LoginRequiredMixin, UpdateView):
def get_queryset(self):
return super(EditPost, self).filter(user=self.request.user)
To add to the previous posts, try this:
Add to your class EditPost:
login_url = 'your url name or path to login page'
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
The test_func method is used by UserPassesTestMixin for this particular logic. To override it we set variable obj to the current object returned by the view using get.object().
After that, if the author on the current object (obj.author) matches the current user (self.request.user) we allow editing. If not (i.e. false) we throw an error.
login_url is from LoginRequiredMixin and the default location is /accounts/login. To override it, set your own path or name of the login template. This will take those people who are not logged in to the login page.
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'),
)
form = UserDetailsForm(request.user, request.POST, request.FILES)
The problem is that you pass the user object as a positional argument, while your form expects the first positional argument to be the form data. Meanwhile, your form expects a keyword argument request that contains the request object, but you're not passing such an argument. Change the above line to:
form = UserDetailsForm(request.POST, request.FILES, request=request)
I can't see your full view function, but for simple form handling you might want to consider using a class-based view, based on Django's UpdateView, like Burhan suggested.
at __init__
instead of :
return super(UserDetailsForm, self).__init__(*args, **kwargs)
try:
forms.ModelForm.__init__(self, *args, **kwargs)
it works for me..
for visitors using get method here:
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'))