NoReverseMatch for the new object - python

Use Django 1.10.4. I have a model Stream, for which I created CreateView. When objects are created through the admin panel everything works fine, but when I use the form CreateView, an object is created (either in admin or in the database is no different from the other), but attempts to provide a link to it through DetailView result in an error:
NoReverseMatch at /
Reverse for 'detail_stream' with arguments '()' and keyword arguments '{'pk': 17}' not found.
2 pattern(s) tried: ['(?P<pk>[0-9])/$', 'streams/(?P<pk>[0-9])/$']
This error occurs when displaying the ListView, and then only for an object created through CreateView.
The place where the error occurs:
{% for item in stream_list %}
<a href="/streams{% url "detail_stream" pk=item.id %}">
...
</a>
{% endfor %}
When you try to go directly to DetailView (http://127.0.0.1:8000/streams/17) 404 error.
urls.py:
from django.conf.urls import url
from .views import StreamDetail, StreamUpdate
urlpatterns = [
url(r'^$', StreamList.as_view(), name='streams'),
url(r'^(?P<pk>[0-9])/$', StreamDetail.as_view(), name='detail_stream'),
url(r'^(?P<pk>[0-9])/update/$', StreamUpdate.as_view()),
]
Also, the url for the streams added to the main site urlpatterns.
View:
class StreamCreate(LoginRequiredMixin, CreateView):
login_url = '/login/'
def form_valid(self, form):
regex = re.compile('[^a-zA-Z]')
newtags = []
for tag in form.cleaned_data['tags']:
tag = regex.sub('',tag)
newtags.append(tag)
form.cleaned_data['tags'] = newtags
return super(StreamCreate,self).form_valid(form)
def form_invalid(self, form):
print(form.errors)
return super(StreamCreate,self).form_invalid(form)
def get_success_url(self):
return reverse('streams')
I suspect that something I did not realize at CreateView, but I can not understand that and need your help.

Your regex in the given route is wrong.
url(r'^(?P<pk>[0-9])/$', StreamDetail.as_view(), name='detail_stream')
[0-9] means it expects a single-digit number. 17 has two digits, so the regexp needs to take that into account (add + there)
url(r'^(?P<pk>[0-9]+)/$', StreamDetail.as_view(), name='detail_stream')
Some other routes in there have the same issue.

Related

Django: passing a URL parameter to every instance of a reverse URL lookup in a template tag

Sorry if the title is unclear, I'm not sure the best way to describe the issue. I have an application with a Ticket model and a Team model. All Tickets are associated with a single Team. The issue I'm having is a problem of URL reversing. I'm trying to set it up my URLs like so: /<team_pk>/tickets/ displays a list of tickets associated with the Team specified by team_pk. So /1/tickets/ would display all of the tickets for the first Team. Both of these objects are in the app tracker.
To do this, I've set up my project/urls.py files like so:
project/urls.py
urlpatterns = [ path('<team_pk>/', include('tracker.urls', namespace='tracker')), ]
tracker/urls.py
urlpatterns = [ path('tickets/', views.TicketTable.as_view(), name='ticket_list'), ]
Then, inside my html templates, I have the following URL tag:
href="{% url 'tracker:ticket_list' %}"
This results in a NoReverseMatch error:
NoReverseMatch at /1/tickets/
Reverse for 'ticket_list' with no arguments not found. 1 pattern(s) tried: ['(?P<team_pk>[^/]+)/tickets/$']
What I would like is for the reverse match to just use the current value for the team_pk URL kwarg.
What I have tried to fix it:
I have found the following solution to the problem, but it involves a lot of repetition, and I feel like there must be a DRYer way.
First, I extend the get_context_data() method for every view on the site.
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['current_team_pk'] = self.kwargs['team_pk']
return context
Then I reference this context in every URL template tag:
href="{% url 'tracker:ticket_list' team_pk=current_team_pk %}"
This results in the desired behavior, but it's a lot of repetition. So, is there a better way?
Edit:
Based on Willem Van Onsem's suggestion, I changed the URL template tag to href="{% url 'tracker:ticket_list' team_pk=team_pk %}", referencing the URL kwarg directly. But this doesn't seem to be working reliably. On the index page /<team_pk>/ loads just fine, and it includes two relevant URLs: /<team_pk>/ and /<team_pk>/tickets/. When I navigate to /<team_pk>/tickets/, however, I get another NoReverseMatch error:
NoReverseMatch at /1/tickets/
Reverse for 'home' with keyword arguments '{'team_pk': ''}' not found. 1 pattern(s) tried: ['(?P<team_pk>[^/]+)/$']
It seems the URL kwarg <team_pk> is not being passed for some reason. But the only link to 'home' is part of my base.html, which the other templates are extending. So the relevant template tags are the same.
Edit 2:
The view in question:
class TicketTable(LoginRequiredMixin, SingleTableMixin, FilterView):
table_class = my_tables.TicketTable
template_name = 'tracker/ticket_list.html'
filterset_class = TicketFilter
context_object_name = 'page'
table_pagination = {"per_page": 10}
PAGE_TITLE = 'Open Tickets'
TICKET_STATUS_TOGGLE = 'Show Closed Tickets'
TICKET_STATUS_TOGGLE_URL = 'tracker:closed_ticket_list'
DISPLAY_DEV_FILTER = True
DISPLAY_STATUS_FILTER = True
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['page_title'] = self.PAGE_TITLE
context['ticket_status_toggle'] = self.TICKET_STATUS_TOGGLE
context['ticket_status_toggle_url'] = self.TICKET_STATUS_TOGGLE_URL
context['display_dev_filter'] = self.DISPLAY_DEV_FILTER
context['display_status_filter'] = self.DISPLAY_STATUS_FILTER
return context
def get_queryset(self):
return models.Ticket.objects.filter_for_team(self.kwargs['team_pk']).filter_for_user(self.request.user).exclude(status='closed')
Edit 3:
I found a solution to access the URL kwargs without modifying context data. I'm not sure why team_pk=team_pk didn't work in the URL tag, but team_pk=view.kwargs.team_pk does work.
Based on the comments and responses from Willem Van Onsem, a friend of mine, and some of my own digging, I found what I think is the DRYest way to do what I was trying to do. I created a custom mixin:
class CommonTemplateContextMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.kwargs.get('team_pk'):
context.setdefault('team_pk', self.kwargs.get('team_pk'))
else:
context.setdefault('team_pk', self.request.user.teams.all()[0].pk)
return context
Then I subclass this mixin with all my views. Then I have a team_pk template tag inside all my templates, and I just add team_pk=team_pk to all my URL template tags. The only advantage to using this instead of team_pk=view.kwargs.team_pk is that I can add some additional logic for the case when the URL kwarg isn't available.

Django Slug raising error NoReverse Match

I am working on a django project(Django v2.0) which has users from different groups, this group is set up within the user profile. I would like to make the website more personal and have the user's group at the start of the website's url (added in through a slug), after they login.
Ideally once they login they would then be redirected to the right page with that slug at the beginning of the url. Within the login page I access the user's group, this group is called and will from now on be referred to as troop, to minimise confusion with Django groups. Here's my login class post view where I retrieve this:
def post(self, request):
def normal_user(user):
return user.groups.filter(name='Normal_User').exists()
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
if normal_user(user):
redirecting_url = 'accounts:home'
elif user.is_superuser:
redirecting_url = 'admin:index'
slug = user.userprofile.troop
return redirect(redirecting_url, slug=slug)
else:
form = AuthenticationForm()
error = True
args = {'form': form, 'error': error}
return render(request, self.template_name, args)
In this case I am trying to access the url "account" which I have called "home" and is in my app "accounts".
My home function in views.py in the app accounts has this code:
def home(request, slug):
args = {'user': request.user}
return render(request, 'accounts/home.html', args)
I do not want to do anything with the slug except display it in the url.
My template view looks like this:
<div class="container">
<br>
<h2><strong>Your Progress:</strong></h2><br>
<h3>
<strong>50% progress</strong>
<div class="progress">
<div class="progress-bar bg-danger progress-bar-striped progress-bar-
animated" role="progressbar" style="width:50%" aria-valuenow="50" aria-
valuemin="0" aria-valuemax="100">50%</div> </div>
</div></h3>
<br><br>
{% endblock %}
I also have a link to 'home' within my navbar and I link it in the similar fashion than the link above, using {% url 'accounts:home' slug = instance.slug %}
I'm currently having the following error:
"NoReverseMatch at /BC/account/
Reverse for 'home' with keyword arguments '{'slug': ''}' not found. 1 pattern(s) tried: ['(?P[-a-zA-Z0-9_]+)/account/$']"
The slug in this case is 'BC'.
My urls.py look like this:
In the root folder:
main_urls = [
path('leaders/admin/', admin.site.urls),
path('account/', include('accounts.urls', namespace='accounts'))
]
urlpatterns = [
path('', include('home.urls', namespace='home_page')),
path('login/', include('login.urls', namespace='login')),
path('<slug:slug>/', include(main_urls))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
And my urls.py in my accounts app looks something like this:
app_name="accounts"
urlpatterns = [
path('', views.home, name="home")
]
Sorry for the long description, but I've been reading every possible site/video I could find on Django slugs and have not been able to find anything which works yet. Could someone please help me with this problem?
Your regular expression for your url requires the slug to be at least one character long. However, the exception you're getting suggests that the troop name (which is used as the slug named argument) is empty:
Reverse for 'home' with keyword arguments '{'slug': ''}'
If the user isn't required to have a troop, in which case you want to just redirect him to /accounts/ instead of /troop/accounts, I suggest you add a separate URL for just accounts/$, or tweak the regular expression to allow an empty troop.
After poking around my code I found something which works. This is as it seems to be that I should be able to have views which take in a slug argument whilst still working if the slug is empty. To fix this all I did was change the slug part in my view function definitions to **kwargs.
Then I added a varaiable which accessed what the slug was and passed that through my templates for when I have a link.
For example:
def home(request, **kwargs):
slug = request.user.userprofile.troop
args = {'slug_arg': slug}
return render(request, 'accounts/home.html', args)
My links in my templates then look like this: {% url 'accounts:home' slug=slug_arg %}
Edit
I have found that the problem was that during my rendering, I should be able to pass through the slug, and in this case slug=instance.slug was not working and was sometimes coming up as being empty. However if I have it at slug=request.user.userprofile.troop then this works without having to accept an argument from the view.
In having said such, I am unable to do this for the admin site as I do not know how to access the views.py for the admin and thus add the parameter to take in the slug.

Django: Reverse for 'delete' with arguments '(49,)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['tidbit/delete_tidbit/']

Can't figure out how to solve this error
Here is the urls.py snippet:
urlpatterns = [
...
url(r'^delete_tidbit/', views.delete_tidbit, name='delete'),
...
]
The view:
def delete_tidbit(request, pk):
tidbit = Tidbit.objects.get(pk=pk)
tidbit.delete()
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
And the portion of the template that raises this error:
<a href="{% url 'delete' tidbit.pk %}">
The issue is here:
url(r'^delete_tidbit/', views.delete_tidbit, name='delete'),
This URL doesn't accept an argument, where as you are trying to give it one.
Try this instead:
url(r'^delete_tidbit/(?P<pk>.*)', views.delete_tidbit, name='delete'),
But beware: you are accepting GET requests to delete items in your database, any crawler coming across those links may try to follow them and inadvertently delete your data. Consider using a GET that delivers a form to be POSTed to ensure an actual user is doing the action.

Django django-cms reverse 'NoReverseMatch at..' multisite language

This seems to be a "classic" problem :
NoReverseMatch at /tr/showroom/
Reverse for 'project-list' with arguments '()' and keyword arguments '{}' not found. 0 pattern(s) tried: []
But the problem is, when I check the different topic on the internet and then my files, I don't get it.
So it's working perfectly on the native language. It happens when I try to change the language on this showroom page only.
Every page that have been copy with the commad cms copy works fine except this one.
Well, here is the model :
def get_slug(self):
return self.safe_translation_getter(
'slug',
language_code=get_language(),
any_language=False)
def get_absolute_url(self, current_app=None):
"""
Warning: Due to the use of django application instance namespace
(for handle multi instance of an application)we add the possibility
to use it with the reverse.
So if get_absolute_url is call without app instance it may failed (
for example in django_admin)
"""
lang = get_language()
if self.has_translation(lang):
kwargs = {'slug': self.get_slug()}
return reverse('djangocms_showroom:project-detail',
kwargs=kwargs,
current_app=current_app)
return reverse('djangocms_showroom:project-list', current_app=current_app)
def get_full_url(self, site_id=None):
return self._make_full_url(self.get_absolute_url(), site_id)
def _make_full_url(self, url, site_id=None):
if site_id is None:
site = Site.objects.get_current()
else:
site = Site.objects.get(pk=site_id)
return get_full_url(url, site)
The url :
from django.conf.urls import patterns, url
from .views import (ProjectListView, ProjectDetailView)
urlpatterns = patterns(
'',
url(r'^$', ProjectListView.as_view(), name='project-list'),
url(r'^project/(?P<slug>\w[-\w]*)/$', ProjectDetailView.as_view(), name='project-detail'),
)
And the html error :
Error during template rendering
In template /var/www/webapps/blippar_website/app/blippar/templates/djangocms_showroom/project_list.html, error at line 14
Which is :
<form id="projects-filter" action="{% url 'djangocms_showroom:project-list' %}">
Well, I am not very good yet with django and django-cms, if someone has any clue, it would be marvellous !

How to reverse a custom admin url in Django 1.5?

I want to add a custom url for a ModelAdmin but it seems Django changed template syntax in 1.5. I tried this way as the documentation pointed out but unfortunately it doesn't work.
class VariableAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(VariableAdmin, self).get_urls()
my_urls = patterns('',
url(r'^settings/([A-Za-z0-9]*)', self.admin_site.admin_view(self.settings), name="settings"))
return my_urls + urls
def settings(self, request, category):
return render_to_response('variables.html', {"opts": Variable._meta}))
Here is my variables.html
{% load admin_urls %}
{% url opts|admin_urlname:'settings' %}
This code throws an error like this:
Reverse for 'common_variable_settings' with arguments '()' and keyword arguments '{}' not found.
How can I fix this problem?
Try changing name of the view to include application and model names:
...
my_urls = patterns('', url(r'^settings/([A-Za-z0-9]*)',
self.admin_site.admin_view(self.settings),
name="common_variable_settings"))
...
The admin_urlname template filter returns "full" names, see it's source code:
#register.filter
def admin_urlname(value, arg):
return 'admin:%s_%s_%s' % (value.app_label, value.module_name, arg)
So definitely You need to name Your view "appname_modulename_settings". Maybe then try changing regular expression to something like this:
r'^settings/([A-Za-z0-9]+/)?$'

Categories

Resources