I'm hoping somebody can help me with this issue. Im having trouble adding my page/ article title to the url path. I've tried a number of ways can't seem to get it. If anyone could help that would be great.
My current Url path is "https://stackoverflow.com/article/1"
Would like it to be "https://stackoverflow.com/article/1/example-question-help", or some variation of that.
Below you can find how my views and url files are set up.
<a href="{% url 'article-detail' post.pk %}"
path('article/<int:pk>/', ArticleDetailView.as_view(), name='article-detail'),'
class ArticleDetailView(DetailView):
model = Post
template_name = 'article_detail.html'
def get_context_data(self, *args, **kwargs):
cat_menu = Category.objects.all()
stuff = get_object_or_404(Post, id=self.kwargs['pk'])
total_likes = stuff.total_likes()
liked = False
if stuff.likes.filter(id=self.request.user.id).exists():
liked = True
context = super(ArticleDetailView, self).get_context_data(*args, **kwargs)
context["cat_menu"] = cat_menu
context["total_likes"] = total_likes
context["liked"] = liked
return context
class Post(models.Model):
title = models.CharField(max_length=250)
header_image = models.ImageField(null=True, blank=True, upload_to="images/")
title_tag = models.CharField(max_length=250, default='none')
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = RichTextField(blank=True, null=True)
slug = models.SlugField(null=True)
# body = models.TextField()
post_date = models.DateField(auto_now_add=True)
category = models.CharField(max_length=250, default='')
snippet = models.CharField(max_length=250)
likes = models.ManyToManyField(User, related_name='blog_posts')
CodePudding user response:
Assuming that your model has a slug field, called slug
, which it looks like it may given your request, you'd change things like this;
path('article/<slug:slug>/', ArticleDetailView.as_view(), name='article-detail'),'
Django will then do the rest because SingleObjectMixin
which is used by DetailView
looks at the URL first for a primary key, then for a slug.
So this will give you URLs that look like;
https://stackoverflow.com/article/example-question-help
CodePudding user response:
You can define a path that includes both the primary key and the slug:
path(
'article/<int:pk>/<slug:slug>/',
ArticleDetailView.as_view(),
name='article-detail',
),
This will automatically filter the item properly. You can boost efficiency by determining the number of likes and whether the object is liked all in the same queryset with an Exists
subquery [Django-doc]:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Count, Exists, OuterRef
class ArticleDetailView(LoginRequiredMixin, DetailView):
model = Post
template_name = 'article_detail.html'
queryset = Post.objects.annotate(total_likes=Count('likes'))
def get_queryset(self, *args, **kwargs):
super().get_queryset(*args, **kwargs).annotate(
is_liked=Exists(
Post.likes.through.objects.filter(
post_id=OuterRef('pk'), user=request.user
)
)
)
def get_context_data(self, *args, **kwargs):
return super().get_context_data(
*args, **kwargs, cat_menu=Category.objects.all()
)
In the modeling, you might want to work with an AutoSlugField
[readthedocs.io] from the django-autoslug
package [readthedocs.io] to automatically slugify. Otherwise you will have to do this yourself. It also makes not much sense that the slug field is NULLable: normally a record will always have a slug. You thus might want to refactor the model to:
from autoslug import AutoSlugField
from django.conf import settings
class Post(models.Model):
title = models.CharField(max_length=250)
header_image = models.ImageField(null=True, blank=True, upload_to='images/')
title_tag = models.CharField(max_length=250, default='none')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
body = RichTextField(blank=True, null=True)
slug = models.AutoSlugField(populate_from='title')
post_date = models.DateField(auto_now_add=True)
category = models.CharField(max_length=250, default='')
snippet = models.CharField(max_length=250)
likes = models.ManyToManyField(
settings.AUTH_USER_MODEL, related_name='liked_posts'
)
Note: It is normally better to make use of the
settings.AUTH_USER_MODEL
[Django-doc] to refer to the user model, than to use theUser
model [Django-doc] directly. For more information you can see the referencing theUser
model section of the documentation.