RedirectView give NoReverseMatch - django

Recently I've changed the paths of the blog posts to give more legible the urls.
Before I've this paths:
path("category/<slug:slug_category>/", views.singleCategory_postList, name="single_category"),
path("<slug:slug_post>/", views.singlePost, name='single_blog_post'),
Now I've this:
path("<slug:slug_category>/", views.singleCategory_postList, name="single_category"),
path("<slug:slug_category>/<slug:slug_post>/", views.singlePost, name='single_blog_post'),
Before get_absolute_url was this:
class Category(models.Model):
.....
def get_absolute_url(self):
return reverse("single_category", kwargs={"slug_category": self.slug_category})
class BlogPost(ModelPost, TimeManager):
.....
def get_absolute_url(self):
return reverse("single_blog_post", kwargs={"slug_post": self.slug_post})
Now is this:
class Category(models.Model):
.....
def get_absolute_url(self):
return reverse("single_category", kwargs={"slug_category": self.slug_category})
class BlogPost(ModelPost, TimeManager):
.....
def get_absolute_url(self):
return reverse("single_blog_post", kwargs={
"slug_post": self.slug_post,
"slug_category": self.category.slug_category,
})
I'm trying to use the RedirectView for redirect all the old paths; then into urls.py I've this now:
path("category/<slug:slug_category>/", RedirectView.as_view(pattern_name='single_category', permanent=True)),
path("<slug:slug_post>/", RedirectView.as_view(pattern_name='single_blog_post', permanent=True)),
path("categorie/", views.categoryList, name="list_category"),
path("<slug:slug_category>/", views.singleCategory_postList, name="single_category"),
path("", views.postList, name='list_post'),
path("<slug:slug_category>/<slug:slug_post>/", views.singlePost, name='single_blog_post'),
When I use those RedirectView it's shown me this error:
NoReverseMatch at /blog/gis/
Reverse for 'single_blog_post' with keyword arguments '{'slug_post':
'gis'}' not found. 1 pattern(s) tried:
['blog\/(?P[-a-zA-Z0-9_]+)\/(?P[-a-zA-Z0-9_]+)\/$']
If I comment the two RedirectView paths the error disappear and I can use the site without problems but when I use the old paths I see 404 error.
I don't have understood how RedirectView works. Someone can give me an example?

The problem is that your old view only required the post slug, but your new one additionally requires the category slug. However, there's no way a simple RedirectView can do this redirection, because it can only look at the arguments passed in the original URL itself. It doesn't know how to go to the database to find the category slug and use it in the redirection.
So, you'll need to write the redirect view yourself. It could be pretty simple:
def redirect_with_category(request, slug):
post = get_object_or_404(Post, slug=slug)
return redirect(post, permanent=True)
Note that redirect will automatically call the get_absolute_url method of the Post. Also note you want to use permanent=True to return a 301 rather than a 302.

Related

Wagtail: Add method to PageModel to get an inspect url

I am trying to add a dynamic instead of a hard coded get_inspect_url method to a PageModel:
class MyModel(Page):
# ...
# this is what I have now, which breaks in prod due to a different admin url
def get_inspect_url(self):
inspect_url = f"/admin/myapp/mymodel/inspect/{self.id}/"
return inspect_url
I know that you can use the ModelAdmin.url_helper_class in wagtail_hooks.py to get admin urls for an object, but I can't figure a way to do the same on the model level as a class method. Is there a way?
thanks to #Andrey Maslov tip I found a way:
from django.urls import reverse
def get_inspect_url(self):
return reverse(
f"{self._meta.app_label}_{self._meta.model_name}_modeladmin_inspect",
args=[self.id],
)
The basic form of a Wagtail admin url is:
[app_label]_[model_name]_modeladmin_[action]
Just change the [action] to one of the following to generated other instance level admin urls:
edit
delete
if you have in you urls.py line like this
path(MyModel/view/<id:int>/', YourViewName, name='mymodel-info'),
then you can add to your models.py this lines
from django.urls import reverse
...
class MyModel(Page):
def get_inspect_url(self):
inspect_url = reverse('mymodel-info', kwargs={'id': self.id})
return inspect_url

How to redirect url with added url parameters

I have a workout calendar app where if a user goes to /workoutcal, I want them to be redirected to workoutcal/<today's year>/<today's month>. That means I want them to be redirected to this route, should they go to /workoutcal:
url(r'^(?P<year>[0-9]+)/(?P<month>[1-9]|1[0-2])$', views.calendar, name='calendar'),
So how can I write a new url pattern in urls.py that does something like:
url(r'^$', RedirectView().as_view(url=reverse_lazy(),todays_year, todays_month)),
You can subclass RedirectView, override get_redirect_url, and reverse the url there.
class MonthRedirectView(RedirectView):
def get_redirect_url(*args, **kwargs):
today = timezone.now()
return reverse(calendar, args=[today.year, today.month])
Then include your view in the URL pattern:
url(r'^$', MonthRedirectView().as_view()),
I found another solution to my problem, which is making a view that simply calls the calendar with the right arguments:
the url:
url(r'^$', views.redirect_to_calendar),
the redirect view:
def redirect_to_calendar(request):
today = timezone.now()
return calendar(request, year = today.year, month = today.month)
the view we serve to the user:
def calendar(request, year = None, month = None):
## A bunch of server logic
return HttpResponse(template.render(context, request))
Assuming your RedirectView is in your app's urls.py:
url(r'^(?P<year>[0-9]+)/(?P<month>[1-9]|1[0-2])$', views.calendar, name='calendar'),
url(r'^$', RedirectView().as_view(url='{}/{}'.format(todays_year, todays_month)),
EDIT: This assumes that todays_year and todays_month are calculated when a user goes to /workoutcal/, which they aren't (they're loaded at URL load time). See Alasdair's answer.

DJANGO Generic Views: How to use reverse() in get_absolute_url method?

I'm trying to implement generic editing views as shown here:
I started with the CreateView which renders and submits data correctly. However, I am getting an error when I tries to use reverse() to return to the detail view page for the new object.
Here is my Error message:
NoReverseMatch at /work/clients/create/
Reverse for 'ClientDetailView' with arguments '('14',)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
Here is how I defined get_absolute_url() in my model:
def get_absolute_url(self):
return reverse('ClientDetailView', kwargs={'pk': self.pk})
My view is called ClientDetailView. I'm not sure what other information would be helpful.
Here is class ClientDetailView:
class ClientDetailView(generic.DetailView):
model = Client
template_name = 'work/client_detail.html'`
and here is url() from urls.py:
url(r'^clients/(?P<pk>[0-9]+)/$', views.ClientDetailView.as_view(), name='clients_detail'),`
Can anyone explain what I am doing wrong?
I solved my own problem. I had to add the namespace to the reverse() method:
return reverse('work:clients_detail', kwargs={'pk': self.pk})
I would appreciate if someone else could explain why I needed to do this.
Here is the my complete urls.py:
from django.conf.urls import url
from . import views
app_name = 'work'
urlpatterns = [
url(r'^work_orders/$', views.WorkOrdersIndexView.as_view(), name='quotes_index'),
url(r'^work_orders/(?P<pk>[0-9]+)/$', views.WorkOrdersDetailView.as_view(), name='work_orders_detail'),
url(r'^quotes/$', views.QuotesIndexView.as_view(), name='quotes_index'),
url(r'^quotes/(?P<pk>[0-9]+)/$', views.QuotesDetailView.as_view(), name='quotes_detail'),
url(r'^project/(?P<pk>[0-9]+)/$', views.ProjectDetailView.as_view(), name='project_detail'),
url(r'^project/create/$', views.ProjectCreateView.as_view(), name='project_create'),
url(r'^project/(?P<pk>[0-9]+)/update/$', views.ProjectUpdateView.as_view(), name='project_update'),
url(r'^project/(?P<pk>[0-9]+)/delete/$', views.ProjectDeleteView.as_view(), name='project_delete'),
url(r'^clients/$', views.ClientView.as_view(), name='client_index'),
url(r'^clients/(?P<pk>[0-9]+)/$', views.ClientDetailView.as_view(), name='clients_detail'),
url(r'^clients/create/$', views.ClientCreateView.as_view(), name='client_create'),
url(r'^clients/(?P<pk>[0-9]+)/update/$', views.ClientUpdateView.as_view(), name='clients_update'),
url(r'^clients/(?P<pk>[0-9]+)/delete/$', views.ClientDeleteView.as_view(), name='clients_delete'),
]
actually you are trying to reverse the view, instead of ClientDetailView
use url name clients_detail

NoReverseMatch error while redirecting to results page

I have intended to write an search application by Django.
I am getting a NoReverseMatch error when I redirect to results page.
I posted my codes here http://pastebin.com/AL4rG9NU
Or you can read it below
urls.py
urlpatterns = patterns('pylucene.views',
(r'^$', 'index'),
(r'^search/$', 'search'),
)
views.py
def index(request):
return render_to_response('pylucene/index.html', context_instance=RequestContext(request))
def search(request):
query = request.POST['query']
search_results = search_files(query)
return HttpResponseRedirect(reverse('pylucene.views.results', args=(search_results,)))
The Error:
NoReverseMatch at /pylucene/search/
Reverse for 'pylucene.views.results' with arguments
'([(u'Documents\\Dieu khien may tinh bang y nghi.txt', u'Dieu khien
may tinh bang y nghi.txt'), '1 total matching documents.'],)' and
keyword arguments '{}' not found.
def results(request, search_results):
return render_to_response('pylucene/results.html', {'search_results': search_results}, context_instance=RequestContext(request))
I read several similar topics but I can not solve my problem.
Please help me.
Thank you so much.
I think that you are not undestanding how the reverse function works and what are you trying is just not posible.
For the reverse function your url must be declared on urls.py for example:
#urls.py:
urlpatterns = patterns('blog.views',
(r'^$', 'index'),
url(r'^blog/(?P<slug>\d{4})/$', 'blog', name="blog-detail"),
)
Now in your code you can do
reverse('blog-detail', args=('django-urls',))
# the resolt of this is
# /blog/django-urls/
And this is how reverse works.
The real answer
I think that you do not need 2 views, but if you really want to: you have to do this to pass all the query already performed
def search(request):
query = request.POST['query']
search_results = search_files(query)
return results(request, search_results)
but i think that the best that you can do is this (using GET):
def search(request):
query = request.GET['query'] # I think using get is better because you are GETing info
search_results = search_files(query) # better if returns a generator
return render_to_response('pylucene/results.html',
{'search_results': search_results},
context_instance=RequestContext(request)

Django - reversing wrapped view functions

I am trying to incorporate django-schedule into my project. Django-schedule's source is here. I don't like the urls, because they all capture a slug. My project will only allow one calendar per user, so it doesn't make sense to capture the slug. So, I wrapped the django-schedule views like this (look up the slug using the current user, and pass it to django-schedule's views):
from schedule.views import calendar_by_periods
from schedule.models import Calendar
from schedule.periods import Month
def cal_by_periods_wrapper(view):
def new_view(request, *args, **kwargs):
kwargs['calendar_slug'] = Calendar.objects.get_calendars_for_object(obj=request.user, distinction="owner")[0].slug
return view(request, *args, **kwargs)
return new_view
And here is the relevant section from urls.py:
urlpatterns = patterns('',
url(r'^$',
cal_by_periods_wrapper(calendar_by_periods),
name = "month_calendar",
kwargs={'periods': [Month], 'template_name': 'schedule/calendar_month.html'}),
This works fine until it hits one of the template tags included with django-schedule, prev_url:
#register.simple_tag
def prev_url(target, slug, period):
return '%s%s' % (
reverse(target, kwargs=dict(calendar_slug=slug)),
querystring_for_date(period.prev().start))
This function raises:
TemplateSyntaxError at /teacher/calendar/
Caught an exception while rendering: Reverse for 'month_calendar' with arguments
'()' and keyword arguments '{'calendar_slug': u'asdf'}' not found.
How can I wrap this view and still make the reverse call work?
This has nothing to do with wrapping the function. It's just that you no longer have a URL with the name 'month_calendar' which takes a 'calendar_slug' argument. Either define one in your urlconf, or edit the templatetag.
Edit after comment Yes but the 'reverse' call is still passing a slug argument, and there's no 'month_calendar' url which takes one, so the reverse match fails.