I tried to use SuccessMessageMixin with CreateView but got an error.
I used it with UpdateView and it worked.
It'd be nice to get a hint about what to do next. Thanks.
Repo: https://github.com/jeremy886/DjangoBasics/blob/DjangoForms/courses/views.py
Error:
AttributeError at /courses/2/create_quiz/
'Quiz' object has no attribute 'cleaned_data'
Request Method: POST
Request URL: http://localhost:8000/courses/2/create_quiz/
Django Version: 2.0.5
Exception Type: AttributeError
Exception Value:
'Quiz' object has no attribute 'cleaned_data'
Exception Location: C:\Users\jeremy\.virtualenvs\django\lib\site-packages\django\contrib\messages\views.py in form_valid, line 12
Code:
class QuizCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
model = "course"
pk_url_kwarg = "course_pk"
context_object_name = 'course'
form_class = forms.QuizForm
template_name = "courses/quiz_create.html"
success_message = "%(title)s was created successfully"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
course_pk = self.kwargs.get(self.pk_url_kwarg)
course = get_object_or_404(models.Course, pk=course_pk)
context["course"] = course
return context
def form_valid(self, form):
course_pk = self.kwargs.get(self.pk_url_kwarg)
quiz_form = form.save(commit=False)
quiz_form.course = get_object_or_404(models.Course, pk=course_pk)
# find a way to add "Successfully added!" message
return super().form_valid(quiz_form)
def get_success_url(self):
course_pk = self.kwargs["course_pk"]
return reverse_lazy('courses:detail', kwargs={'pk': course_pk})
# More: how to get the quiz id from the above quiz form
The problem is in form_valid function: return super().form_valid(quiz_form). It should be return super().form_valid(form) instead.
def form_valid(self, form):
course_pk = self.kwargs.get(self.pk_url_kwarg)
form.instance.course = get_object_or_404(models.Course, pk=course_pk)
return super().form_valid(form)
Related
I have an update view that is working as expected. The only issue is that I cant figure out how to redirect back to the post that was being updated after submission. I believe I am on the right track with get_success_url but I cant get it working
view
class UpdateBuildLog(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = BuildLog
form_class = BuildLogupdateForm
template = 'blog/buildlog_update.html'
def get_object(self):
return BuildLog.objects.get(pk=self.kwargs["pkz"])
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def get_success_url(self):
pk = self.kwargs["pkz"]
return reverse("build-log-view", kwargs={"pkz": pk})
def test_func(self):
post = self.get_object()
if self.request.user.id == post.author_id:
return True
return False
urls:
path('post/<int:pk>/build-log/<int:pkz>/', views.BuildLogDisplay, name='build-log-view'),
path('post/<int:pk>/build-log/<int:pkz>/delete/', views.BuildLogDelete, name='build-log-delete'),
path('post/<int:pk>/build-log/<int:pkz>/update/', UpdateBuildLog.as_view(), name='build-log-update'),
Error:
NoReverseMatch at /post/127/build-log/75/update/
Reverse for 'build-log-update' with keyword arguments '{'pkz': 75}' not found. 1 pattern(s) tried: ['post/(?P<pk>[0-9]+)/build\\-log/(?P<pkz>[0-9]+)/update/$']
You need both pk and pkz in your reverse:
def get_success_url(self):
pkz = self.kwargs["pkz"]
pk = self.kwargs["pk"]
return reverse("build-log-view", kwargs={"pkz": pkz, "pk": pk})
In views I have the following classes:
class AddPersonView(CustomCreateView):
template_name = "register/add.html"
form_class = PersonForm
model = Person
obj = Person.objects.all().count()
if obj > 0:
obj = Person.objects.last()
success_url = reverse_lazy('register:edit_person_view', kwargs={'pk': obj.id})
def get_context_data(self, **kwargs):
context = super(AddPersonView,
self).get_context_data(**kwargs)
class EditPersonView(CustomUpdateView):
template_name = "register/edit.html"
form_class = PersonForm
model = Person
obj = Person.objects.all().count()
if obj > 0:
obj = Person.objects.last()
success_url = reverse_lazy('register:edit_person_view', kwargs={'pk': obj.id})
def get_context_data(self, **kwargs):
context = super(EditPersonView,
self).get_context_data(**kwargs)
return context
My intention is that when you submit submit on the person's registration page the new page is that person's edit page. But the way I'm using it, after adding or editing a person, the next page is opening with the id of the previous record.
For example, if I now register a person with id 12 and give submit the page I should open it would be mysite.com/register/edit/12, but it is opening mysite.com/register/edit/11.
I have tried to do the following: Instead of using the variable sucess_url, I used this function in both classes:
def get_success_url(self):
return reverse('register:edit_person_view', kwargs={'pk': self.kwargs('pk')})
However, by giving submit to the page I get this error:
TypeError at /register/edit/12/
argument of type 'NoneType' is not iterable
During handling of the above exception (Reverse for 'None' not found. 'None' is not a valid view function or pattern name.)
What is the correct way for the next page to be the edit page of the object I'm adding / editing?
EDIT
Doing some perceived tests that a get_success_url function is not working, because when it uses the same form, with a success_url variable, when editing a person and giving submit I am redirected to a new page for a new registration.
class EditPersonView(CustomUpdateView):
template_name = "register/edit.html"
form_class = PersonForm
model = Person
success_url = reverse_lazy('register:add_person_view')
def get_context_data(self, **kwargs):
context = super(EditPersonView,
self).get_context_data(**kwargs)
return context
But when using the function:
def get_success_url(self):
return reverse('register:add_person_view')
I get the same error
TypeError at /register/edit/12/
argument of type 'NoneType' is not iterable
During handling of the above exception (Reverse for 'None' not found. 'None' is not a valid view function or pattern name.)
What is wrong so that the get_success_url function is not working?
Your out-by-one error is because Person.objects.last() is called when the create view is instantiated, which is before the new Person is created.
For get_success_url try this:
def get_success_url(self):
return reverse('register:edit_person_view', kwargs={'pk': self.object.pk})
I have used SuccessMessageMixin class but then also in the createview I did'nt get the success message but in the updateview it is working when I return the super().form_valid(form)
class DepartmentCreateView(LoginRequiredMixin, PermissionRequiredMixin,SuccessMessageMixin ,CreateView):
template_name = 'departments/create_department.html'
form_class = DepartmentForm
success_url = reverse_lazy('departments:departments')
permission_required = ('departments.add_department',)
def form_valid(self, form):
department = form.save(commit=False)
department.created_by = self.request.user
department.updated_by = self.request.user
department.slug = slugify(uuid.uuid4())
department.save()
message = '[{"created": {}}]'
# retriving ContentType object
ct_obj = ContentType.objects.get(model='department')
# creating history object
history = History.objects.create(
action_time=timezone.now(),
action='created',
user=department.created_by,
content_type=ct_obj,
object_id=department.id,
change_message=message,
)
history.save()
return super().form_valid(form)
You haven't actually set a success message.
class DepartmentCreateView(LoginRequiredMixin, PermissionRequiredMixin,SuccessMessageMixin ,CreateView):
success_message = "Department was created successfully"
...
Note, your form_valid is saving things twice. You should do:
def form_valid(self, form):
form.instance.slug = slugify(uuid.uuid4())
return super().form_valid(form)
I have a form implemented in a ListView. The form is used for editing existing items. However I get a key error when i try to submit the form:
File "C:\Users\John\Desktop\website\app\views.py", line 124, in form_valid
id = self.kwargs['id']
KeyError: 'id'
[14/Dec/2016 21:42:09] "POST /car/ HTTP/1.1" 500 99346
This is the main code:
class CarListFormView(FormView):
form_class = CarListForm
def form_valid(self, form):
id = self.kwargs['id']
obj = Car.objects.get(pk=id)
form = CarListForm(instance=obj)
car = form.save(commit=False)
if self.request.user.is_staff:
car.agency = Agency.objects.get(agency_id=9000)
else:
car.agency = self.request.user.agency
car.save()
return redirect('car_list')
What causes this key error?
Thanks!
id is not in your kwargs, that is causing the KeyError. In fact, you don't need to load the car like that. Your method can be simplified to this:
from django.views.generic import UpdateView
class CarListFormView(UpdateView):
model = Car
form_class = CarListForm
template_name = 'something/car_form.html'
def form_valid(self, form):
car = form.save(commit=False)
if self.request.user.is_staff:
car.agency = Agency.objects.get(agency_id=9000)
else:
car.agency = self.request.user.agency
car.save()
return redirect('car_list')
I'm trying to update a model in Django using the class-based generic view UpdateView.
I read the page Updating User model in Django with class based UpdateView to try and get me started, but I'm getting an error 'WSGIRequest' object has no attribute 'id'
I'm a fresh face to Django, so please be forgiving if I'm doing something stupid.
//urls.py
url(r'^portfolios/update/(?P<id>\d+)/$',PortfoliosUpdateView.as_view()),
//views.py
class PortfoliosUpdateView(UpdateView):
form_class = PortfoliosCreateForm
model = Portfolios
template_name = 'portfolios/create.html'
def get(self, request, **kwargs):
self.object = Portfolios.objects.get(id=self.request.id)
form_class = self.get_form_class()
form = self.get_form(form_class)
context = self.get_context_data(object=self.object, form=form)
return self.render_to_response(context)
def get_object(self, queryset=None):
obj = Portfolios.objects.get(id=self.request.id)
return obj
It's mostly just a modified version of the code originally posted, but I thought it'd work. I know that I'm trying to retrieve the id passed as a GET parameter, but that doesn't seem to come through in the request variable. Am I going about this the wrong way?
Thanks
Edit: I think I fixed it, but this may be wrong:
I changed the lines
self.object = Portfolios.objects.get(id=self.request.id)
obj = Portfolios.objects.get(id=self.request.id)
to
self.object = Portfolios.objects.get(id=self.kwargs['id'])
obj = Portfolios.objects.get(id=self.kwargs['id'])
I could be wrong.
It should be:
def get_object(self, queryset=None):
obj = Portfolios.objects.get(id=self.kwargs['id'])
return obj
Look at class based generic view dispatch explains that keyword arguments are assigned to self.kwargs.:
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
self.request = request
self.args = args
self.kwargs = kwargs
return handler(request, *args, **kwargs)
id = self.request.GET.get('id',None) is what you needed when trying to access the GET query string.
However, your view can be simplified:
from django.conf.urls import *
from django.views.generic import UpdateView
from yourapp.models import Portfolios
from yourapp.forms import PortfoliosCreateForm
urlpatterns = patterns('',
url('^portfolios/update/(?P<pk>[\w-]+)$', UpdateView.as_view(
model=Portfolios,
form_class=PortfoliosCreateForm,
template_name='portfolios/create.html',
success_url='/portfolios'
), name='portfolio_update'),
)
views.py
class MyUpdateView(UpdateView):
model = ModelName # required
template_name = 'x/h1.html'
form_class = ModelNameForm
success_url = reverse_lazy('app:page1')
def get_queryset(self):
"""
Optional condition to restrict what users can see
"""
queryset = super().get_queryset()
return queryset.filter(id__lt=20)
def get_success_url(self):
return reverse_lazy(
'app1:abc',
kwargs={'pk': self.object.id}
)
urls.py
In urlpatterns=[]
path('xyz/<pk>/', MyUpdateView.as_view(),name='xyz')
my_model_view.html
{{form}}
You will be able to edit ModelName at url /xyz/<pk>/ where <pk> can be anything from 1 to 20 based on our condition in get_queryset(). Take that condition out to allow users to edit any object.
self.object is only available after post request to the UpdateView.