Slug for url in django - django

I cant get my head around this problem. Went to countless sites and question but can't find why it doesn't work. Everything is imported.
The error I get after I run the server is: Reverse for 'random_book' with arguments '('',)' not found. 1 pattern(s) tried: ['book/(?P[-a-zA-Z0-9_]+)$']
It highlights this with red from template: {% url 'random_book' random_item.slug %}
Models:
class Books(models.Model):
title=models.CharField(max_length=200)
author=models.CharField(max_length=150)
description=models.TextField()
cover=models.ImageField(upload_to='images/', blank=True)
slug=models.SlugField(max_length=100, blank=True, unique=True)
genre=models.ManyToManyField(Genres)
def save(self, *args, **kwargs):
self.slug= slugify(self.title)
super().save(*args, **kwargs)
def __str__(self):
return self.title
def get_absolute_url(self):
kwargs=self.slug
return reverse('random_book', kwargs=kwargs)
Views:
def random_book(request, slug):
cartile=Books.objects.all()
random_item=random.choice(cartile)
return render(request, 'carti/book.html', context={"random_item": random_item})
Urls:
path('book/<slug:slug>', views.random_book, name="random_book"),
Template:
{{ random_item.title }}
Hope you guys can help me.
EDIT:
If anybody ever comes across this, I've fixed the problem myself and here's how:
The problem is with the view. I try to get the random book in the page that displays the book instead of home page where the button is pressed. Basically on the home page if you press the button it takes you to another page where the randomize process starts, hence it doesn't have any slug data to pass. You need to add there the random.choice().
Then, to get that random data and display it with it's unique slug, you just need to use book=Books.objects.get(slug=slug) on that random_book view. For some reason I still don't understand, by fetching the slug, somehow you get the data associated with that slug.

This is a "reverse using kwargs" type of problem. I suspect your error originates here:
def get_absolute_url(self):
kwargs=self.slug
return reverse('random_book', kwargs=kwargs)
This is not how you use reverse, see an example.
I would suspect that rewriting the code in this manner is going to fix the issue:
def get_absolute_url(self):
kwargs={'slug': self.slug}
return reverse('random_book', kwargs=kwargs)

Related

Django Detailview Error Handling

I have a scenario where I am trying to send different messages to the user in the event that they are not authorized to see a record or if the record does not exist. I have been able to get the interface to send a message to the user if a 404 occurs, but can't quite figure out the logic based on criteria of the record. I've been playing with get, get_object, or get_object_or_404 and nothing quite works.
Here is my DetailView..
class BookSearchDetailView(LoginRequiredMixin,DetailView):
model = Book
context_object_name = 'book_detail'
template_name = 'book/book_search_detail.html'
def get_object(self, queryset=None):
return get_object_or_404(Book, book_number=self.request.GET.get("q"))
def get(self, request, *args, **kwargs):
book_number=self.request.GET.get("q")
try:
self.object = self.get_object()
except Http404:
messages.add_message(self.request, messages.INFO, 'Book Number %s Not Found' % book_number )
return HttpResponseRedirect(reverse('Book:book_number_search'))
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
except Not a Preference:
Book_number exists but is not self.request.user.preferences.all()
I realize the except Not a Preference doesn't work, but I am illustrating what I am trying to do.
Here is my model.
class Book(models.Model):
book_name = models.CharField(max_length=80,null=True,unique=False)
book_number = models.IntegerField(editable=True,null=True)
Note I do not have a slug defined to my model. In the current code above, this doesn't cause any problems. When I have played with different code variations, one of the error messages I get is that DetailView must be called with a pk or a slug.
I am ultimately trying to filter the book so that if the user doesn't have attributes to allow them to see the book it would essentially return none.
I have also tried to override get_queryset, but when I use the code below,
def get_queryset(self):
queryset = super(BookSearchDetailView, self).get_queryset()
return queryset.filter(book_number__in=self.request.user.preferences.all())
However, when I define the code above, my DetailView does not seem to honor it or have any effect.
What I've read is that the get_object either gets the object or 404. I've got that piece work working fine. Just can't figure out how to incorporate a third variation of error message.
Thanks in advance for any thoughts.

Django - get_redirect_url() causes Page not found (404)

I'm trying to implement favorite feature so that user can choose favorite stores. I'm currently referencing the video https://www.youtube.com/watch?v=pkPRtQf6oQ8&t=542s and stuck with the beginning.
When I try to move to the url https://www.fake-domain.com/my-domain/like, it throws the error message saying No Store matches the given query. So, I guess self.kwargs.get("domainKey") this snippet seems to throw the error, but I don't know why.
I'm not sure if I'm showing my codes enough, so please let me know I need to show more.
models.py
class Store(models.Model):
...
domainKey = models.CharField(max_length=100)
likes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True)
...
urls.py
url(r'^(?P<store_domainKey>.*)/$', views.detail, name='detail'),
url(r'^(?P<store_domainKey>.*)/like/$', views.StoreLikeRedirect.as_view(), name='like'),
views.py
class StoreLikeRedirect(RedirectView):
def get_redirect_url(self, *args, **kwargs):
store_domainKey = self.kwargs.get("domainKey")
print(store_domainKey)
obj = get_object_or_404(Store, pk='store_domainKey')
return obj.get_absolute_url()
--------------------------------EDIT------------------------------
Revised the codes based on the feedback, but still not working.
When I typed the url, the terminal says the following:
None <-- this is shown by print(store_domainKey) in views.py
Not Found: /down-east/like/
Since the print function in views.py prints None, I think there's something wrong on the line store_domainKey = self.kwargs.get("domainKey"). The part self.kwargs.get() seems not working. In the example video at the top of the post, the guy used SlugField(), but I used CharField() for my domainKey. Can it be an issue to use self.kwargs.get()?
views.py
class StoreLikeRedirect(RedirectView):
def get_redirect_url(self, *args, **kwargs):
store_domainKey = self.kwargs.get("domainKey")
print(store_domainKey)
obj = get_object_or_404(Store, domainKey=store_domainKey)
return obj.get_absolute_url()
urls.py
url(r'^(?P<store_domainKey>.*)/like/$', views.StoreLikeRedirect.as_view(), name='like'),
url(r'^(?P<store_domainKey>.*)/$', views.detail, name='detail'),
models.py
class Store(models.Model):
...
domainKey = models.CharField(max_length=100)
likes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True)
...
def get_absolute_url(self):
return reverse('boutique:detail', kwargs={"domainKey":self.domainKey})
----------------------------2nd Update-----------------------------
So, now self.kwargs.get("domainKey") returns the domain key very well!
but having
NoReverseMatch at /down-east/like/
Reverse for 'detail' with arguments '()' and keyword arguments '{'domainKey': 'down-east'}' not found. 1 pattern(s) tried: ['(?P<store_domainKey>.*)/$']
In your view you are using a string instead of the variable you created. And you probably need to filter on the field domainKey instead of pk.
Try changing
obj = get_object_or_404(Store, pk='store_domainKey')
to
obj = get_object_or_404(Store, domainKey=store_domainKey)

"Next" and "Previous" button on Django, on posts with Slugs

What I want is to create "Next Post" and "Previous Post" on Django. What I have managed to do until now is to link the button to a page where the primary key is one above or one below the current post:
<a class="btn btn-default" href="{% url 'post_detail' slug=post.slug pk=post.pk|add:'1' %}">Next</a>
There are two issues: one is with the Slug and with the URL, and the other is with the showing (or hiding) of the button.
When I click next, I am taken to the next post (if it exists. If it doens't I receive an error telling me that such a page doesn't exist). The URL remains the same as the previous post, correspondingly: If I click it three times, the URL displayed will be the one of the second post. What I would like is to somehow also display the corresponding slug.
The second problem, is that I want to disable the button if there is no post before or after. But the following piece of code:
{% if post.pk|add:'1' %}
Which is what I have come up with up until now. But apparently it checks if such a number exists, not if the post exists or not.
The view class for this page is the following:
class PostDetailView(HitCountDetailView):
model = Post
slug_field = "title"
count_hit = True
Whereas the model is (removing irrelevant-to-this-issue code):
#python_2_unicode_compatible
class Post(models.Model, HitCountMixin):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=100, null=False, blank=False)
text = models.TextField(null=False, blank=False)
slug = models.SlugField(max_length=100, blank=True)
# rewrite save method
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def article_pre_save(signal, instance, sender, **kwargs):
instance.slug = slugify(instance.title)
signals.pre_save.connect(article_pre_save, sender="blog.Post")
# go to post page
def get_absolute_url(self):
return reverse("post_detail", kwargs={'slug':self.slug, 'pk':self.pk})
# retorna o título do post
def __str__(self):
return self.title
And the related url:
url(_(r'^post/(?P<pk>\d+)-(?P<slug>[-\w]+)/$'), views.PostDetailView.as_view(), name='post_detail'),
How could I proceed to this? I thought about writing a Raw SQL query (I've been having problems with this too, but that's another matter), but I don't know, up to now, how I could actually link it to the template. Thank you for your help, if you will, and attention, in advance.
django-next-prev refer this. its good package for these kinds of requirements. this package will get you the next and previous object of the current object. after that, you have to build the next and previous URL using reverse or reverze_lazy methods in Django

Pass slug and context to render page?

I'm displaying an Entry using DetailView and also have a comment form on the same page.
The commment form works fine for submissions but it doesn't display validation errors.
I can't figure out how to pass both the slug (required for the DetailView) AND the form (which contains the validation errors).
I'm not trying to display the slug in my html template -- I need the slug to retrieve the Entry's detailview.
Url:
...
url(r'^(?P<slug>[\w-]+)/$', EntryDetailView.as_view(), name='entry_detail'),
...
View:
class EntryDetailView(DetailView):
template_name = "entry_detail.html"
def get_context_data(self, **kwargs):
context = super(EntryDetailView, self).get_context_data(**kwargs)
context['comments'] = [(comment, comment.get_children_count()) for comment in EntryComment.get_tree()]
entry_comment_form = EntryCommentForm()
context['entry_comment_form'] = entry_comment_form
return context
def get_object(self, **kwargs):
# If the user created the entry:
try: return Entry.objects.get(Q(slug=self.kwargs['slug']), author=self.request.user.id)
except: pass
# If the user received the entry:
try: return Entry.objects.get(Q(slug=self.kwargs['slug']), recipients=self.request.user)
except: pass
# Otherwise, bye-bye.
raise Http404
def post(self, request, *args, **kwargs):
entry_comment_form = EntryCommentForm(request.POST)
if entry_comment_form.is_valid():
entry_comment_form.add_method(
author=entry_comment_form.cleaned_data['author'],
body=entry_comment_form.cleaned_data['body'],
date_created=datetime.datetime.now()
)
success(request, 'Success')
slug = self.kwargs['slug']
# Proper? It works.
return HttpResponseRedirect(reverse('entry_detail', subdomain='blog', kwargs={'slug':slug}))
else:
error(request, 'Error')
slug = self.kwargs['slug']
# Here's where I need to render the same Entry but include the form context so I can display the validation errors.
return render(
request,
reverse('entry_detail', kwargs={'slug':slug}),
{'entry_comment_form':entry_comment_form}
)
Seems like it's just a rookie mistake somewhere due to my lack of python/django foo.
The end goal is to display the same DetailView page (is there a way to do this without passing the slug to the url?) and include the form in context so I can display comment form validation errors.
Searched around, couldn't find any relevant answers.
What if you refactor it to use one of the customtags that come with the commenting module?
https://docs.djangoproject.com/en/1.5/ref/contrib/comments/#quickly-rendering-the-comment-form
Can you just use the get_comment_form tag with object to avoid passing the form around?
What you are trying makes no sense. You are passing the result of reverse - ie a URL - as the template in a call to the render function. Why are you doing that?
In order to actually help you, though, we'd need to know what you are actually doing. Where is this strange code? Is it in a view function, a class-based view method, a template tag, or somewhere else?
Edit after full code posted
There are several things wrong with that view code (blank excepts that just do pass?), but I will concentrate on the issue at hand. You seem to be making this much harder than it needs to be: if you want to pass both the form and the slug/URL to the template, you just put them in the template context, and pass the context to render as normal. There's nothing out of the ordinary or complicated here at all.
return render(
request,
self.template_name,
{'entry_comment_form':entry_comment_form, 'slug': slug}
)
Note that it's probably best to pass the slug in the context and use the url tag in the template:
{% url 'entry_detail' slug=slug %}

Django: How to provide a category that changes at runtime in the urls.py?

I am trying to create a new URL level in my django-powered ecommerce site, meaning if a user is in domain.com/category/foo/ I am trying to add the next level down, in which they click on some element in foo and wind up in domain.com/category/foo/tag/bar/. To that end, I have created my urls.py line for detecting this, and I believe there is no problem here:
(r'^category/(?P<category_slug>[-\w]+)/tag/(?P<tag_slug>[-\w]+)/$', 'show_tag', {
'template_name':'catalog/product_list.html'},'catalog_tag'),
Once the request has been mapped through the urls.py, I know it is going to need some variables from views.py in order for get_adsolute_url to do its thing, so I create the view:
def show_tag(request,
tag_slug,
template_name="catalog/product_list.html"):
t = get_object_or_404(Tag,
slug=tag_slug)
products = t.product_set.all()
page_title = t.name
meta_keywords = t.meta_keywords
meta_description = t.meta_description
return render_to_response(template_name,
locals(),
context_instance=RequestContext(request))
And lastly, in my Tag model, I set up my get_absolute_url to fill the keyword arguments:
#models.permalink
def get_absolute_url(self):
return ('catalog_tag', (), { 'category_slug': self.slug, 'tag_slug': self.slug })
I made sure the category and tag I'm about to request exist, and then I type domain.com/category/foo/tag/bar and I receive a
TypeError at /category/foo/tag/bar/
show_tag() got an unexpected keyword argument 'category_slug'
I believe I know where the error is, but I don't know how to solve it. my get_abolute_url sets 'category_slug': self.slug but that definition, as I said, lives in my Tag model. My Category model lives in the same models.py, but how do I tell my get_absolute_url to go find it?
Your view show_tag must have a parameter to accept the category_slug which is not there right now:
def show_tag(request,
category_slug, tag_slug,
template_name="catalog/product_list.html")