Using multiple slugs in a url - regex

I want to keep my urls dynamic as well as clean.
Therefore I'm using slugs.
My problem right now is, that I get the following error:
redefinition of group name 'slug' as group 2; was group 1 at position 42
I think I get that error, because I have two slugs in my chain.
For reference I have a ListView into ListView into an UpdateView, alls importet from django.views.generic. The first list view gives me the first slug and the update view the second.
Here is the url pattern (spread across the apps):
First list view:
urlpatterns = [
url(r'^$', RestaurantListView.as_view(), name='restaurant-list'),
url(r'^(?P<slug>[\w-]+)/menus/', include('menu.urls', namespace='menu')),
]
Second list view:
urlpatterns = [
url(r'^$', MenuListView.as_view(), name='menu-list'),
url(r'^(?P<slug>[\w-]+)/', MenuUpdateView.as_view(), name='menu-detail'),
]
In the templates I get the objects via:
<li><a href='{{obj.get_absolute_url}}'> {{obj}} </a></li>
Which I have defined in the respective models:
def get_absolute_url(self):
return reverse('restaurants:menu:menu-detail', kwargs={'slug': self.slug})
and
def get_absolute_url(self):
return reverse('restaurants:menu:menu-list', kwargs={'slug': self.slug})
So the resulting pattern at the end is:
restaurants/(?P<slug>[\w-]+)/menus/(?P<slug>[\w-]+)/
How can I fix it so that I don't get the error anymore?

from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.special_case_2003),
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
for more details https://docs.djangoproject.com/en/1.11/topics/http/urls/

Related

Django optional URL captured value for rest APIView

For following Django code using django_rest_framework:
class PollMessageView(generics.ListAPIView):
serializer_class = MessageSerializer
lookup_url_kwarg = 'count'
def get_queryset(self):
count = self.kwargs.get(self.lookup_url_kwarg)
queryset = Message.objects.all()[:count]
return queryset
urlpatterns = [
path('poll_message/<int:count>', PollMessageView.as_view(), name="poll_message"),
]
How do I make the count parameter optional in the URL pattern? For example, if I visit /poll_message/ without a count, it would still call the PollMessageView (and I can set a default number for count if it's missing)?
create a new path as ,
urlpatterns = [
path('poll_message/', PollMessageView.as_view(), name="poll_message-wo-count"),
path('poll_message/<int:count>', PollMessageView.as_view(), name="poll_message"),
]
# URLconf
from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.page),
path('blog/page<int:num>/', views.page),
]
# View (in blog/views.py)
def page(request, num=1):
# Output the appropriate page of blog entries, according to num.
...
In detail reference - https://docs.djangoproject.com/en/3.0/topics/http/urls/#specifying-defaults-for-view-arguments
You can specify defaults for view arguments, but the example on the website is for functional view, not sure how would that work for class based view or in this case, the rest framework class based view.
However, you can add another path with extra options to achieve the same thing
path('poll_message/', PollMessageView.as_view(), {
"count": 5
}, name="poll_message"),

Django REST framework RetrieveAPIView gets empty "id" parameter and returns 404 error

I use Django 1.11 + Django REST Framework. I'm trying to get detailed data about distinct record using generic RetrieveAPIView from Django REST Framework and server returns "HTTP 404 Not Found" result, i.e. no data.
models.py file:
class TestPage( models.Model):
title = models.CharField( size = 120)
counter = models.IntegerField( default = 0)
def __unicode__(self):
return u'' + self.title
serializers.py file:
class TestPageSerializer(serializers.ModelSerializer):
class Meta:
fields = ('id', 'title', 'counter',)
model = models.TestPage
urls.py file:
urlpatterns = [
url( r'^admin/', admin.site.urls),
url( r'^api/', include( 'rest_framework.urls')),
url( r'^api/', include( 'testpages.urls')),
]
testpages/urls.py file:
urlpatterns = [
url( r'^$', views.TestPageList.as_view()),
url( r'^(?P<id>)\d+/$', views.TestPageDetail.as_view()),
]
and at last views.py file:
class TestPageList(generics.ListAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
class TestPageDetail(generics.RetrieveAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
# def get_object( self):
# print self.kwargs
If I request "http://127.0.0.1:8000/api/" for all records in the list, server returns all records. But if I want to get record by id using "http://127.0.0.1:8000/api/1/" for example, I get the empty result like in the photo below.
[![enter image description here][1]][1]
If I uncomment get_object()-function in last 2 lines, I can see {'id': u''} in terminal i.e. server gets empty parameter 'id'.
What wrong with this code?
The issue is your regular expression matching for id, you're putting \d+ outside of the matching group when it's actually what you want to match:
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view())
By the way, to be good REST citizen, you shouldn't add the / at the end of a URL for a specific object.
Note: As mentioned by JPG, adding a name for the resource you're fetching (plural) would be proper RESTful:
url(r'^pages/(?P<id>\d+)$', views.TestPageDetail.as_view())
This way, you fetch page nr. 1 at /api/pages/1
The original problem was the closing parathesis (the regex expression), you were misplaced that.
urlpatterns = [
url(r'^$', views.TestPageList.as_view()),
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view()),
^^^ here
]
Apart from that, You should change the urls patterns to a sensible form, as
#testpages/urls.py
urlpatterns = [
url(r'^test/$', views.TestPageList.as_view()),
url(r'^test/(?P<id>\d+)/$', views.TestPageDetail.as_view()),
]
then the list-api will be available on /api/test/ endpoint and the detail-api will be available on /api/test/{id}/

r'^(?P<pk>\d+)$ URL 404ing

I'm trying to use this link <a href="Annotation/{{ vod.id }}"> to load another page in my website base on primary key of videos. My url file is as follows:
urlpatterns = [
url(r'^$', ListView.as_view(queryset=Vod.objects.all().order_by("-date")[:25], template_name="Annotation/home.html")),
url(r'^(?P<pk>\d+)$', ListView.as_view(queryset=Vod.objects.get(pk=1).posts.all().order_by("-date"), template_name="Annotation/post.html")),
]
I get the standard 404 from the links generated using the aforementioned link.
Thanks!
edit: Added the base URLs
url(r'^admin/', admin.site.urls),
url(r'^Annotation', include('Annotation.urls')),
url(r'^Profile', include('Profile.urls')),
This is the URL for Profile.urls
url(r'^$', views.index, name='index'),
edit2: Changed URL and added view I'm trying to use.
url(r'^(?P<key>[0-9]+)$', views.post, name="post")
Here's views.post
def post(request, key):
try:
target_vod = Vod.objects.get(pk=key)
except Target.DoesNotExist:
raise Http404("Vod does not exist")
target_posts = Vod.objects.get(pk=key).posts.all().order_by("-date")
context = {'target_vod': target_vod, 'target_posts': target_posts}
return render(request, 'Annotation/post.html', context)
I have seperated the code and put it into a views.py. This should work.
urls.py
url(r'^(?P<pk>[0-9]+)$', AnnoList.as_view(), template_name="Annotation/post.html")),
Then in views.py:
class AnnoList(ListView):
template_name = 'Annotation/post.html'
def get_queryset(self):
self.vod = get_object_or_404(Vod, pk=self.args[0])
return Posts.objects.filter(vod=self.vod).all().order_by("-date")
I assuming here that you have two different tables and that Posts has a foreign key to Vod.

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

merge 2 django (very similar) urlconfs in one

I have an app called "products" that manages "products" and "categories". And I have products/views.py (with generic views) that goes like this:
Objects = {
'products': {'model':Product, 'form':ProductForm}
'categories': {'model':Category, 'form':CategoryForm}
}
and something like this:
def list(request, obj):
model = Objects[obj]['model']
queryset = model.objects.all()
return object_list(request, queryset=queryset)
and then my project urls.py is something like this:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^products/', include('products.product_urls.py'), {obj:'product'}),
(r'^categories/', include('products.category_urls.py'), {obj:'category'}),
)
and then I have the two urls.py for category and product like this:
1) products/product_urls.py
urlpatterns = patterns('',
url(r'^$', 'products.views', name='products-list'),
)
2) and a very similar line in products/category_urls.py
urlpatterns = patterns('',
url(r'^$', 'products.views', name='categories-list'),
)
As you can see, products/product_urls.py and products/category_urls.py are really very similar except for the url names.
My question is: is there a smart technique to "merge" products/product_urls.py and products/category_urls.py into a single module and still have different names for the urls depending on the "object" they're working on. i.e. have a single products/urls.py that'll manage both objects: product and category
You can maybe include the same url module twice, but use namespaced urls!
To me, this seems obvious:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
url(r'^products/$', 'products.views.list', {'obj':'product'}, name='products-list'),
url(r'^categories/$', 'products.views.list', {'obj':'category'}, name='categories-list'),
)
The url function only differs from the tuple approach in that you can use keyword args (like name) in it.
Your code seems like it would break if you were to really try it verbatim. This, along with the perceived obviousness, makes me wonder if your actual use case is more complicated and requires a different answer.
Furthermore, the object-list generic view already employs the functionality you're trying to create with your Objects approach. (See the queryset argument; also, create-object's form_class arg). An example:
from django.conf.urls.defaults import *
from models import Product, Category
from django.views.generic.list_detail import object_list
urlpatterns = patterns('',
url(r'^products/$',
object_list,
{'queryset': Product.objects.all()},
name='products-list'),
url(r'^categories/$',
object_list,
{'queryset': Category.objects.all()},
name='categories-list'),
)