Django 3.0: Getting error for get_absolute_url - django

I am getting this error:
TypeError at /product/177042279214449276022367789942330057699/
product() got an unexpected keyword argument 'id'
I am trying to generate detail page of product (book is product).
urls.py
app_name = 'bookrepo'
urlpatterns = [
path('',views.home,name='home'),
path('product/',views.product,name='product'),
path('product/<id>/', views.product, name='product_detail'),
]
template where I am using get_absoulte_url
<a href="{{ item.get_absolute_url }}" class="btn btn-sm my-btn detail-btn">
<span><i class="fa fa-info-circle"></i></span> View Details
</a>
views.py
def product(request):
return render(request, 'bookrepo/product.html')
models.py
class Book(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField('Title', max_length=255)
authors = models.ManyToManyField(Author, related_name='books_written')
publisher = models.ForeignKey(Publisher, on_delete=models.DO_NOTHING, related_name='books_published')
price = models.DecimalField('Price', decimal_places=2, max_digits=10)
description = models.TextField('Description')
upload_timestamp = models.DateTimeField('Uploading DateTime', auto_now_add=True)
categories = models.ManyToManyField(Category, related_name='book_category')
def get_absolute_url(self):
return "/product/%i/" % self.id
I might be completely wrong with respect to my view and urls. I want to display book details after button in template gets clicked.

Change views.py
def product(request, id=None):
return render(request, 'bookrepo/product.html')

Related

How to get the previous and next related post in django?

views.py
def post_details(request,pk):
post = Post.objects.get(id=pk)
# next_post = Post.objects.filter(id=pk)
context={'post':post,'next':next_post}
return render(request, 'blog/post_detail.html', context)
blog-detail
<div class="s-content__pagenav group">
<div class="prev-nav">
<a href="#" rel="prev">
<span>Previous</span>
Tips on Minimalist Design
</a>
</div>
<div class="next-nav">
<a href="#" rel="next">
<span>Next</span>
Less Is More
</a>
</div>
</div>
models
# this is my model
class User(AbstractUser):
# pass
name = models.CharField(max_length=200)
bio = models.TextField(null=True)
email = models.EmailField(unique=True, null=True)
avatar = models.ImageField( null=True, upload_to='blog_media', default="images/avatar.svg")
facebook = models.URLField(blank=True, null=True)
twitter = models.URLField(blank=True, null=True)
dribbble = models.URLField(blank=True, null=True)
instagram = models.URLField(blank=True, null=True)
class Category(models.Model):
name = models.CharField(max_length=20)
class Meta:
verbose_name = 'Category'
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ManyToManyField(Category)
title = models.CharField(max_length=200, blank=False);
description = models.TextField(null=True,blank=True)
image = models.ImageField(upload_to='blog_media')
url = models.URLField(null=True, blank=True)
body = HTMLField()
created = models.DateTimeField(auto_now=True)
updated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Based on your comments, I'm assuming that you would like to get two related posts that have the same category as the current post.
If I'm correct, then one method you could use is to filter the queryset for the same category belonging to the current post then you could choose the next and previous posts of the current post from the retrieved queryset. For example:
def post_details(request, pk):
current_post = Post.objects.get(pk=pk) # retrieving the current post...
# filtering for related posts only by using the category of the current post
# using -> category_in=post.category.all() since it's a ManyToMany field
related_posts = Post.objects.filter(category_in=current_post.category.all())
# next -> get posts with id greater than the current post id, then get the first instance 'next post'
# previous -> get posts with id less than the current post id, then get the first instance 'previous post'
context = {
'post': current_post,
'next': related_posts.filter(id__gt=current_post.id).order_by('id').first(),
'previous': related_posts.filter(id__lt=current_post.id).order_by('-id').first()
}
return render(request, 'blog/post_detail.html', context)
Ideally, that should work.
A quick recommendation here as well... Instead of using Post.objects.get(pk=pk), I'd suggest using get_object_or_404() as this will handle any potential error that Post.objects.get(pk=pk) will throw. So a small update...
from django.shortcuts import get_object_or_404
def post_details(request, pk):
current_post = get_object_or_404(Post, pk=pk) # retrieving the current post...
# the rest of the code follows...

Trying to delete a comment from a post in django

I am currently trying to delete a comment from my database via a button in django template.
Model looks like this
from django.db import models
from django.contrib.auth.models import User
from cloudinary.models import CloudinaryField
from profiles.models import UserProfile
class Post(models.Model):
user_profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True, related_name='user_posts')
title = models.CharField(max_length=220, unique=True)
location = models.CharField(max_length=220)
rating = models.DecimalField(
max_digits=6, decimal_places=2)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="activity_post")
updated_on = models.DateTimeField(auto_now=True)
description = models.TextField()
featured_image = CloudinaryField('image', blank=False)
created_on = models.DateTimeField(auto_now_add=True)
likes = models.ManyToManyField(User, related_name='activity_likes', blank=True)
like_count = models.BigIntegerField(default='0')
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
def number_of_likes(self):
return self.likes.count()
def liked_by_user(self):
return self.likes.values_list('id', flat=True)
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name="user_comment")
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_on']
def __str__(self):
return f"Comment {self.body} by {self.name}"
Delete function
def delete_comment(request, post_id):
users_comment = get_object_or_404(Comment, post=post_id)
users_comment.delete()
return redirect(reverse('activity'))
URLS
from . import views
from django.urls import path
urlpatterns = [
path('like/', views.like, name='like'),
path("add/", views.add_post, name="add_post"),
path('edit/<int:post_id>/', views.edit_post, name='edit_post'),
path('delete/<int:post_id>/', views.delete_post, name='delete_post'),
path('edit_comment/<int:id>/', views.edit_comment, name='edit_comment'),
path('delete_comment/<int:post_id>/', views.delete_comment, name='delete_comment'),
path("activity/", views.PostList.as_view(), name="activity"),
path('comment/<int:post_id>/', views.Comment.as_view(), name='comment'),
path('searched_posts/', views.search_posts, name='searched_posts'),
path('post/<int:post_id>/', views.post_detail, name='post_detail')
]
here is the comment part that is showing the button.
{%if comments %}
{% for comment in comments %}
{% if comment.user == request.user %}
{{comment.body}} :comment
{{comment.id}} id
<a class="btn tbn-success" href="{% url 'edit_comment' comment.id %}" aria-label="edit button">Edit</a>
<button class="btn btn-warning">Delete</button>
{% endif %}
{% endfor%}
{% endif%}
When I click delete i get an error
Error
Any help would be greatly appreciated, I have tried a ton of different ways from online but nothing seems to work. can anyone point me in the right direction
The first thing I can see is that your delete function uses post = post_id.
Every comment on a particular post will share that post foreign key, so if there is more than one comment on a post, you can't use get_or_404() - it's limited to returning 1 item.
The URL you create for your button is using comment.id so it makes sense to use that instead - this will make it easier to see what's happening.
urls.py
path('delete_comment/<int:comment_id>/', views.delete_comment, name='delete_comment'),
views.py
def delete_comment(request, comment_id):
users_comment = get_object_or_404(Comment, pk=comment_id)
users_comment.delete()
return redirect(reverse('activity'))

Django-Vue get_absolute url error on template

I am trying to route my Vue home page to the detail page of the specific product with its url when the button is clicked on.
Though my django and vue servers run fine. I get this error in chrome.
Chrome error:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'get_absolute_url')
at Proxy.render (HomeView.vue?43d0:14:1)
at renderComponentRoot (runtime-core.esm-bundler.js?d2dd:893:1)
...
The error only shows up when I add the router link tag with the product.get_absolute_url method. When I take it off, the product items (latestProducts) render fine.
HomeView.vue
<template>
<div class="home">
<div class="product-container">
<div
class="product-item"
v-for="product in latestProducts"
:key="product.id"
>
<img class="prod-img" :src="product.get_image" alt="" />
<h2>{{ product.name }}</h2>
<p>{{ product.price }}</p>
</div>
<div>
<router-link v-bind:to="product.get_absolute_url"
><button>View Item</button></router-link
>
</div>
</div>
<router-view />
</div>
</template>
Here is my models.py file where the get_absolute_url model is defined.
class Product(models.Model):
category = models.ForeignKey(Category, related_name='products', on_delete=models.CASCADE)
name = models.CharField(max_length=255)
slug = models.SlugField()
description = models.TextField(blank=True, null=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
image = models.ImageField(upload_to='uploads/', blank=True, null=True)
thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True)
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'/{self.category.slug} / {self.slug }/'
I have read the django docs that suggest using the reverse() function and the viewname in the views.py to route to the url. But i don't think that would work because I am using the APIView of the rest_framework.
Here is my views.py file:
class ProductDetail(APIView):
def get_object(self, category_slug, product_slug):
try:
return Product.objects.filter(category_slug=category_slug).get(slug=product_slug)
except Product.DoesNotExist:
raise Http404
def get(self, request, category_slug, product_slug, format=None):
product = self.get_object(category_slug, product_slug)
serializer = ProductSerializer(product)
return Response(serializer.data)
I would like to know how to clear this error and/or resources to help understand the url and routing concept better.

Django no reverse match for view details

im stuck on show details for object, no matter i do (following all guides on internet )
still getting reverse not match error
views.py
def val_details(request, id):
val = Validator.objects.get(id=id)
print(f'vali: {val}')
context = dict(val=val)
return render(request, 'users/val_details.html', context)
print(f'vali: {val}') printing vali: Validator object (14)
html
<button class="btn btn-warning " href="{% url 'val-details' val.id %}">detals</button>
urls.py
path('dashboard/validator/<int:id>/', user_views.val_details, name='val-details'),
models.py
class Validator(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=200, blank=True, null=True)
address = models.CharField(max_length=200, blank=True, null=True)
owner = models.CharField(max_length=250, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __int__(self):
return self.id
error
django.urls.exceptions.NoReverseMatch: Reverse for 'val-details' with arguments '('',)' not found. 1 pattern(s) tried: ['users/validator/(?P<id>[0-9]+)/$']
profile view
def profile(request):
valid = Validator.objects.filter(user=request.user)
valid_count = valid.count()
context = {
'valid': valid,
'valid_count': valid_count,
}
return render(request, 'users/profile.html', context)
and urls.py
from django.urls import path
from users import views as user_views
urlpatterns = [
path('dashboard/', user_views.profile, name='dashboard'),
path('dashboard/validator/<int:id>/', user_views.val_details, name='val-details'),
]
this is the typical error message if in your
<button class="btn btn-warning " href="{% url 'val-details' val.id %}">detals</button>
val.id is either NULL or empty.
Please check where you assign it.

NoReverseMatch with get_absolute_url

I have a list of links made up of queryset objects. Clicking on each link should take me to that object's detail view. But I'm getting the following error:
NoReverseMatch at /idea_tracker/shoppinglist/
Reverse for 'views.recipient_detail' not found. 'views.recipient_detail' is not a valid view function or pattern name.
My model:
class Recipient(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
birthday = models.CharField(max_length=10, blank=True)
notes = models.TextField(max_length=255, blank=True)
def __str__(self):
return "{} {}".format(self.first_name, self.last_name)
def get_absolute_url(self):
return reverse(
'views.recipient_detail',
args=(),
kwargs={'recipient_id': str(self.id)}
)
class Gift(models.Model):
name = models.CharField(max_length=30, blank=True)
model_number = models.CharField(max_length=30, blank=True)
price = models.DecimalField(default=0.00, decimal_places=2,
max_digits=6)
recipients = models.ManyToManyField(Recipient, blank=True)
purchased = models.BooleanField(default=False)
def __str__(self):
return "{}".format(self.name)
My views:
def shopping_list(request):
recipients =
models.Recipient.objects.prefetch_related('gift_set').\
all().order_by('last_name')
gift_list = models.Gift.objects.all()
total = []
for y in gift_list:
total.append(y.price)
total_price = sum(total)
return render(request, 'idea_tracker/shoppinglist.html', {
'recipients': recipients,
'total_price': total_price
})
def recipient_detail(request, pk):
recipient = get_object_or_404(models.Recipient, pk=pk)
gift = recipient.gift_set
return render(request, 'idea_tracker/recipient_detail.html', {
'recipient': recipient
})
My url:
urlpatterns = [
url(r'^shoppinglist/', views.shopping_list, name='shopping_list'),
url(r'^recipient_detail/(?P<recipient_id>)/$',
views.recipient_detail, name='recipient_detail'),
]
My template:
<h3> Click on a Recipient to edit or delete:</h3>
{% for name in recipients %}
<ul>
<li><a href="{{ name.get_absolute_url }}">{{ name.first_name }}
{{ name.last_name }} /
{% for gift in name.gift_set.all %}
<span style='color:darkblue'>{{ gift }}</span></a></li>
{% endfor %}
</ul>
{% endfor %}
You should use the name of the url pattern recipient_detail:
return reverse(
'recipient_detail',
args=(),
kwargs={'recipient_id': str(self.id)}
)
Your recipient_id group is empty. For example, you could match digits with:
url(r'^recipient_detail/(?P<recipient_id>\d+)/$',
views.recipient_detail, name='recipient_detail'),
Finally, you should change pk to recipient_id in your recipient_detail view to match the URL pattern.