Django - reverse not working and I don't know why - django

I've used reverse before and it's been fine. But now, I don't know why it's not working. It might be because I'm trying to reverse to a class based view and I've never done that before.
Here's my code -- where am I messing it up?
views.py:
class DocumentRequestDetail(NavMixin, TitleMixin, SelectedBrokerage, DetailView):
model = DocumentRequest
mod_nav = 'brokerage_nav.html'
context_object_name = 'doc'
subtitle = 'Document Request Details'
def MarkDocRequestComplete(request, bpk, pk):
d = DocumentRequest.objects.get(pk= pk)
d.is_complete = True
d.save()
return reverse('doc_request_detail', args=[bpk, pk]) #<--- the offending line
urls.py
from django.conf.urls import include, url
from django.views.generic.list import ListView
from brokerage.views import *
urlpatterns = [
url(r'^(?P<pk>[0-9]+)/detail/$', BrokerageDetail.as_view(), name="brokerage_detail"),
url(r'^(?P<pk>[0-9]+)/edit/$', BrokerageEdit.as_view(), name="brokerage_edit"),
url(r'^(?P<bpk>[0-9]+)/doc-request/all/$', DocumentRequestList.as_view(), name="doc_request_list"),
url(r'^(?P<bpk>[0-9]+)/doc-request/(?P<pk>[0-9]+)/$', DocumentRequestDetail.as_view(), name="doc_request_detail"),
url(r'^(?P<bpk>[0-9]+)/mark-doc-request-complete/(?P<pk>[0-9]+)/$', MarkDocRequestComplete, name="mark_doc_request_complete"),
]
HTML Link that calls MarkDocRequestComplete:
<a href="{% url 'brokerage:mark_doc_request_complete' brokerage.pk doc.pk %}" class='btn btn-lg btn-wide btn-success'>
<i class='ti-check'></i>
Mark Complete
</a>
Error When Clicking HTML Link:
Reverse for 'doc_request_detail' with arguments '(u'1', u'19')' and keyword arguments '{}' not found. 0 pattern(s) tried: []
I don't get it... what am I doing wrong?
Edit I should say that, in the error, the 1 and 19 are the correct values for bpk and pk.
Edit 2 Added full URLs.py

The reason your url tag isn't working, is because you are namespacing it by adding brokerage: to it), when there is no namespace setup in your urls. See the documentation on how to properly setup namespaces.
The correct tag should be:
{% url 'mark_doc_request_complete' bpk=brokerage.pk pk=doc.pk %}

You have to change:
return reverse('doc_request_detail', args=[bpk, pk])
to
return reverse('doc_request_detail', kwargs={'bpk': bpk, 'pk': pk})
You must also change your template tag from:
<a href="{% url 'brokerage:mark_doc_request_complete' brokerage.pk doc.pk %}" class='btn btn-lg btn-wide btn-success'>
to:
<a href="{% url 'brokerage:mark_doc_request_complete' bpk=brokerage.pk pk=doc.pk %}" class='btn btn-lg btn-wide btn-success'>
The reason for these changes is because you have names in your regular expression groups.

Related

Does This Hierarchy work with ListView? Getting NoReverseMatch Error

I'm running into an issue debugging the following error:
Reverse for 'tasks' with arguments '('',)' not found. 1 pattern(s) tried: ['customer/(?P[-a-zA-Z0-9_]+)/tasks/$']
However, I'm pretty certain that my url parameters are correct and without typos. The url and page worked fine with a function based view, the problem started when I changed to a ListView.
Template:
<a href="{% url 'customerportal:tasks' object.slug %}" class="nav-link">
I also tried:
<a href="{% url 'customerportal:tasks' slug=object.slug %}" class="nav-link">
<a href="{% url 'customerportal:tasks' slug=object.slug|slugify %}" class="nav-link">
DetailView - passing slug param to url
class ProjectDetailView(DetailView):
model = Project
template_name = 'customerportal/customer_portal.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if not self.object.access_verification(request.user):
raise PermissionDenied()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
My ListView (errors):
class TaskListView(ListView):
model = Task
template_name = 'customerportal/tasks.html'
def get_queryset(self):
self.project = get_object_or_404(Project, slug=self.kwargs['customer_portal'])
return Task.objects.filter(project=self.project.id)
URLs:
path('<slug:slug>/', ProjectDetailView.as_view(), name='customer_portal'),
path('<slug:customer_portal>/tasks/', TaskListView.as_view(), name='tasks'),
I've confirmed that I'm passing the right slug by printing it. The slug parameter is from a parent directory as seen in the urls.py above. I'm guessing ListView doesn't work this way? If not is there a way I could maintain this structure, or do I have to switch back to a function based view in order to get this to work?
Thanks.
there is 2 things to sort out:
your error message says that there is a path that looks similar to this:
path('customer/<slug:customer_portal>/tasks/', TaskListView.as_view(), name='tasks'),
but your paths do not match that pattern of the error message - maybe you edited the paths after the error appeared.
the error type "... arguments '('',)' ..." always means that the parameter in your {% url ... %} statement is empty. In your case that would mean object.slug is empty
Please check what you handover to the template as object.slug

trouble converting Python regex to path with django

I am working on a project which uses regex urls. The way it is currently designed is you have to manually type the url as a model field which then gets passed to the urls/views as an argument? (I don't fully understand this part yes, still learning). These are the views, urls, and the templates which I think are the issue here. I am trying to automatize the process of adding the slug and that part is working. However, I can't seem to find a way around getting it to work with the way urls are configured currently.
the views sections:
class QuizDetailView(DetailView):
model = Quiz
slug_field = 'url'
template_name = 'exam/quiz_detail.html'
another section of the view where I think the url from the above snippet is turned into a variable which then gets used in url:
class QuizTake(FormView):
form_class = QuestionForm
template_name = 'exam/question.html'
def dispatch(self, request, *args, **kwargs):
self.quiz = get_object_or_404(Quiz, url=self.kwargs['quiz_name'])
if self.quiz.draft and not request.user.has_perm('quiz.change_quiz'):
raise PermissionDenied
The urls:
#passes variable 'quiz_name' to quiz_take view
url(regex=r'^(?P<slug>[\w-]+)/$',
view=QuizDetailView.as_view(),
name='quiz_start_page'),
url(regex=r'^(?P<quiz_name>[\w-]+)/take/$',
view=QuizTake.as_view(),
name='quiz_question'),
and finally the template snippets, this is a detail view of the model/quiz:
<a href="{% url 'quiz_start_page' quiz.url %}">
this one displays the model (which is a quiz users take):
<a href="{% url 'quiz_question' quiz_name=quiz.url %}">
What I have tried so far:
first, since I have the slug field:
class QuizDetailView(DetailView):
model = Quiz
slug_field = 'url' ---this was commented out
template_name = 'exam/quiz_detail.html'
next, I changed the urls from:
url(regex=r'^(?P<slug>[\w-]+)/$',
view=QuizDetailView.as_view(),
name='quiz_start_page'),
url(regex=r'^(?P<quiz_name>[\w-]+)/take/$',
view=QuizTake.as_view(),
name='quiz_question'),
to:
path('<slug:slug>/', QuizDetailView.as_view(), name='quiz_start_page'),
path('<slug:slug>', QuizTake.as_view(), name='quiz_question'),
Then I changed the temlates from:
<a href="{% url 'quiz_start_page' quiz.url %}">
<a href="{% url 'quiz_question' quiz_name=quiz.url %}">
To:
<a href="{% url 'quiz_start_page' quiz.slug %}">
<a href="{% url 'quiz_question' quiz.slug %}">
When I click on the listview for the model/quiz, the url is displayed correctly on browser which I think means the automatic slug is working. However, once I click the link I see this error:
Reverse for 'quiz_question' with keyword arguments '{'quiz_name': ''}' not found. 1 pattern(s) tried: ['(?P<slug>[-a-zA-Z0-9_]+)$']
Really appreciate any help/pointers
Just figured it out myself, I realized I left out the argument which links the Quiz to the TakeQuiz model above.
path('<quiz_name>', QuizTake.as_view(), name='quiz_question'),
Above, instead of the slug I passed the argument <quiz_name> and changed the templates too:
{% url 'quiz_question' quiz_name=quiz.slug %}
That worked.

Selenium .click() on link doesn't give the right address

I'm testing a page.
I'm writing a functional test and I want to click on a link using Selenium 3.141 .
The link is working (if I click on it by myself).
The address should be http://127.0.0.1:8000/lists/addname/ but when selenium click on it go to http://localhost:63286/lists/addname/lists/addname/ and ofcourse it doesn't find the page (63286 is a number always different but shouldn't be the problem, if I click on the link before it does (using time.sleep, for example) I go to the page http://localhost:63286/lists/addname and it works).
I'm not expert so maybe it's a trivial error and I don't know what code can be relevant, so tell me if you have to know more.
Edit: probabily is something related to load time /Edit
My functional_tests.py:
class NewVisitorTest(LiveServerTestCase):
def setUp(self):
self.browser = webdriver.Firefox()
def tearDown(self):
self.browser.quit()
def test_can_add_name_and_read_it_after(self):
# Bob go to the homepage
self.browser.get(self.live_server_url)
# He notice the title in header is Home
self.assertIn('Home', self.browser.title)
# He finds and clicks the link on the sidebar for the addname page
element = self.browser.find_element_by_link_text('Add Name')
# element = self.browser.find_element_by_xpath('/html/body/div/div[1]/nav/div/ul/li[2]/a') # this gives the same problem
time.sleep(1)
element.click()
In my template.html the link appear two time but that shouldn't be the problem:
<div class="navbar">
<a class="nav-link" href="{% url 'lists:addname' %}">{% trans "Add Name" %}</a>
</div>
[...]
<div class="sidebar">
<a class="nav-link" href="{% url 'lists:addname' %}">{% trans "Add Name" %}</a>
</div>
My main url.py:
urlpatterns = [
path('lists/', include('lists.urls')),
]
and my lists url.py:
urlpatterns = [
path('addname/', views.addname, name='addname'),
]
My views.py:
def addname(request):
if request.method == 'POST':
Item.objects.create(text=request.POST['item_text'])
return redirect('lists/addname/')
items = Item.objects.all()
return render(request, 'addname.html', {'items': items})
Thank you!

Django - delete button on form not redirecting to the correct path

I have a html template on which multiple messages are posted and can be deleted using a 'delete' button that has been created. My code seeks to search for the id of the item to be deleted and delete it, redirecting to /deleteMessage and concatenating with the id number. I don't fully understand the process and have an error I cannot spot.
html form
<ul>
{% for g in all_items %}
<li> {{ g.content }}
<form action="/deleteMessage/{{g.id}}/"
style="display: inline;"
method="post">{% csrf_token %}
<input type="submit" value="Delete"/>
</form>
</li>
{% endfor %}
</ul>
views.py relevant code
def deleteMessage(request,GuestBookItem_id):
item_to_delete =GuestBookItem.objects.get(id=GuestBookItem_id)
item_to_delete.delete()
return HttpResponseRedirect('/worldguestbook/')
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('worldguestbook/',worldguestbookView),
path('login/',loginView),
path('addMessage/',addMessage),
path('deleteMessage/',deleteMessage),
]
I imagine it is this line that is causing the error - an error in concatenation and not redirecting to the right path.
**<form action="/deleteMessage/{{g.id}}/"**
Error message:
Page not found (404)
Request Method: POST
Request URL: http://127.0.0.1:8000/deleteMessage/17/
Using the URLconf defined in mysite.urls, Django tried these URL patterns, in this order:
admin/
worldguestbook/
login/
addMessage/
deleteMessage/
The current path, deleteMessage/17/, didn't match any of these.
What I tried:
I tried, in views.py changing this to g.id (instead of GuestBookItems.id) to match with what is in the form, but that didn't work either.
item_to_delete =GuestBookItem.objects.get(id=g_id)
You need to capture GuestBookItem_id in the URL pattern:
path('deleteMessage/<int:GuestBookItem_id>/', deleteMessage),
Note that in Python, you would normally use guest_book_item_id as the variable name. Or since it's the primary key of a model instance, you could use pk. It would be a good idea to use get_object_or_404, so that you get a 404 page when the item does not exist.
You're already using a POST request, which is a good idea when you are changing or deleting objects. You should also check that it's a POST request in the view.
Finally, it's a good idea to reverse URLs instead of hardcoding them. First, you need to add names to your URL patterns, then you can use {% url %} in the template and reverse() or the redirect shortcut in the template.
Putting that together, you get:
<form action="{% url 'delete_message' g.id %}">
urlpatterns = [
path('admin/', admin.site.urls),
path('worldguestbook/', worldguestbookView, name="worldguestbook"),
path('login/', loginView, name="login"),
path('addMessage/', addMessage, name="add_message"),
path('deleteMessage/', deleteMessage, name="delete_message"),
]
path('deleteMessage/<int:pk>/', deleteMessage),
from django.shortcuts import get_object_or_404, redirect
def deleteMessage(request, pk):
if request.method == "POST"
item_to_delete = get_object_or_404(GuestBookItem, pk=pk)
item_to_delete.delete()
return redirect('worldguestbook')

multiple Id arg django

I have an issue with my web-app django.
In example, user can add an artist, and with this artist, he can (list, delete, play) music.
When the user click on the artist, the URL is 'music/1/' where the artist ID is 1, and music is listed.
When user want delete a music, the URL is 'delmusic/1/' where the music ID is 1.
delmusic function works, but returns an error PageNotFound with this http URL :
'http://host.local/appli/delmusic/1/webgui.views.music'
My urls.py :
url(r'^music/(\d+)/$', 'webgui.views.music'),
url(r'^delmusic/(\d+)/$','webgui.views.delmusic'),
My music template :
<a href="{% url 'webgui.views.delmusic' music.id %}"style="margin-bottom: 3px" type="button">
My view :
def music(request, id):
listmusic = Music.objects.filter(artist=id)
return render(request, 'music.html', {'listmusic': listmusic})
def delmusic(request, id):
music = Music.objects.get(id=id)
music.delete()
return redirect('webgui.views.music')
I think there is confusion with music.id and arist.id, but I don't know..
Maybe you will have an idea ?
You have a slight issue with your urls.py - the 2nd argument points to the views function, but is not used as a reference in the templates, for that you need to do:
url(r'^music/(\d+)/$', 'webgui.views.music', name='music'),
url(r'^delmusic/(\d+)/$','webgui.views.delmusic', name='delmusic'),
and then in the template
<a href="{% url 'delmusic' music.id %}" style="margin-bottom: 3px" type="button">
or if you had a namespace defined for your app in django_settings/urls.py:
<a href="{% url '<APPNAMESPACE>:delmusic' music.id %}" style="margin-bottom: 3px" type="button">
and then at then in views.py do
from django.core.urlresolvers import reverse
def delmusic(request, id):
music = Music.objects.get(id=id)
artist_id = music.artist.id
music.delete()
return redirect(reverse('music', args=[artist_id]))