Django-Vue get_absolute url error on template - django

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.

Related

when I try to render tags I get Wallpaper.Wallpaper.None

views.py
def download(request, wallpaper_name):
try:
wallpaper = Wallpaper.objects.get(name=wallpaper_name)
similar_wallpapers = wallpaper.tags.similar_objects()[:2]
except Exception as exc:
wallpaper = None
similar_wallpapers = None
messages.error = (request, 'Sorry! data does not exist')
context = {'wallpaper': wallpaper, 'similar_wallpapers': similar_wallpapers}
return render(request, 'Wallpaper/download.html', context)
models.py
class Tags(models.Model):
tag = models.CharField(max_length=100)
def __str__(self):
return self.tag
class Wallpaper(models.Model):
name = models.CharField(max_length=100, null=True)
size = models.CharField(max_length=50, null=True)
pub_date = models.DateField('date published', null=True)
resolution = models.CharField(max_length=100, null=True)
category = models.ManyToManyField(Category)
tags = TaggableManager()
Device_Choices = [
('PC', 'pc'),
('mobile', 'mobile')
]
Devices = models.CharField(max_length=20,choices=Device_Choices, default= 'PC')
image = models.ImageField(upload_to='Wallpaper/Images/', default="")
def __str__(self):
return self.name
download.html
<div class="tag">
<h3>Tags</h3>
<ul>
<li>{{wallpaper.tags}}</li>
</ul>
</div>
I want all the tags of that particular wallpaper to be rendered
and if possible please tell me if there is any other way to handle tags, because using taggit its very difficult i am getting manny errors
Tags are a many-to-many relation so you need to use .all in your template to get them. However, this will just show you the queryset, so you need to loop through them to render their names:
<ul>
{% for tag in wallpaper.tags.all %}
<li>{{ tag.name }}</li>
{% endfor %}
</ul>

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 3.0: Getting error for get_absolute_url

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

Django - How to leave ImageField blank if user doesn't upload image

I'm trying to build a recipe app that allows users to upload images and save recipes in a list.
The problem I'm facing is when the user doesn't upload an image, I get error: attribute has no file associated with it.
Error
I've looked in Django documentation & tried using default tag in my HTML template with no success.
The value is named image_ingredients in models.py
How could I make it so the user can just leave the ImageField empty?
Here is my code:
models.py
# Recipe Field
class Recipe(models.Model):
title = models.CharField(max_length=200)
# TODO: Add default image if image is left blank
image = models.ImageField(upload_to='recipes/images/', blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE,)
daily_meals = ['Breakfast', 'Brunch', 'Elevenses', 'Lunch', 'Tea', 'Supper', 'Dinner']
meal = models.ForeignKey(Meal, limit_choices_to={'name__in': daily_meals}, on_delete=models.CASCADE,)
image_ingredients = models.ImageField(upload_to='recipes/images/', null=True, blank=True)
ingredients = models.TextField(blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
views.py
# Solo recipe with instructions
def solo(request, recipe_id):
recipe = get_object_or_404(Recipe, pk=recipe_id)
return render(request, 'recipes/solo.html', {'recipe':recipe})
solo.html
<h4>Ingredients</h4>
<img src="{{ recipe.image_ingredients.url }}">
<p>{{ recipe.ingredients }}</p>
You can render the image only when there is an image_ingredients item present, for example:
<h4>Ingredients</h4>
{% if recipe.image_ingredients %}<img src="{{ recipe.image_ingredients.url }}">{% endif %}
<p>{{ recipe.ingredients }}</p>

Detailview Object Relations

;TLDR - After some solutions discovered, my final question is how can I, if at all, access models related to models related to the main detailview model?
I'm trying to use a generic detailview to return an object and it's related object. In this example, a company like mcdonalds would have any sites (or locations). What I want the detailview to be able to show is the company detail, and the site detail related to the company. I'm stuck though. Dispite my efforts in not asking for help, I have not been able to pull the data from the model referencing the company sites. Where am I going wrong? I have sort of proven this to work in the django shell with SiteModel.objects.filter(company=5) showing all of the company with an ID of 5's site names.
models.py
'''
The company model consists of the base company information
'''
class CompanyModel(models.Model):
name = models.CharField(_('Company Name'), max_length=255, blank=False)
website = models.URLField(_('Company Website'), blank=True)
since = models.DateField(auto_now_add=True)
rate = models.DecimalField(max_digits=5, decimal_places=2, blank=False)
def __str__(self):
return '%s' % (self.name)
class Meta:
ordering = ['name']
verbose_name = 'Company'
verbose_name_plural = 'Companies'
'''
The site model consists of sites of a company as
some companies have several sites that we will work from.
'''
class SiteModel(models.Model):
company = models.ForeignKey(CompanyModel, on_delete=models.PROTECT)
address = models.ForeignKey(AddressModel, on_delete=models.PROTECT)
phone = models.ForeignKey(PhoneModel, blank=True, null=True, on_delete=models.PROTECT)
distance = models.SmallIntegerField(blank=True)
def __str__(self):
return '%s - %s, %s' % (self.company, self.address.city, self.address.state)
class Meta:
ordering = ['company']
verbose_name = 'Company Site Information'
verbose_name_plural = 'Company Sites'
views.py
class CompanyDetailView(DetailView):
model = CompanyModel
template_name = 'customers/detail.html'
def get_context_data(self, **kwargs):
context = super(CompanyDetailView, self).get_context_data(**kwargs)
context['sites'] = SiteModel.objects.filter(id=self.kwargs['pk'])
return context
urls.py
url(r'^customer/(?P<pk>[0-9a-z-]+)/detail/$', CompanyDetailView.as_view(),
name='customer-detail'),
Update 1:
My template is showing the correct company, but only 1 site, and the site is not related to the company. Arg. It's showing both the company who's ID is 5, and the site who's ID is 5. How do I connect the dots correctly here?
template
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Customer Detail</title>
</head>
<body>
<div class="container">
{{ object.name }}
{% for site in sites %}
{{ site }}
{% endfor %}
</div>
</body>
</html>
Update 2:
I was able to sort this out by not supering get_context_data, and just itterating through the _set suffix of the related model's name. Django Documentation Reference
template
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Customer Detail</title>
</head>
<body>
<div class="container">
{{ company.name }}
{% for site in company.sites.all %}
{{ site }}
{% endfor %}
</div>
</body>
</html>
The follow up to this, however, is how do I go more than one layer deep? Following up with the above models, I also have a "reports" model. But when I use the same method as above, it seems to break down after the first model. i.e. I can't just use company.sites.reports.
models.py
class ServiceReportModel(models.Model):
report_number = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
site = models.ForeignKey(customers_models.SiteModel, on_delete=models.PROTECT, related_name='reports')
request_number = models.ForeignKey(ServiceRequestModel,
on_delete=models.PROTECT,
null=True,
blank=True,
related_name='s_report_number'
)
reported_by = models.ForeignKey(main_models.MyUser, related_name='reports')
reported_date = models.DateTimeField(auto_now_add=True)
updated_by = models.ForeignKey(main_models.MyUser, blank=True, null=True, related_name='+')
updated_date = models.DateTimeField(auto_now=True)
equipment = models.ForeignKey(customers_models.EquipmentModel, on_delete=models.PROTECT)
report_reason = models.CharField(max_length=255, null=True)
time_in = models.DateTimeField(blank=True, null=True)
time_out = models.DateTimeField(blank=True, null=True)
actions_taken = models.TextField(null=False, blank=False)
recommendations = models.TextField(null=True, blank=True)
def get_absolute_url(self):
return reverse('service-report', kwargs={'pk': self.pk})
def __str__(self):
return '%s - %s, %s' % (self.site.company, self.reported_date.strftime('%d %B %Y'), self.equipment.name)
class Meta:
ordering = ['reported_date']
verbose_name = 'Service Report'
verbose_name_plural = 'Service Reports'
I was able to get a solution with some help. I went back to super'ing the get_context_data method, and following this documentation regarding spanning relationships using filters and double underscore notation.
class CompanyDetailView(DetailView):
model = CompanyModel
context_object_name = 'company'
template_name = 'customers/detail.html'
def get_context_data(self, **kwargs):
context = super(CompanyDetailView, self).get_context_data(**kwargs)
context['sites'] = SiteModel.objects.filter(company=self.get_object())
context['reports'] = ServiceReportModel.objects.filter(site__company=self.get_object())
return context