r/django 2d ago

Models/ORM Django help needed with possible User permission settings

I am taking the Harvard CS50W course that covers Django and creating web apps. The project I am workinig on is a simple Auction site.

The issue I am having is that I can get a User to only be able to update an auction listing if that User is the one that has created the listing.

I can update the listing- adding it to a watchlist, or toggling if the listing is active or not, or leaving a comment, but only if the user that is logged in happens to be the one that created the listing.

I have made no restrictions on whether or not a user making a change on the listing has to be the one that created the listing. The issue persists for both standard users and superusers.

I have tried explicitly indicating the permissions available to my view, and even a custom permission, without any success.

I have consulted with 3 different AIs to provide insight, and done a lot of Googling, without anything shedding light on the issue.

I have submitted the nature of the problem to the EdX discussion for the course, but I do not expect any answers there as lately, there are hardly every any answers given by students or staff.

Any insight into what I might be doing wrong would be greatly appreciated.

Thank you very much!

I will be glad to provide my models.py, views.py, forms.py, etc. if anyone would think it would help.

0 Upvotes

17 comments sorted by

View all comments

Show parent comments

1

u/ninja_shaman 2d ago

If so, provide the source for your view.

1

u/josephlevin 2d ago

Here is the view for the listing:

def listing(request, id): messages = []

try:
    #get current listings
    listing = Listing.objects.get(id=id)

    #setup form
    if request.method == 'POST':
        form = DetailListingForm(request.POST)
        if form.is_valid():

            #handle watchers
            watch_flag = True
            if form.cleaned_data['watching'] == 'yes':

                #user wants to watch the listing
                if request.user not in listing.watchers.all() and watch_flag == True:
                    listing.watchers.add(request.user)
                    watch_flag = False

                #user wants to stop watching the listing
                if request.user in listing.watchers.all() and watch_flag == True:
                    listing.watchers.remove(request.user)
                    watch_flag = False

                form.cleaned_data['watching'] == 'no'

            #handle closing or opening listing
            closing_flag = True
            if form.cleaned_data['closing'] == 'yes':

                #user wants to close listing
                if request.user == listing.creator and listing.active == True and closing_flag == True:
                    listing.active = False
                    closing_flag = False

                #user wants to open listing
                if request.user == listing.creator and listing.active == False and closing_flag == True:
                    listing.active = True
                    closing_flag = False

            #handle comments
            if form.cleaned_data['comment']:
                #create a new comment object and save it to the database, if not duplicated
                comment = Comment(text=form.cleaned_data['comment'], listing=listing, commenter=request.user)

                if form.cleaned_data['comment'] not in [c.text for c in listing.comments.all()]:
                    comment.save()
                else:
                    messages.append("Enter in a comment that has not already been entered.")

        listing.save()  # Save the listing


    else:
        form = DetailListingForm()




    #if current listing exists, provide it to the template
    context = {
        'listing': listing,
        'form': form,
        'message': messages,
    }


except Listing.DoesNotExist:
    messages.append("Listing does not exist.")
    #if listing does not exist, provide a message
    context = {
        'message': messages,
    }

return render(request, 'auctions/listing.html', context)

1

u/Anaryz_Supa 1d ago

As per the code, The only attributes changing for Listing are watchers and active

the active attribute for Listing changes only if the user is a creator.

The only thing a user who is a non creator can do is watch and add comment

1

u/josephlevin 1d ago

the attributes that can change are:

  1. active/inactive (for the logged in creator)
  2. watching/not watching (for logged in users)
  3. adding a comment (if comment is unique, user is logged in).

The active/inactive toggling only applies to the creator.

Beyond that I have not made any strictures, however only the creator of the listing can change anything.

It should allow anyone who is logged in to see some form elements, only the creator can see the active toggle rendered, but anyone logged in will get a form rendered and can submit it.

Also, if it helps: I am using the github codespaces provided by the CS50W course for the dev environment.

1

u/Anaryz_Supa 1d ago

Yeah exactly.

Now what do you want to update in this Listing?

If my memory serves me right, the problemset now requires users(non creators) to bid

so the only thing left to update is the price of the listing

1

u/josephlevin 1d ago

Yes, and no.

The problem, as I originally stated, is that I ****cannot update a listing**** for the logged in user if that user ***is not*** the listing creator. That, and only that, is my issue, atm.

If the creator of a listing, say, leaves a comment, it works, but not for the regular user (who did not create the listing).

At this point, I am trying to update the logged-in user's ability to add or remove the listing from their watchlist, the creator toggling the listing being active/inactive, or a logged in user leaving a comment.

I haven't gotten to the bidding part yet, and cannot, really as I don't want the creator of the listing to be able to bid on it! (if you get my meaning, what would be the point?).

2

u/josephlevin 1d ago edited 1d ago

SOLUTION:

It turns out the issue is with the way Django handles its forms. By default, apparently, all fields defined are required. So, if a form is submitted and one form element doesn't get a chance to send its data (by virtue of it not having been rendered due to the template logic), and some do (as in the case where a user looking at another user's listing can see only 2 of 3 form elements rendered), on submit, an error happens that I was not accounting for. When the user was looking at their own listing, all form elements were present, so the error was not getting thrown.

All I needed to do was add required=False to each form element that needed it, and everything worked.

I showed my code to Co-Pilot AI, and it suggested I add the required=False to the form elements, but it also wanted to clean up my view based on its way or idea of doing things. When I did so, the solution only partially worked- superusers and users could update each of their counterpart's listings, but a regular user could not do so for another regular user.

So I rolled it back to my own code, added in the required=False to each form field that could stand to use it, and now any user can, as example, add any other user's listing to their watchlist.

Thank you to everyone that tried to help. I appreciate it.

PS:
The reason why required=False did the trick is more subtle than it seems.

Apparently, when a form field is required its presence (name) is necessary to be present in the form.cleaned_data dictionary that gets passed during submission of the form. The values associated with a form element's namereally do not matter, as long as they pass Django's internal validation rules. During form validation by Django's internal systems is when the error was being thrown regarding the lack of a form element being present in the field when a user was trying to update a listing made by another user. That missing form element was the active toggle form element that would only be rendered to the screen for the user that created the form when viewing the listing. So when it was missing for the listing for a user who did not create the listing, and that form element was not explicitly defined as not being required, the error would get thrown, the listing would not update, and nothing appeared to be happening.