How to reverse a custom admin url in Django 1.5? - python

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]+/)?$'

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.

NoReverseMatch for the new object

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.

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 generate urls in django

In Django's template language, you can use {% url [viewname] [args] %} to generate a URL to a specific view with parameters. How can you programatically do the same in Python code?
What I need is to create a list of menu items where each item has name, URL, and an active flag (whether it's the current page or not). This is because it will be a lot cleaner to do this in Python than the template language.
If you need to use something similar to the {% url %} template tag in your code, Django provides the django.core.urlresolvers.reverse(). The reverse function has the following signature:
reverse(viewname, urlconf=None, args=None, kwargs=None)
https://docs.djangoproject.com/en/dev/ref/urlresolvers/
At the time of this edit the import is django.urls import reverse
I'm using two different approaches in my models.py. The first is the permalink decorator:
from django.db.models import permalink
def get_absolute_url(self):
"""Construct the absolute URL for this Item."""
return ('project.app.views.view_name', [str(self.id)])
get_absolute_url = permalink(get_absolute_url)
You can also call reverse directly:
from django.core.urlresolvers import reverse
def get_absolute_url(self):
"""Construct the absolute URL for this Item."""
return reverse('project.app.views.view_name', None, [str(self.id)])
For Python3 and Django 2:
from django.urls import reverse
url = reverse('my_app:endpoint', kwargs={'arg1': arg_1})
Be aware that using reverse() requires that your urlconf module is 100% error free and can be processed - iow no ViewDoesNotExist errors or so, or you get the dreaded NoReverseMatch exception (errors in templates usually fail silently resulting in None).

Categories

Resources