Watchlist showing in admin interface but not showing on the site django

I am working on a project and the features is that the user should be able to add the item to their “Watchlist.” If the item is already on the watchlist, the user should be able to remove it and users who are signed in should be able to visit a Watchlist page, which should display all of the listings that a user has added to their watchlist.

When I click on the ‘add to watchlist’ button, it successfully registers on the admin panel ( screenshot included) but on my webpage, it shows me an empty image (it’s supposed to show the listings image) and when I click on that image, it shows me an error message ‘No Auction matches the given query.’ and I noticed it gives me a different url id (screenshot included and different url from the actual url id of the details page and actual url id of the details page which i’m supposed to be linked to(which is 1)).

Also, when I delete one of the products/listing (still by clicking on the add to watchlist button - i would later improve this), it removes everything in the watchlist (even if there were other items in the watchlist screenshot included).

MODELS.PY

class Watchlist(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    item = models.ManyToManyField(Auction)

    def __str__(self):
        return f"{self.user}'s watchlist"

VIEWS.PY

def add_watchlist(request, listing_id):
    items = Auction.objects.get(pk=listing_id)
    watched = Watchlist.objects.filter(user=request.user, item=listing_id)

    if watched.exists():
        watched.delete()
        messages.info(request, 'Successfully deleted from your watchlist')
        return render(request, 'auctions/watchlist.html', {'all_watchlist': Watchlist.objects.filter(user=request.user)}) 
            
    else:
        watched, created = Watchlist.objects.get_or_create(user=request.user)
        watched.item.add(items)
        messages.success(request, 'Successfully added to your watchlist')
        return redirect('index')

@login_required
def watchlist(request):
    watchlists = Watchlist.objects.all()
    context = {'watchlists':watchlists}
    return render(request, 'auctions/watchlist.html', context)  

DETAILS.HTML

                        {% if request.user.is_authenticated %}
                            <div class="my-2">
                                <a href="{% url 'add_watchlist' detail.id %}" role="button" class="btn btn-primary btn-block">Add to Watchlist</a> 
                            </div>
                        {% endif %}

WATCHLIST.HTML

            <div class="container pb-4">
                <div class="row text-center">
                    {% for watchlist in watchlists %}
                        <div class="col-lg-3 col-sm-4">
                            <a href={% url 'listing_detail' watchlist.id %} class="btn btn-outline-dark btn-sm m-1">
                                    {% if watchlist.image_url %}
                                        <img src='{{ watchlist.image_url }}' alt="{{ watchlist.title }}" style="width:100%">
                                    {% else %}
                                        <img src="https://demofree.sirv.com/nope-not-here.jpg">
                                    {% endif %}  
                                        <h5 class="card-title mb-0">{{ watchlist.title }}</h5>
                            </a>
                        </div>
                    {% empty %}
                        <p>No watchlist found.</p>
                    {% endfor %}
                </div>
            </div>  
1 Like

It looks like you’re addressing a number of different issues here.

In an attempt to keep me from getting confused among them, I’m going to try and separate them into individual issues.

What I think I understand what you’re talking about first is:

Which I believe is supposed to be handled by this line in your template:

In your template, you’re iterating over the context element named watchlists, which is being created here:

You’re trying to retrieve an attribute named image_url from the Watchlist object, but your watchlist Model as you’ve posted it:

Doesn’t have an attribute named image_url. Therefore, there’s no image to show.

This appears to be the result of this line:

You don’t show your URLs here, or the view that is mapped to the name listing_detail. But from the error message you describe, I’m going to guess that that URL is supposed to give the details of an Auction object. However, the parameter you’re passing to that URL is the PK of a Watchlist object and not the PK of an Auction object.

Correct. You’re deleting the Watchlist object here, not the relationship between a Watchlist and a specific Auction.

You may want to take some time to review the ManyToMany relationship docs, especially the examples, to ensure you’re understanding the APIs that Django uses when working with a many-to-many relationship, and to understand how that type of relationship works within the Django ORM.

1 Like

Thank you, I realized that I needed to reference the foreign key to the watchlist so did this

{% if watched.item.image_url %}
<img src='{{ watched.item.image_url }}' alt="{{ watched.item.title }}" style="width:100%">

I was able to revamp this and I used a foreign key instead, these are the changes made so far

class Watchlist(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    item = models.ForeignKey(Auction, on_delete=models.CASCADE)

VIEWS.PY

@login_required
def add_watchlist(request, listing_id):
    if request.method == 'POST':
        items = Auction.objects.get(pk=listing_id)
        watched = Watchlist.objects.filter(user=request.user, item=items)

        if watched.exists():
            watched.delete()
            messages.info(request, 'Successfully deleted from your watchlist')
            return redirect('watchlist')            
        else:
            watched, created = Watchlist.objects.get_or_create(user=request.user, item=items)
            watched.save()
            messages.success(request, 'Successfully added to your watchlist')
            return redirect('index')
    messages.success(request, 'Problem')
    return redirect('index')

@login_required
def watchlist(request):
    watchlist = Watchlist.objects.filter(user=request.user)
    return render(request, 'auctions/watchlist.html', {'watchlist': watchlist}) 

DETAILS.HTML

                        {% if request.user.is_authenticated %}
                            <div class="my-2">
                                <form action="{% url 'add_watchlist' detail.id %}" method = "POST">
                                    {% csrf_token %}
                                    <button type="submit" class="btn btn-primary btn-block">Watchlist</button>
                                </form>
                            </div>
                        {% endif %}

WATCHLIST.HTML

            <h2>Watchlist</h2>
            <div class="container pb-4">
                <div class="row text-center">
                    {% for watched in watchlist %}
                        <div class="col-lg-3 col-sm-4">
                            <a href={% url 'listing_detail' watched.id %}>
                                {% if watched.item.image_url %}
                                    <img src='{{ watched.item.image_url }}' alt="{{ watched.item.title }}" style="width:100%">
                                {% else %}
                                    <img src="https://demofree.sirv.com/nope-not-here.jpg">
                                {% endif %}  
                                    <h5 class="card-title mb-0">{{ watched.item.title }}</h5>
                            </a>
                        </div>
                    {% empty %}
                        <p>No watchlist found.</p>
                    {% endfor %}

URLS.PY

    path("add_watchlist/<int:listing_id>/", views.add_watchlist, name="add_watchlist"),
    path("watchlist", views.watchlist, name="watchlist"),

This is almost working perfectly right now :grinning:, the only problem I’m currently having is that when it adds successfully to the watchlist, for e.g the url for the added watchlist is 'http://127.0.0.1:8000/detail/1/, if I delete that watchlist and try to add it again, instead of the url still being the same and showing me the details of the url.

It changes the url path to for e.g http://127.0.0.1:8000/detail/3/ and gives me this error.

Page not found (404)

No Auction matches the given query.

Request Method: GET
Request URL: http://127.0.0.1:8000/detail/8/
Raised by: auctions.views.listing_detail

Using the URLconf defined in commerce.urls , Django tried these URL patterns, in this order:

  1. admin/
  2. [name=‘index’]
  3. login [name=‘login’]
  4. logout [name=‘logout’]
  5. register [name=‘register’]
  6. create/ [name=‘create_listing’]
  7. detail/int:listing_id/ [name=‘listing_detail’]

The current path, detail/8/ , matched the last one.

Unfortunately, I didn’t find how to resolve this online.

Could you please point me to the right direction why I am having this issue

Correct.

The primary key of the Watchlist object (the automatically-supplied id field) is always going to be unique and not reused.

If you delete Watchlist id=1, and add another Watchlist, you will get a new id.

But, getting to the issue at hand:

What object type is listing_detail supposed to display?

What object type is watched?

1 Like

Please clarify what you mean by object type?

These should display the details of the listing/product which includes the image, title.

These are the models

class Auction(models.Model):
    title = models.CharField(max_length=25)
    description = models.TextField()
    current_bid = models.IntegerField(null=False, blank=False)
    image_url = models.URLField(verbose_name="URL", max_length=255, unique=True, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    category = models.ForeignKey(Category, max_length=12, null=True, blank=True, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    class Meta:
        ordering = ['-created_at']

class Watchlist(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    item = models.ForeignKey(Auction, on_delete=models.CASCADE)

Learnt something new. Thanks

“Object type” → Model (generally speaking)

1 Like
class Auction(models.Model):
    title = models.CharField(max_length=25)
    description = models.TextField()
    current_bid = models.IntegerField(null=False, blank=False)
    image_url = models.URLField(verbose_name="URL", max_length=255, unique=True, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    category = models.ForeignKey(Category, max_length=12, null=True, blank=True, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    class Meta:
        ordering = ['-created_at']

class Watchlist(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    item = models.ForeignKey(Auction, on_delete=models.CASCADE)

So, for this line:

<a href={% url 'listing_detail' watched.id %}>

What object type is listing_detail supposed to display?

What object type is watched ?

(I don’t need to see the models themselves, just for you to identify which models answer these questions.)

1 Like

The Auction model

Is this what you meant ?

<a href={% url 'listing_detail' watched.item.id %}>

Yes, I do believe you’ve got it now.

1 Like

Thank you but it’s still giving me the issue of unique urls, so when I delete a watchlist and try to add it again, I’m still not able to click on it and see the details.

Do I need to delete the previous migration or db.sqlite to effect the changes?

EDIT: Just had to delete and add it again to the watchlist.

Thank you so much

1 Like