I am trying to create an e-commerce site (CS50 Project 2) through Django and have a Django view with multiple forms. In this view I am trying to use a Boolean Field in a Django form to enable the user to add a listing to his watchlist, both of which are models. While trying to do that, I am receiving the above error.
part of the views.py
def listing(request, id):
#gets listing
listing = Listings.objects.get(id=id)
watchlist_form = WatchListForm()
if request.method == "POST":
watchlist_form = WatchListForm(request.POST)
if watchlist_form.is_valid():
watchlist = watchlist_form.save(commit=False)
watchlist.user = request.user
watchlist.add_to_watchlist = True
new_watchlist_listing = watchlist.objects.listings.add(listing)
return render(request, "auctions/listing.html",{
"auction_listing": listing,
"watchlistForm": watchlist_form
})
else:
return render(request, "auctions/listing.html",{
"auction_listing": listing,
"form": comment_form,
"comments": comment_obj,
"bidForm": bid_form,
"bids": bid_obj,
"watchlistForm": watchlist_form
})
return render(request, "auctions/listing.html",{
"auction_listing": listing,
"form": comment_form,
"comments": comment_obj,
"bidForm": bid_form,
"bids": bid_obj,
"watchlistForm": watchlist_form
})
models.py
class Listings(models.Model):
CATEGORY = [
("Miscellaneous", "Miscellaneous"),
("Movies and Television", "Movies and Television"),
("Sports", "Sports"),
("Arts and Crafts", "Arts and Crafts"),
("Clothing", "Clothing"),
("Books", "Books"),
]
title = models.CharField(max_length=64)
description = models.CharField(max_length=500)
bid = models.DecimalField(max_digits=1000000000000, decimal_places=2)
image = models.URLField(null=True, blank=True)
category = models.CharField(max_length=64, choices=CATEGORY, default=None)
user = models.ForeignKey(User, on_delete=models.CASCADE, default="")
class WatchList(models.Model):
listings = models.ManyToManyField(Listings)
user = models.ForeignKey(User, on_delete=models.CASCADE, default="")
add_to_watchlist = models.BooleanField(default=False)
error message
raise AttributeError("Manager isn't accessible via %s instances" % cls.__name__)
AttributeError: Manager isn't accessible via WatchList instances
This error is happening because of this line: new_watchlist_listing = watchlist.objects.listings.add(listing)
.
CodePudding user response:
You add elements with modelobject.manytomanyfield.add()
, so without the part. Your view should thus look like:.objects
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect
@login_required
def listing(request, id):
listing = get_object_or_404(Listings.objects, pk=id)
watchlist_form = WatchListForm()
if request.method == "POST":
watchlist_form = WatchListForm(request.POST)
if watchlist_form.is_valid():
watchlist_form.instance.user = request.user
watchlist_form.instance.add_to_watchlist = True
watchlist = watchlist_form.save()
watchlist.listings.add(listing)
return redirect('name-of-the-view')
return render(request, 'auctions/listing.html',{
'auction_listing': listing,
'form': comment_form,
'comments': comment_obj,
'bidForm': bid_form,
'bids': bid_obj,
'watchlistForm': watchlist_form
})
Note: It is often better to use
get_object_or_404(…)
[Django-doc], then to use.get(…)
[Django-doc] directly. In case the object does not exists, for example because the user altered the URL themselves, theget_object_or_404(…)
will result in returning a HTTP 404 Not Found response, whereas using.get(…)
will result in a HTTP 500 Server Error.
Note: In case of a successful POST request, you should make a
redirect
[Django-doc] to implement the Post/Redirect/Get pattern [wiki]. This avoids that you make the same POST request when the user refreshes the browser.
Note: You can limit views to a view to authenticated users with the
@login_required
decorator [Django-doc].
Note: Usually it is better not to work with
commit=False
when saving the form with the.save(…)
method [Django-doc]: this will revent saving many-to-many relations since these can only be saved if the item is committed. Usually it is better to alter the.instance
wrapped in the form. For more information, see my article[Django-antippaterns] on this.