I want to create add to favourites feature in ListView but I am struggling with passing product slug in each item.
I have a table with the product name and Action (Add/Remove buttons). I want to allow users to add a specific product to favourites or remove it from there.
models.py
class Product(models.Model):
[...]
favourite = models.ManyToManyField(User, default=None, blank=True, related_name='favourite_product')
slug = models.SlugField(unique=True, blank=True, max_length=254)
views.py
@login_required
def products_in_favourites(request, slug):
product = get_object_or_404(Product, slug=slug)
added_to_favourite = False
if product.favourite.filter(id=request.user.id).exists():
product.favourite.remove(request.user)
added_to_favourite = False
else:
product.favourite.add(request.user)
added_to_favourite = True
context = {
'object': product,
'added_to_favourite': added_to_favourite,
}
if request.is_ajax():
html = render_to_string('favourite.html', context, request=request)
return JsonResponse({'form': html})
class AddToFavourite(LoginRequiredMixin, ListView):
template_name = "AddToFavourite.html"
model = Product
queryset = Product.objects.all()
context_object_name = 'product_list'
def get_context_data(self, **kwargs):
context = super(AddToFavourite, self).get_context_data(**kwargs)
product_item = get_object_or_404(Product, slug=self.kwargs['slug']) # how to pass slug of each product item here?
added_to_favourite = False
if product_item.cart.filter(id=self.request.user.id).exists():
added_to_favourite = True
context['added_to_favourite'] = added_to_favourite
AddToFavourite.html
{% block body %}
<section >
<table >
<thead>
<tr>
<th scope="col">Product</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody id="favourite">
{% for product in product_list %}
<tr>
<td>{{product.title}}</td>
<td>
{% include 'favourite.html' %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</section>
{% endblock body %}
{% block javascript_files %}
<script>
$(document).ready(function(event){
$(document).on('click', '#favourite', function(event){
event.preventDefault();
var slug = $(this).attr('value');
$.ajax({
type: 'POST',
url: '{% url "products_in_favourites" product.slug %}',
data: {
'slug':slug,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
dataType: 'json',
success: function(response){
$('#favourite-section').html(response['form'])
},
error: function(rs, e){
console.log(rs.responseText);
},
});
});
});
</script>
{% endblock javascript_files %}
favourite.html
<form action="{% url 'products_in_favourites' product.slug %}" method="POST" >
{% csrf_token %}
{% if added_to_favourite %}
<button type="submit" id="favourite" name="product_slug", value="{{product.slug}}">Remove</button>
{% else %}
<button type="submit" id="favourite" name="product_slug", value="{{product.slug}}">Add</button>
{% endif %}
</form>
urls.py
urlpatterns = [
path('add-to-favourite/', views.AddToFavourite.as_view(), name='cost_calculator'),
path('add-to-favourite/<slug:slug>', views.products_in_favourites, name='products_in_favourites'),
]
CodePudding user response:
You are sending your slug not in a ListView
you are sending it
<form action="{% url 'products_in_favourites' product.slug %}" method="POST" >
to products_in_favourites
function, and doing it wrong at recieving part,
You are not sending it to a function as slug
, you are sending it as data
with post reqeust
, and so you should recieve it same way:
@login_required
def products_in_favourites(request):
#print all data to see what is coming to this view
print(request.POST)
slug = request.POST['product_slug']
product = get_object_or_404(Product, slug=slug)
Also it is better to pass pk (primary key) instead of slug, primary key has indexes by default in database, so your query will be much much faster if you use pk.