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

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

Related

Slug for url in 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)

No user matches given query even though the user exists

I'm fairly new to Django and am working on making user profile pages accessible by using the user's username in the url, e.g. mysite.com/profile/someusername
I'll be having links to the profile in a couple places, but the first place I'm experimenting on is in my navbar to access the logged-in user's page.
base.html
<a class="dropdown-item" href="{% url 'fillups:user_profile' username=user.username %}" class="btn btn-simple">Overview</a>
This correctly displays the currently logged-in user's name, for the case of this example we'll user the username seconduser
This is the url pattern I'm using for this:
path('profile/<str:username>/',views.UserProfile.as_view(),name='user_profile')
So far, the navbar will display the username, seconduser, and when I click the button I'm brought to the url /profile/seconduser/, which is what I want.
The problem is, I'm not able to now use the username in my view to query the objects for the given user. Here is what I have for this view so far
views.py
class UserProfile(TemplateView):
template_name = 'fillups/user_profile.html'
slug_field = "username"
slug_url_kwarg = "username"
def get_context_data(self, **kwargs):
context = super(UserProfile, self).get_context_data(**kwargs)
usr = get_object_or_404(User, username=self.kwargs.get("username"))
overview_stats = {
'total_cars': Car.objects.filter(username=usr).count(),
'total_fillups': Fillup.objects.filter(username=self.request.user).count(),
'total_distance': Fillup.objects.filter(username=self.request.user).aggregate(Sum('trip_distance')),
'total_gallons': Fillup.objects.filter(username=self.request.user).aggregate(total_gallons = Round(Sum('gallons'),4)),
'avg_price': Fillup.objects.filter(username=self.request.user).aggregate(avg_price = Round(Avg('price_per_gallon'),3)),
'total_spent': sum_total_sale(Fillup.objects.filter(username=self.request.user)),
'avg_mpg': avg_mpg(Fillup.objects.filter(username=self.request.user))
}
context['stats'] = overview_stats
context['active_cars'] = Car.objects.filter(status='Active').filter(username=self.request.user)
context['last_10_fillups'] = Fillup.objects.filter(username=self.request.user).order_by('-date')[:10]
return context
For now, everything in the overview_stats dict is what I originally had when I was just querying stuff for the logged-in user, where there was just a simple "myprofile" url. The problem I'm having her is that the get_object_or_404 isn't finding the user. I know that username=self.kwargs.get("username") is getting 'seconduser' like it should be, but for some reason I just can't get the user.
For some extra info, here is one of my models:
class Car(models.Model):
username = models.ForeignKey(User,on_delete=models.CASCADE)
name = models.CharField(max_length=25)
make = models.CharField(max_length=25)
model = models.CharField(max_length=25)
model_year = models.IntegerField(choices=MODEL_YEARS)
status = models.CharField(max_length=10,choices=STATUS,default='Active')
def __str__(self):
return self.name
And in the initial Django tutorial I did, the instructor said it is best to extend the user model so it's easier to make changes, so I have this in a separate app, accounts/models.py
class User(auth.models.User,auth.models.PermissionsMixin):
def __str__(self):
return "#{}".format(self.username)
I've tried using the method in this question which is why I have the slug field stuff in my view currently, and while my question is essentially a duplicate of this question
I've been stuck on this all night and would really appreciate any help, thanks!
Remove the the self from self.kwargs.get("username"). It should be kwargs.get("username").
kwargs is an argument not on object property.

change my url from depending on id to title | django

I've a url for checking books by its id:
path('book/<int:book_id>', views.book, name='book'),
view:
def book(request, book_id):
book = get_object_or_404(Book, pk=book_id)
context = {
'book': book
}
return render(request, 'media/book.html', context)
but my client asked me to change it for the title instead but I tried it and it didn't seem to work, there are no examples for it in the docs either.
The NOTE in the answer above does not take SEO into account. For SEO purposes, it is much better to have a url that includes the name of the book rather than just the ID. If you are already in production, then remember to do a permanent redirect in your view from all ID-based urls to the slug-based url. Your Book model definition (or Product or whatever you've called it) should include a slugified field:
class Book(models.Model):
name = models.CharField(...)
slug = models.CharField(max_length=255, default='', unique=True, blank=True)
other_fields...
def save(self, *args, **kwargs):
# Create slug for SEO
#Don't ever change once established since is part of the URI
if self.slug is None or self.slug == '':
self.slug = slugify(self.name)
super(Book, self).save(*args, **kwargs)
You need to change the url to 'book/<str:book_title>', and also adjust your function accordingly:
def book(request, book_title):
book = get_object_or_404(Book, yourfieldfortitle=book_title)
...
It might be helpful to try accessing the url first with a pattern that you know must work. NOTE: this makes the strong assumption that a book is identified by its title, otherwise using the primary key is the proper way even if it doesn't "look nice" as a url.

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")

Outputting data from a django app

This is probably something really simple but for some reason I just can't seem to do it.
I'm trying to output some data from a blog (app) that I have created. The blog is working fine and out putting the content in the model fields that I have created and outputting to the templates I have specified.
But when tryingn to output the information out to the homepage nothing is showing. I'm fairly new to django and I think I might be missing something.
Do I need to include something to pages that are outside of the app? or do I need to set up somethin in the urls file?
I hope this makes sense as I don't think it's anything to complicated but I just think I'm missing something!
Thanks.
CODE:
url(r'blog/(?P<slug>[-\w]+)/$', 'blog.views.blog', name="blog"),
url(r'blog/', 'blog.views.blog_index', name="blog_index"),
def blog_index(request):
blogs = Blog.objects.filter(active=True)
return render_to_response('blog/index.html', {
'blogs':blogs,
}, context_instance=RequestContext(request))
def blog(request, slug):
blog = get_object_or_404(Blog, active=True, slug=slug)
return render_to_response('blog/blog_post.html', {
'blog': blog
}, context_instance=RequestContext(request))
class Blog(TimeStampedActivate):
title = models.CharField(max_length=255, help_text="Can be anything up to 255 character")
slug = models.SlugField()
description = models.TextField(blank=True, help_text="Give a short description of the news post")
content = models.TextField(blank=True, help_text="This is the main content for the news post")
user = models.ForeignKey(User, related_name="blog")
def __unicode__(self):
return self.title
#models.permalink
def get_absolute_url(self):
return ('blog', (), {
'slug': self.slug
})
Are you saying that going to mysite.com/blog/ is displaying correctly, but you also want it to be the sites index page at mysite.com?
If so, you need to add a new pattern to your projects urls.py file, like so:
url(r'^$', 'blog.views.blog_index')
Doing this will make mysite.com/blog/ and mysite.com route to the same view.