Passing two arguments through URL - python

I have this link in my template:
See More
Here are my app urls:
from django.conf.urls import url
from listings import views
app_name = 'listings'
urlpatterns = [
url(r'^$',views.UniversityListView.as_view(),name='universities'),
url(r'^(?P<l_slug>[-\w]+)$',views.ListingView.as_view(),name='listing_detail'),
]
And here is my view associated with it:
class UniversityHomePageView(ListView):
model = Listing
context_object_name = 'listing'
template_name_suffix = '_university_homepage'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['university'] = University.objects.all()
context['company'] = Company.objects.all()
return context
I want this link to go to the the listings page however it is giving me an error:
NoReverseMatch at /tu/
Reverse for 'listing_detail' with keyword arguments '{'name_initials': '', 'l_slug': 'stafford-apartments'}' not found. 1 pattern(s) tried: ['(?P<name_initials>\\w+)/(?P<l_slug>[-\\w]+)$']
I am not really sure what to do. I think I need to be passing two variables to the view but if what I am doing isn't correct then I am a little lost on what to do, I have read the docs but don't quite no what to do still.
Using Django 1.11.

Related

Django - URL not found using generic views

I am writing a basic Events app which contains of two modules(apps) so far : users and events.
I am using Django 2.1 with Python 3.6 on Ubuntu 16.04
So far, I've been able to handle users, but on events, I can't use Update, Detail and Delete generic views. All of them return 404.
My views.py:
class EventListView(ListView):
model = EventModel
template_name = 'event_list.html'
queryset = EventModel.objects.order_by('start_date_time')
class EventUpdateView(UpdateView):
model = EventModel
fields = ['event_type','start_date_time'
]
template_name = 'event_update.html'
class EventDeleteView(DeleteView):
model = EventModel
template_name = 'event_delete.html'
success_url = reverse_lazy('event_list')
class EventDetailView(DetailView):
model = EventModel
template_name = 'event_detail.html'
My urls.py (in project folder):
urlpatterns = [
path('', include('pages.urls')),
path('admin/', admin.site.urls),
path('users/', include('users.urls')),
path('users/', include('django.contrib.auth.urls')),
path('events/', include('events.urls')),
]
My urls.py (in events app):
urlpatterns = [
path('', views.EventListView.as_view(), name='event_list'),
path('<int:id>', views.EventDetailView.as_view(), name='event_detail'),
path('<int:id>/edit/', views.EventUpdateView.as_view(), name='event_update'),
path('<int:id>/delete/', views.EventDeleteView.as_view(), name='event_delete'),
]
What am I doing wrong? I've been searching the whole day and still have no idea how this might be wrong.
Note that the first line works (EventListView) but the other lines don't. By the way, I am using the book Django for Beginners. Most of the code here is identical to the code in the book.
Update
I don't use namespace in this application, the rest of urls.py is only some basic imports :
from django.urls import path
from . import views
The urls.py for the Project is like above, except it has include and admin as well.
The examples of URLs giving 404 error:
http://127.0.0.1:8000/events/1/
http://127.0.0.1:8000/events/1/edit/
PS I thought edit and delete give me 404, but actually the error is :
ImproperlyConfigured at /events/1/edit/
EventUpdateView is missing a QuerySet. Define EventUpdateView.model, EventUpdateView.queryset, or override EventUpdateView.get_queryset().)
In short: you defined a models (with s) attribute, but it should be model (without s).
Well the error actually already explains the problem:
ImproperlyConfigured at /events/1/edit/ EventUpdateView is missing a QuerySet.
Define EventUpdateView.model, EventUpdateView.queryset,
or override EventUpdateView.get_queryset().)
In your EventUpdateView you did not specify a model attribute, you wrote models, and for Django that is an entirely different attribute. So you should rename it to:
class EventListView(ListView):
model = EventModel
template_name = 'event_list.html'
queryset = EventModel.objects.order_by('start_date_time')
class EventUpdateView(UpdateView):
model = EventModel
fields = ['event_type','start_date_time'
]
template_name = 'event_update.html'
class EventDeleteView(DeleteView):
model = EventModel
template_name = 'event_delete.html'
success_url = reverse_lazy('event_list')
class EventDetailView(DetailView):
model = EventModel
template_name = 'event_detail.html'
For the EventListView, that did not matter, since you also defined a queryset attribute, and so Django took that one, but I would update it anyway.
Furthermore in the urls.py, you need to specify a pk parameter by default:
urlpatterns = [
path('', views.EventListView.as_view(), name='event_list'),
path('<int:pk>', views.EventDetailView.as_view(), name='event_detail'),
path('<int:pk>/edit/', views.EventUpdateView.as_view(), name='event_update'),
path('<int:pk>/delete/', views.EventDeleteView.as_view(), name='event_delete'),
]
Finally in the template you wrote something like:
{% url 'event_update' event.id %}
But apparently there was no event identifier, as a result the event.id is the string_if_invalid (by default the empty string), which is not an integer (well at least not if you did not specify that), and hence it can not find a relevant URL. After some discussion, it turned out that the correct identifier was object, so the correct url is something like:
{% url 'event_update' pk=object.id %}
The same of course should happen with other {% url ... %} calls.

Django Urls - two views with same regular expression

I have two views with the same regular expression, as you can see below. It's a Category and an Article view, their entry slugs will never be the same so it should be no problem. But at the moment it doesn't work well, as you prolly know the category-view will get triggered.
Please do not suggest to make the url structure unique, the slugs of categories and articles will never be the same. It should be as short as possible.
urls.py
urlpatterns = [
url(r'^index', index.Index.as_view(), name='index'),
url(r'^search', search.Index.as_view(), name='search'),
url(r'^(?P<slug>.+)$', category.Index.as_view(), name='category'),
url(r'^(?P<slug>.+)$', article.Index.as_view(), name='article'),
]
I tried to reverse from views.category back to urls.py if there is no category to find like this:
views.category.py
class Index(View):
def get(self, request, slug):
category = CategoryModel.objects.get(slug=slug)
if category is None:
return HttpResponseRedirect(reverse('article', args=[slug]))
context = {
'category': category
}
return render(request, 'category/index.html', context)
The error (but there is a article with slug 'test123'):
NoReverseMatch at /wiki/test123
Reverse for 'article' with arguments '('test123',)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
Using Python 3.6
Why dont you try differentiating the URLs like so
urlpatterns = [
url(r'^index', index.Index.as_view(), name='index'),
url(r'^search', search.Index.as_view(), name='search'),
url(r'^category/(?P<slug>.+)$', category.Index.as_view(), name='category'),
url(r'^article/(?P<slug>.+)$', article.Index.as_view(), name='article'),
]
You get to use the same regular expressions without the URL being ambiguous.
You can remove the article.Index view and rather than trying to redirect when there's no object for Category, you can call the method you defined in article.Index with the same parameters as the get method takes in article.Index view.
Example:
urls.py
urlpatterns = [
url(r'^index', index.Index.as_view(), name='index'),
url(r'^search', search.Index.as_view(), name='search'),
url(r'^(?P<slug>.+)$', category.Index.as_view(), name='category'),
# article url removed
]
views.category.py
from path.to.article import Index as ArticleIndexView
class Index(View):
def get(self, request, slug):
category = CategoryModel.objects.get(slug=slug)
if category is None:
# calling article app's Index get method
article_index_view_obj = ArticleIndexView()
return article_index_view_obj.get(request, slug)
context = {
'category': category
}
return render(request, 'category/index.html', context)
If you make the article.Index class view as function-based view.
You can import from path.to.article import index as article_index
and then, instead of instantiating the object, you can directly call article_index(request, slug).
There are several issues here.
(1) You call reverse with args but you have specified a kwarg.
if category is None:
return HttpResponseRedirect(reverse('article', args=[slug]))
Reverse for 'article' with arguments '('test123',)' and keyword arguments '{}' not found.
Says exactly that - as you did not provide the keyword argument, it could not find the matching URL pattern. This would be correct:
if category is None:
return HttpResponseRedirect(reverse('article', kwargs={'slug':slug}))
(2) You will end up in that same code again and again - is what I expect to happen once you fix (1). Because if reverse really does reverse the URL - the result of reverse will of course also match the category URL which means it will simply call the category.Index view - again.
I think your URL setup could actually work because the resolver does try all URLs sequentially until one comes along that matches. I'm just not sure if you can make the view return something that will lead to the URL resolver to kick in and decide to take the next URL (article) instead of category which just resolved. Probably not.
In this case, if you are fine with redirects, you could just define 3 URL patterns. 1 for the view that will operate as a switch and redirect to the CategoryView or ArticleView respectively.
Otherwise go with Sachin Kukreja's solution of handling both in one view.
Finally I made it like this. Actually I wanna use 2 different views but I guess it's fine too. Does someone see a mistake?
class Index(View):
def get(self, request, slug):
self.request = request
self.slug = slug
self.item = None
is_article = ArticleModel.objects.filter(slug=self.slug).exists()
if is_article:
self.item = 'article'
return self.article()
is_category = CategoryModel.objects.filter(slug=self.slug).exists()
if is_category:
self.item = 'category'
return self.category()
self.item = None
# 404 here
return HttpResponse(self.item)
def article(self):
article = ArticleModel.objects.get(slug=self.slug)
context = {
'article': article
}
return render(self.request, 'article/index.html', context)
def category(self):
category = CategoryModel.objects.get(slug=self.slug)
context = {
'category': category
}
return render(self.request, 'article/index.html', context)

Reverse not found in Django

This might be a simple one but I have been on this for hours, I must be missing something. Here we go:
urls.py:
urlpatterns = [
# Event patterns
url('^$', views.BuddyProgram.as_view(), name='buddy_program'),
url('^dashboard/$', views.BuddyDashboard.as_view(), name='buddy_dashboard'),
url('^thank-you/$', views.BuddyFinal.as_view(), name='final'),
url('^completed_intro/$', views.CompletedIntro.as_view(), name='buddy_completed_intro'),
url('^completed_passive_track/$', views.CompletedPassiveTrack.as_view(), name='buddy_completed_passive_track'),
url('^about/$', views.BuddyAbout.as_view(), name='buddy_about'),
url('^list/$', views.Buddies.as_view(model=BuddyProfile), name='buddies'),
url('^signup/$', views.BuddySignupView.as_view(), name='buddy_signup'),
# url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
url(r'^(?P<buddy_id>[0-9]+)/$', views.Buddy.as_view(model=BuddyProfile), name='buddy'),
]
views.py:
class BuddyFinal(TemplateView):
template_name = 'buddy/thank_you.html'
class BuddySignupView(SignupView):
template_name = 'buddy/buddy_create.html'
success_url = reverse('final') # profile specific success url
form_class = BuddySignupForm
profile_class = BuddyProfile # profile class goes here
def form_valid(self, form):
response = super(BuddySignupView, self).form_valid(form)
profile = self.profile_class(user=self.user)
profile.save()
return response
and the error I get:
django.core.urlresolvers.NoReverseMatch: Reverse for 'final' with arguments '()' and keyword arguments '{}' not found. 0 pattern(s) tried: []
As your URLs aren't loaded yet when importing the BuddySignupView (and thus executing reverse), Django cannot find the URL.
You should use reverse_lazy instead: https://docs.djangoproject.com/en/1.11/ref/urlresolvers/#reverse-lazy
In your views.py file:
from django.core.urlresolvers import reverse_lazy
class BuddySignupView(SignupView):
template_name = 'buddy/buddy_create.html'
success_url = reverse_lazy('final') # profile specific success url
reverse_lazy only reverse the URL name at "runtime" (when Django actually needs the value) instead of "import time" when everything may not be available yet.
This error is coming because in any template file you are accecsing url(thank-you or signup) in wrong format. make sure about this url in html.

readonly_fields returns empty value in django inlined models

i am new in django framework, in my current try, i have two models (Client and Facture). Facture is displayed as a TabularInline in client change view.
i want display a link for each inlined facture object to download the file. so i added a custom view that download the facture file, but dont know how to link to it
class Client(models.Model):
...
class Facture(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
numero = models.IntegerField(unique=True, default=rand_code)
...
and in the admin.py:
class FactureInline(admin.TabularInline):
model = Facture
extra = 0
readonly_fields = ('numero', 'dl_link')
def DLFacture(self, request, obj):
...
response.write(pdf)
return response
def get_urls(self):
urls = super(FactureAdmin, self).get_urls()
from django.conf.urls import url
download_url = [
url(r'^(?P<pk>\d+)/download/$', self.admin_site.admin_view(self.DLFacture), name="download"),
]
return download_url + urls
def dl_link(self, obj):
from django.core.urlresolvers import reverse
return reverse("admin:clients_facture_download", args=[obj.pk])
admin.site.register(Facture, FactureAdmin)
class ClientAdmin(admin.ModelAdmin):
inlines = [
FactureInline,
]
admin.site.register(Client, ClientAdmin)
i get the following error:
Reverse for 'clients_facture_download' with arguments '(1,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
all works fine when i change the reverse url to
reverse("admin:clients_facture_change", args=[obj.pk])
so any one could help me know how to reverse the download view and if i am doing thinks right ?
thanks for any help
Firstly, you are using name='download', but trying to reverse clients_facture_download.
I would try changing the url from
url(r'^(?P<pk>\d+)/download/$', self.admin_site.admin_view(self.DLFacture), name="download"),
to
url(r'^(?P<pk>\d+)/download/$', self.admin_site.admin_view(self.DLFacture), name="clients_fracture_download"),
Secondly, InlineModelAdmin does not have a get_urls method. You should move it to your ClientAdmin class.
I would think you need to reverse the order in the url:
url(r'^download/(?P<pk>\d+)$', self.admin_site.admin_view(self.DLFacture), name="download"),
]

How to convert a Django ListView to work on the admin site?

I have a list view (for the admin site) that uses a template as follows:
class UserImageListPendingView(ListView):
model = UserImage
queryset = UserImage.objects.filter(status=ImageBase.PENDING)
template_name = 'userimage_list_pending.html'
context_object_name = 'userimage_list'
paginate_by = 5
#method_decorator(staff_member_required)
def dispatch(self, *args, **kwargs):
return super(UserImageListPendingView, self).dispatch(*args, **kwargs)
Although this works there are problems with putting the URL in urls.py:
urlpatterns = [
url(r'^admin/app/pendinguserimages/?$', login_required(
UserImageListPendingView.as_view()),
name='pendinguserimages'),
...
]
...as this stops the redirection working properly.
I did try to define the URL through admin.py:
def get_admin_urls(urls):
def get_urls():
return patterns('',
url(r'^app/pendinguserimages/?$',
UserImageListPendingView.as_view(), name='pendinguserimages'),
url(r'^app/checkuserimage/(?P<pk>[0-9]+)/?$',
userimage_check, name='checkuserimage'),
...
) + urls
return get_urls
admin_urls = get_admin_urls(admin.site.get_urls())
admin.site.get_urls = admin_urls
... but there was an error when reversing the checkuserimage URL.
How would I go about converting this view to fit in better with the admin site, but still use the template?
I didn't need to rewrite the ListView afterall. After defining the URLs in admin.py instead of in urls.py, all I needed to do was put "admin:" in front of the name when reversing the URL in the template, as follows:
check

Categories