I'm having trouble using 'success_url' with Django - django

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

Related

How to define the slug of another model in myListView

I sort of need help understanding my own code specifically the views.py. I'm trying to change url pattern for my TitleUpdateListView from using my Update models title field and instead using the slug field instead.
If someone could help explain line by line whats going in in my TitleUpdateListView so I could better understand whats specifically going on that would be great.
urls.py
urlpatterns = [
# Update view for each game
path('<str:title>/updates/', TitleUpdateListView.as_view(), name='title-updates'),
# Adds the ability to sort by platform
path('<str:title>/updates/<int:platform_id>/', TitleUpdateAjaxListView.as_view(), name='title-updates-ajax'),
]
views.py
class TitleUpdateListView(ListView):
model = Update
context_object_name = 'updates'
template_name = 'updates/title_updates.html'
def get_queryset(self):
title = get_object_or_404(Game, title=self.kwargs.get('title'))
return Update.objects.filter(game=title).order_by('-date_published')
def get_context_data(self, **kwargs):
context = super(TitleUpdateListView, self).get_context_data(**kwargs)
context['game'] = get_object_or_404(Game, title=self.kwargs.get('title'))
return context
class TitleUpdateAjaxListView(ListView):
model = Update
template_name = 'updates/updates_ajax.html'
context_object_name = 'updates'
paginate_by = 5
def get_queryset(self):
title = get_object_or_404(Game, title=self.kwargs.get('title'))
return Update.objects.filter(game=title, platform=Platform.objects.filter(
id=self.kwargs.get('platform_id')).first()).order_by('-date_published')
def get_context_data(self, **kwargs):
context = super(TitleUpdateAjaxListView, self).get_context_data(**kwargs)
context['game'] = get_object_or_404(Game, title=self.kwargs.get('title'))
return context
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
context = self.get_context_data()
return render(request, self.template_name, context)
Not sure what you meant by "I'm trying to change url pattern for my TitleUpdateListView from using my Update models title field and instead using the slug field instead.". In the urls.py, you can change the name of the parameter (the xxxx in <str:xxxx>) to whatever you want, as long as you also look for this same name in the view. You can change it to <str:slug> and in your view you'd fetch it like self.kwargs.get('slug'). Just remember to also change which parameter are you using to filter the Game table (slug instead of title).
As for explaining what your view does, you should probably take a look at Django's docs on Class Based Views, but I'll try to give an overview:
The get_queryset method is searching the Game table to find the games whose title matches the title passed in the URL parameter. It then returns a list of all Update objects whose game field points to the game just found.
The get_context_data method is adding the same Game object found in the get_queryset method to the view's context under the 'game' key. This means that you can access the Game object inside the template that this view renders.
You just need to change the get_queryset method of your view:
# change url variable name from title to slug
path('<str:slug>/updates/', TitleUpdateListView.as_view(), name='title-updates'),
def get_queryset(self):
# the url variables are stored in the dictionary self.kwargs
slug = self.kwargs.get('slug')
game = get_object_or_404(Game, slug=slug)
return Update.objects.filter(game=game).order_by('-date_published')
The same applies for get_context_data:
def get_context_data(self, **kwargs):
context = super(TitleUpdateListView, self).get_context_data(**kwargs)
context['game'] = get_object_or_404(Game, slug=self.kwargs.get('slug'))
return context

get form input value from form_class in views

How do I access the company_id field in form input in the class based views
forms.py
class CompanyProductForm(forms.Form):
company_id = forms.IntegerField()
I tried overriding the form_valid() but didn't work out.
also I want to convert 'obj' queryset to a python list and then append it to products.
view.py
class CompanyProduct(LoginRequiredMixin, UserPassesTestMixin, FormView):
template_name = 'company_product.html'
form_class = CompanyProductForm
products = []
def test_func(self):
return self.request.user.is_staff
def post(self, request, *args, **kwargs):
self.products = []
def form_valid(self, form):
id = form.cleaned_data['company_id']
return super(CompanyProduct, self).form_valid(form)
obj = Product.objects.filter(company_id=id)
return render(request, 'product_download.html',{'product':self.products,
'num':len(self.products)})
Error:
TypeError at /upload/company_product/
int() argument must be a string or a number, not 'builtin_function_or_method'
By overriding the post method with just an assignment you have disabled all the other actions that are supposed to happen on post, such as validating the form. You should remove that method altogether - define that list somewhere else, such as in form_valid.

Django - dynamic success_url in UpdateView

I want my user to be able to see a page, update that page, and then be returned to that page or continue making more edits.
Here's the view to show the information:
# urls.py
url(r'^gameview/$', views.GameView.as_view(template_name='matchview.html'), name='GameView')
# Views.py
class GameView(generic.TemplateView):
template_name = "matchview.html"
def get_context_data(self, **kwargs):
context = super(GameView, self).get_context_data(**kwargs)
q = self.request.GET.get('match')
context['report'] = GameNotes.objects.filter(game=q)
context['game'] = Schedule.objects.get(match=q)
context['gamepic'] = Photo.objects.filter(game=q)
return context
So now they want to add information about a game. I use an UpdateView
class GameView(generic.TemplateView):
template_name = "matchview.html"
def get_context_data(self, **kwargs):
context = super(GameView, self).get_context_data(**kwargs)
q = self.request.GET.get('match')
context['report'] = GameNotes.objects.filter(game=q)
context['game'] = Schedule.objects.get(match=q)
context['gamepic'] = Photo.objects.filter(game=q)
return context
When the user finishes updating in the latter view, they should be returned to the former on the exact same team. This post helped me move in the right direction insofar as using 'get_success_url', but I'm still stuck because I don't think I'm using kwargs. Everything I've tried has resulted in errors.
(My (gulp) thought is that I should re-write the urls to use PKs so that this stuff is easier, but I wanted to make sure)
EDIT:
My fugly attempt (Note: I Have two submit buttons, one to update and one to update and add notes).
def form_valid(self, form):
if form.is_valid():
form.save()
if 'submit' in self.request.POST:
q = self.request.GET.get('match')
return reverse_lazy('TeamView', args=(q))
else:
return render('addnotes', {'game' : q})
SOLUTION:
Learned how to use URL Parameters and kwargs:
(for anyone new like me, self.kwargs.get is brilliant)
def get_success_url(self, **kwargs):
q = self.kwargs.get('match')
if "submit" in self.request.POST:
url = reverse('GameView', args={q : 'match'})
else:
url = reverse('AddNotes', args={q : 'match'})
return url
What about get_absolute_url for the model object?
https://docs.djangoproject.com/en/1.10/ref/models/instances/#get-absolute-url
from django.urls import reverse
class GameModel(models.Model):
....
def get_absolute_url(self):
return reverse('game:single_page', args=[str(self.id)])
And in your GameView:
class GameView(generic.TemplateView):
template_name = "matchview.html"
def get_context_data(self, **kwargs):
....
def get_success_url(self, **kwargs):
return self.object.get_absolute_url()

Django: access user information from a generic.view

I am trying to access to the current user information to filter the client's items but I am stuck with an error.
I guess must be a different way to request the information of the current user. I am using this: self.kwargs.request.user.
I got that error when using self.request.user I get this error get_context_data() missing 1 required positional argument: 'user'
My url.py
url(r'^update/(?P<pk>[0-9]+)/$', views.CampaignUpdate.as_view(), name='update_campaign'),
AttributeError at
'dict' object has no attribute 'request'
class CampaignUpdate(generic.UpdateView):
model = Campaign
fields = [.....]
template_name = 'campaign/campaign.html'
success_url = '../../'
def get_context_data(self, *args, **kwargs):
context = super(CampaignUpdate, self).get_context_data(**kwargs)
items = items.objects.get(client= self.kwargs.request.user)
Use the below code to get current user 'Item' object.
class CampaignUpdate(generic.UpdateView):
model = Campaign
fields = [.....]
template_name = 'campaign/campaign.html'
success_url = '../../'
def get_context_data(self, *args, **kwargs):
context = super(CampaignUpdate, self).get_context_data(**kwargs)
context['items'] = Items.objects.get(client= self.request.user)
return context
If you want to get the 'Campaign', you can just access line self.object as its a UpdateView
I changed
def get_context_data(self, *args, **kwargs):
to
def get_context_data(self, form_class=None, **kwargs):
and it worked fine, even though I don't know why...

How do I use an UpdateView to update a Django Model?

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.