Home > Blockchain >  conditional querysets Django
conditional querysets Django

Time:08-22

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)

CodePudding user response:

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))
...

CodePudding user response:

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.
  • Related