Django url path matching not working as expected - python

I have problem with Django model get_absolute_url reverse methods.
I have a url pattern that works perfectly but the problem for example
when I visit example.com/blog/python/first-post the path works perfectly, but
when I try a random path like, example.com/blog/python-randomr9353/first-post
it still works correctly even though it shouldn't because, python-randomr9353 is not
a valid path and it should return a page not found error.
Here is my code.
Models
class ArticleSeries(models.Model):
title = models.CharField(max_length=200)
series_slug = AutoSlugField(populate_from='title')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:article_list', kwargs={'series_slug': self.series_slug})
class Tag(models.Model):
title = models.CharField(max_length=50)
def __str__(self):
return self.title
class Article(models.Model):
title = models.CharField(max_length=200)
article_slug = AutoSlugField(populate_from='title')
tag = models.ManyToManyField(Tag, default=1, verbose_name='Tag')
series = models.ForeignKey(ArticleSeries, default=1, verbose_name='Series', on_delete=models.SET_DEFAULT)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:article_detail', args=(self.series.series_slug, self.article_slug))
url patterns
app_name = 'blog'
urlpatterns = [
path('', views.IndexView.as_view(), name='index_view'),
path('blog', views.BlogView.as_view(), name='blog_view'),
path('blog/<slug:series_slug>', views.ArticleListView.as_view(), name='article_list'),
path('blog/<slug:series_slug>/<slug:article_slug>', views.ArticleDetailView.as_view(), name='article_detail'),
]
Views
class IndexView(TemplateView):
template_name = 'blog/index.html'
extra_context = {}
class BlogView(ListView):
model = ArticleSeries
template_name = 'blog/blog_view.html'
context_object_name = 'series_list'
def get_queryset(self):
series = ArticleSeries.objects.all()
return get_list_or_404(series)
class ArticleListView(ListView):
model = Article
template_name = 'blog/article_list.html'
context_object_name = 'article_list'
def get_queryset(self):
slug = self.kwargs['series_slug']
articles = Article.objects.filter(series__series_slug=slug)
return get_list_or_404(articles)
class ArticleDetailView(DetailView):
model = Article
template_name = 'blog/article_detail.html'
context_object_name = 'article'
slug_field = 'article_slug'
slug_url_kwarg = 'article_slug'
def get_object(self, queryset=None):
slug = self.kwargs.get('article_slug')
return get_object_or_404(Article, article_slug=slug)

Try using path('blog/python/<slug:article_slug>', views.ArticleDetailView.as_view(), name='article_detail') in the urls.py instead of path('blog/<slug:series_slug>/<slug:article_slug>', views.ArticleDetailView.as_view(), name='article_detail').
EDIT:
At the point where you obtain the value of series_slug, validate this there itself.
...
validSeries = ['python', 'c', 'cpp', 'js'] # etc
if series_slug not in validSeries:
# Raise a page not found error

Related

How do I change a value of a BooleanField in Django?

The view class:
class PostCreateView(CreateView):
form_class = PostForm
model = Post
The model class:
class Post(models.Model):
title = models.CharField(max_length=256)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
neg_sentiment = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('post_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.title
I'm trying to get BooleanField to change to True, but I don't know how to reassign it
urls.py:
urlpatterns = [
path('publish_post/<int:pk>', views.PublishPost, name='publish_post'),
]
views.py:
def PublishPost(request, pk):
post = Post.objects.get(pk=pk)
post.neg_sentiment.save()
post.save()
context = {'success' : True}
return HttpResponse(context)
Then in your template you can set up a post request to 'publish_post' with the id of the post object.

Generic detail view PostDetailView must be called with either an object pk or a slug in the URLconf

I used get_absolute_url in my model but when I am browsed my post link I got this type error
AttributeError at /blog/details/Hello-World1/
Generic detail view PostDetailView must be called with either an object pk or a slug in the URLconf.
I would like to create link this link /Hello-World1/ (mix with title and pk id)
Models.py
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, models.SET_NULL, null=True, blank=True,)
title = models.CharField(max_length=200)
content = models.TextField()
tags = models.ManyToManyField(Tag)
created_at=models.DateTimeField(auto_now_add=True)
updated_at=models.DateTimeField(auto_now=True)
def get_absolute_url(self):
title = self.title.replace(" ", "-")
return reverse('blog:post_details', args=[title+str(self.id)])
views.py
class PostDetailView(DetailView):
model = Post
template_name = 'blogs/blog_details.html'
urls.py
path('details/<str:new_str>/', PostDetailView.as_view(), name="post_details"),
Your url should have pk arg instead of new_str
path('details/<int:pk>/', PostDetailView.as_view(), name="post_details"),
You need to rewrite get_object
view.py
class PostDetailView(DetailView):
model = Post
template_name = 'blogs/blog_details.html'
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
new_str = self.kwargs.get('new_str') or self.request.GET.get('new_str') or None
queryset = queryset.filter(pk=new_str)
obj = queryset.get()
return obj
see http://www.chenxm.cc/article/1143.html
view.py
class PostDetailView(DetailView):
model = Post
template_name = 'blogs/blog_details.html'
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
new_str = self.kwargs.get('new_str') or self.request.GET.get('new_str') or None
queryset = queryset.filter(pk=new_str)
obj = queryset.get()
return obj
see http://www.chenxm.cc/article/1143.html

How can I make category view to list out all the post with related category in Django using class based views

I want to know how to make category page view using class based view I know how to make this in function based view by using get_object_or_404(category, slug=None) But I am confused how to do this it in class based views. I tried to google this but I am unable to find anything related to this in class view.
I know I could have used function based view but I have used class based view in whole project so I thought to use them here as well
my code
models.py
from django.db import models
from django.utils import timezone
from slugger import AutoSlugField
from django.contrib.auth.models import User
from django.urls import reverse
# Create your models here.
def upload_location(instance, filename):
return "%s/%s" %(instance.slug, filename)
class Category(models.Model):
title = models.CharField(max_length= 60)
slug = AutoSlugField(populate_from='title')
parent = models.ForeignKey('self',blank=True, null=True ,related_name='children',on_delete=models.CASCADE)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
class Meta:
verbose_name_plural = 'categories'
def __unicode__(self):
return self.title
def __str__(self):
return self.title
def get_absolute_url(self, slug=None):
return reverse("posts-detail", kwargs={"slug": self.slug})
class Post(models.Model):
title = models.CharField(max_length=120)
slug = AutoSlugField(populate_from='title')
image = models.ImageField(
upload_to=upload_location,
null=True,
blank=True,
)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='postcategory')
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['-date_posted']
def __str__(self):
return self.title
def get_absolute_url(self, slug=None):
return reverse("posts-detail", kwargs={"slug": self.slug})
urls.py
from django.urls import path
from django.urls import path, include
from .views import PostView, PostDetailView,LatestPostView, CategoryPostListView
urlpatterns = [
path('', PostView.as_view(), name='posts-home'),
path('latest/', LatestPostView.as_view(), name='posts-latest'),
path('<slug>', PostDetailView.as_view(), name='posts-detail'),
path('category/<slug>', CategoryPostListView.as_view(), name='category-detail'),
]
views.py
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.shortcuts import redirect, render,get_object_or_404
#class based view
from django.views.generic import ListView, DetailView
from .models import Post, Category
class PostView(ListView):
template_name = 'posts/home.html'
model = Category
context_object_name = 'all_categs'
def get_queryset(self):
if self.request.user.is_authenticated:
return Category.objects.all()
else:
return Category.objects.all().exclude(title__iexact = 'Featured')[:6]
def get_context_data(self):
if not self.request.user.is_authenticated:
fcategory = Category.objects.get(title__iexact = 'Featured')
context = super(PostView, self).get_context_data()
context['latest_posts'] = Post.objects.exclude(category= fcategory).order_by('-date_posted')[0:6]
context['featured_posts'] = Post.objects.all().filter(category= fcategory).order_by('-date_posted')[0:6]
return context
else:
fcategory = Category.objects.get(title__iexact = 'Featured')
context = super(PostView, self).get_context_data()
context['latest_posts'] = Post.objects.order_by('-date_posted')
context['featured_posts'] = Post.objects.all().filter(category= fcategory).order_by('-date_posted')[0:6]
return context
# def get_success_url(self):
# return reverse('home') #add your path
class LatestPostView(LoginRequiredMixin, ListView):
template_name = 'posts/post_latest.html'
model = Post
context_object_name = 'Posts'
ordering = ['-date_posted']
paginate_by = 6
class PostDetailView(LoginRequiredMixin,DetailView):
model = Post
template_name = 'posts/post_detail.html'
class CategoryPostListView(LoginRequiredMixin, ListView):
model = Category
template_name = 'posts/category_detail.html'
# def get_queryset(self):
# category = get_object_or_404(Category, )
I thought of defining get_queryset inside CategoryPostListView. But I am not sure if it will work or not.
Firstly, if you are using ListView and want to display a list of posts, then you need model = Post.
Next, you can call get_object_or_404 in the get_queryset method. You can access slug from the URL with `self.kwargs['slug'].
Finally, you can filter the queryset to only return posts in that category.
class CategoryPostListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'posts/category_detail.html'
def get_queryset(self):
category = get_object_or_404(Category, slug=self.kwargs['slug'])
return super(CategoryPostListView, self).get_queryset().filter(category=category)
Note that your problem is very similar to the dynamic filtering section in the docs.
Yes. you can use get_object_or_404 in class-based views. just add this to your CategoryPostListView:
class CategoryPostListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'posts/category_detail.html'
def get_queryset(self):
category = get_object_or_404(Category, slug=self.kwargs['slug'])
# do another stuffs here
return Post.objects.filter(category=category)
for more information you can read dynamic filtering in class-based views in django official site

Django: How do you associate an Object_PK in the URL to the Foreign_Key relation field when creating a new object?

I am building an FAQ system. The models extend from Topic -> Section -> Article. When creating a new Article the User will select a Topic then a Section then the create Article button.
The url will look something like //mysite.org/Topic_PK/Section_PK/Article_Create
In Django it should look like this:
url(r'^ironfaq/(?P<pk>\d+)/(?P<pk>\d+)/article$', ArticleCreateView.as_view(), name=’article-create’)
What I am looking to do is to associate the Section_PK to the Article when the user submits the Article. I have the Section_PK in the URL I need help to figure out how to use it to do this.
Alternatively with this set up I can have a form rendered with a choice selection from the Section_FK in the Articles Model. If when creating the Article upon rendering the template if I could limit the Section choices by the Topic in the form.py this will also work for my needs
The url will look something like //mysite.org/Topic_PK/article/create
In Django the url should look like this:
url(r'^ironfaq/(?P<pk>\d+)/article/create$', ArticleCreateView.as_view(), name=’article-create’)
Both these methods require the Passing of the Topic or Section PK to the view or form thru the URL. If there is a better way to do this I am open to other suggestions.
In Django I have the following Models
class Topic(Audit):
name = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
class Meta:
verbose_name_plural = "topics"
def __str__(self):
return self.name
def get_absolute_url(self):
return ('faq-topic-detail',(), {'slug': self.slug})
class Section(Audit):
name = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
topic = models.ForeignKey(Topic,on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "sections"
def __str__(self):
return self.name
def get_absolute_url(self):
return ('faq-section-detail',(), {'topic__slug': self.topic.slug,
'slug': self.slug})
class Article(Audit):
title = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
section = models.ForeignKey(Section,on_delete=models.CASCADE)
answer = models.TextField()
vote_up = models.IntegerField()
vote_down = models.IntegerField()
view_count = models.IntegerField(default=0)
class Meta:
verbose_name_plural = "articles"
def __str__(self):
return self.title
def total_votes(self):
return self.vote_up + self.vote_down
def percent_yes(self):
return (float(self.vote_up) / self.total_votes()) * 100
def get_absolute_url(self):
return ('faq-article-detail',(), {'topic__slug': self.section.topic.slug,
'section__slug': self.section.slug, 'slug': self.slug})
Mysite Forms
class CreateArticleForm(forms.ModelForm):
class Meta:
model = Article
widgets = {
'answer': forms.Textarea(attrs={'data-provide': 'markdown', 'data-iconlibrary': 'fa'}),
}
fields = ('title','section','answer')
Mysite Views
class TopicCreateView(CreateView):
model = Topic
fields = ['name']
template_name = "faq/form_create.html"
success_url = "/ironfaq"
def form_valid(self, form):
topic = form.save(commit=False)
activity_user = self.request.user.username
activity_date = datetime.datetime.now()
topic.save()
return super(TopicCreateView,self).form_valid(form)
class SectionCreateView(CreateView):
model = Section
fields = ['name', 'topic']
template_name = "faq/form_create.html"
def form_valid(self, form):
section = form.save(commit=False)
activity_user = self.request.user.username
activity_date = datetime.datetime.now()
section.save()
self.success_url = "/ironfaq/%s/%s" % (section.topic.slug,section.slug)
return super(SectionCreateView,self).form_valid(form)
class ArticleCreateView(CreateView):
model = Article
form_class = CreateArticleForm
template_name = "faq/form_create.html"
def form_valid(self, form):
article = form.save(commit=False)
activity_user = self.request.user.username
activity_date = datetime.datetime.now()
article.save()
self.success_url = "/ironfaq/%s/%s/%s" % (article.section.topic.slug,article.section.slug,article.slug)
return super(ArticleCreateView,self).form_valid(form)
Let's say that you have this url
url(r'^ironfaq/(?P<topic_pk>\d+)/article/create$', ArticleCreateView.as_view(), name=’article-create’)
Where topic_pk will be pk of topic you want to be associated with your Article.
Then you just need to retrieve it in view. And this is done like this
class ArticleCreateView(CreateView):
model = Article
form_class = CreateArticleForm
template_name = "faq/form_create.html"
def form_valid(self, form):
article = form.save(commit=False)
# what are this variables for?
activity_user = self.request.user.username
activity_date = datetime.datetime.now()
# here we are getting 'topic_pk' from self.kwargs
article.topic_id = self.kwargs['topic_pk']
article.save()
self.success_url = "/ironfaq/%s/%s/%s" % (article.section.topic.slug,article.section.slug,article.slug)
return super(ArticleCreateView,self).form_valid(form)
All url params are stored in self.args and self.kwargs. Our topic_pk is named parameter and thats why we can get it by doing self.kwargs['topic_pk']
But be sure to validate existence of Topic with such pk before assigning it to your Article

Django: Multiple URL parameters

I'm making a study app that involves flashcards. It is divided into subjects. Each subject (biology, physics) has a set of decks (unitone, unittwo). Each deck has a set of cards (terms and definitions). I want my URLs to look like
localhost:8000/biology/unitone/ but I have trouble putting two URL parameters in one URL.
models.py
class Subject(models.Model):
subject_name = models.CharField(max_length=100)
description = models.TextField()
def __str__(self):
return self.subject_name
def get_absolute_url(self):
return reverse('card:index')
class Deck(models.Model):
deck_name = models.CharField(max_length=100)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
def __str__(self):
return self.deck_name
class Card(models.Model):
term = models.CharField(max_length=100)
definition = models.TextField()
deck = models.ForeignKey(Deck, on_delete=models.CASCADE)
def __str__(self):
return self.term
views.py
class IndexView(generic.ListView):
template_name = 'card/index.html'
context_object_name = 'subjects'
def get_queryset(self):
return Subject.objects.all()
class SubjectView(DetailView):
model = Subject
slug_field = "subject"
template_name = 'card/subject.html'
class DeckView(DetailView):
model = Deck
slug_field = "deck"
template_name = 'card/deck.html'
urls.py
# localhost:8000/subjects/1 (biology)
url(r'^subjects/(?P<pk>[0-9]+)/$', views.SubjectView.as_view(), name='subject')
# localhost:8000/subjects/1/1 (biology/unitone)
url(r'^subjects/(?P<pk>[0-9]+)/(?P<pk>[0-9]+)/$', views.DeckView.as_view(), name='deck'),
The second URL in urls.py is what I'm having trouble with. It's not a valid URL.
You can't have multiple parameters with the same name. You have to give each parameter a unique name, e.g.:
url(r'^subjects/(?P<pk>[0-9]+)/(?P<deck>[0-9]+)/$', views.DeckView.as_view(), name='deck'),
In the DeckView you can then access them as self.kwargs['pk'] and self.kwargs['deck'].
there's a way by rewriting the DetailViews
urls.py
url(r'^subjects/(?P<subjects>\w+)/(?P<deck>\w+)/$', views.DeckView.as_view(), name='deck'),
views.py
class DeckView(DetailView):
model = Deck
# slug_field = "deck" # you don't need it
template_name = 'card/deck.html'
def get_object(self, subjects, deck):
subject_obj = Subject.objects.filter(subject_name=subjects).first()
obj = Deck.objects.filter(subject=subject_obj,deck_name=deck).first()
return obj
def get(self, request, subjects, deck):
self.object = self.get_object(subjects, deck)
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
then access localhost:8000/subjects/biology/unitone/

Categories

Resources