0

I'm trying to show the server to the user only if the user is a moderator or the creator of the server.

so I wrote this code:

class ServerModeratingView(LoginRequiredMixin, View):
    def get(self, request, server_tag):
        moderator = ServerModerator.objects.get(user=request.user)
        server = Server.objects.get(Q(tag=server_tag), Q(creator=request.user) | Q(moderators=moderator))
...

I thought Q object will make this code conditional like if the request.user is not the creator then user is a moderator.

This works fine if the user is a moderator of the server but the creator(creator is not one of the moderators so moderator queryset will be empty) shows this error:

ServerModerator matching query does not exist.

models.py:

class Server(models.Model):
    ...
    tag = models.CharField(max_length=50, unique=True)
    moderators = models.ManyToManyField('ServerModerator', related_name='server')
    ...

class ServerModerator(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='moderator_of')
    allow_create_tag = models.BooleanField(default=True)
    allow_create_rule = models.BooleanField(default=False)
    allow_remove_user = models.BooleanField(default=True)
    allow_remove_moderator = models.BooleanField(default=False)
    allow_delete_post = models.BooleanField(default=False)
Sunderam Dubey
  • 1
  • 11
  • 20
  • 40
  • 1
    Hey! I saw you before, right? ;) Try `self.request.user`, the error you are getting is the user does not exist in a ServerModerator object. – nigel239 Aug 21 '22 at 14:30
  • hey! yes :D. `self.request.user` didn't work. and yes the user doesn't exist in ServerModerator when i sign in to the website as the creator of the server.i used Q object to see if the user is the moderator or the creator but it's wrong – Navid Azimi Aug 21 '22 at 14:38

2 Answers2

1

If the user doesn't exist, the ServerModerator .get() will return an error. One like you see above. Alternatively, you could filter like this:

server = Server.objects.get(
            models.Q(moderators__user={{your user}}) \
            | models.Q(creator={{your user}}) \
            & models.Q(tag=server_tag))

It will check if the tag AND user both filter down to one instance, OR if the user is a moderator of A server If it cannot find one, it errors.

Also, if you use .get on server, and your user owns 2 servers, it will error with a MultipleObjectsReturned error. (Though I don't know if this is in-scope of your project)

Another way to write this is with some self-made checks in place, which you can also do at a QuerySet level, if you plan to use this more often:

try:
    #Filter if the user is a creator of the server.
    server = Server.objects.get(creator=self.request.user, tag=server_tag)
    
except Server.DoesNotExist:
    # User is not a creator. Check if he's a moderator.
    moderator = get_object_or_404(ServerModerator, user=self.request.user) #If not moderator, raise 404.
    server = Server.objects.get(moderators=moderator)

    # Or without making an extra query, 
    # which is preferred if you don't do anything else with 
    # the moderator in this view.
    # Use the below:
    # server = get_object_or_404(Server, moderators__user=self.request.user)

You said:

I thought Q object will make this code conditional like if the request.user is not the creator then user is a moderator.

This would mean that, by this logic, any user that's not a creator, WILL be a moderator, instead of COULD be.

QuerySet for filtering servers.

class ServerQuerySet(models.QuerySet):
    def get_has_perms(self, user, tag):
        self = self.get(tag=tag)
        if self.creator == user \
                or self.moderators.filter(pk=user.pk).first() == user
            return self
    

    def filter_has_perms(self, user, tag):
        self = self.filter(models.Q(moderators__user=user) \
                          | models.Q(creator=user) \
                          & models.Q(tag=tag))
        return self

And then if you want to get the moderators object of the server to check permissions (Only for get_has_perms()):

server = Server.objects.get_has_perms(self.request.user, server_tag)
moderator = server.moderators.filter(id=self.request.user.id)
# If there are no moderators from the filter, the user is a creator.
nigel239
  • 1,485
  • 1
  • 3
  • 23
  • thank you for your answer. let me explain what i exactly want. so this website has many servers, each server has a creator and it can have moderators too(creator of the server can add moderators to the server just like reddit). and the code that i wrote is for the moderating page which is only accessible to moderators and creator of the server. so i used Q object to see if the user that is currently signed in is the creator or the moderator, which didn't work when the signed in user was the creator of the server. – Navid Azimi Aug 21 '22 at 15:35
  • I've tried the try except one and i get this error while trying to access the page as a moderator: get() returned more than one Server -- it returned 2! – Navid Azimi Aug 21 '22 at 15:51
  • 1
    That's what I was afraid of before. If you created 2 servers, it will return that. If you are a moderator of 2 servers, it will return that. Only `.filter()` can fix this, but then you don't know which server the user wants, you have to figure that out before you can then get the moderator to perform the logic on. Maybe display them in a listview? Then have the user choose based on PK, and then you can try `Server.objects.filter(pk=pk).get_has_perms(self.request.user, server_tag)` to validate wether the user has permissions, and didn't just fill in a random lucky URL. – nigel239 Aug 21 '22 at 15:53
  • 1
    but i don't understand why it gives me this error, because every server has a unique server_tag and each server only has one creator, how is ` server = Server.objects.get(creator=request.user, tag=server_tag)` returning 2? you can be a creator of more than one server but what does that suppose to do with this queryset? – Navid Azimi Aug 21 '22 at 16:04
  • @NavidAzimi try again. Improved get_has_perms, did not know you needed the check on both. – nigel239 Aug 21 '22 at 16:22
0

Firstly, use get_object_or_404() instead of get() as it calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.

It will raise Http404, if not found anything.

Secondly, while using class based views use self.request.user as stated by @nigel239.

So:

class ServerModeratingView(LoginRequiredMixin, View):
    def get(self, request, server_tag):
        moderator = get_object_or_404(ServerModerator,user=self.request.user)
        server = Server.objects.get(Q(tag=server_tag), Q(creator=self.request.user) | Q(moderators=moderator))
...
Sunderam Dubey
  • 1
  • 11
  • 20
  • 40
  • why should i use `self.request.user` instead of `request.user`? – Navid Azimi Aug 21 '22 at 14:57
  • 1
    @NavidAzimi As it's class not function, in classes global variables are accessed via `self.variable_name` inside methods or functions. Have you tried it? – Sunderam Dubey Aug 21 '22 at 15:03
  • 2
    @NavidAzimi refer [this](https://stackoverflow.com/q/39339221) question for further information about self.reqest or only request but the main thing here is using get_object_or_404. Also refer my [this](https://stackoverflow.com/a/73223321/17562044) answer of `View` class with files. – Sunderam Dubey Aug 21 '22 at 15:13
  • 1
    @NavidAzimi Does the problem solved or not? – Sunderam Dubey Aug 23 '22 at 02:31
  • kinda , I made a signal which will add the creator of the server that just been created to the moderators of the server and that way i don't have to check both conditions(creator & moderator) now i only have to check if the user is the moderator of the server or not. but what if adding creator of the server to the moderators didn't make sense in a different situation? – Navid Azimi Aug 23 '22 at 14:50
  • 1
    @NavidAzimi Try grouping Q objects, so `Server.objects.get((Q(tag=server_tag)& Q(creator=request.user))| (Q(moderators=moderator)))`. Can you please also share the full `Server` model, I'd debug the code after some hours. – Sunderam Dubey Aug 23 '22 at 16:06