1

I have a model for Group:

class Group(models.Model):
   leader = models.ForeignKey(User, on_delete=models.CASCADE)
   name = models.CharField(max_length=55)
   description = models.TextField()
   joined = models.ManyToManyField(User, blank=True)
   size = models.BooleanField(default=False)
   max_size = models.IntegerField(default=10)
   closed = models.BooleanField(default=False, blank=True)

The idea is that Users can join groups (thus adding to the total joined for that Group) and leave groups. They do that through this view:

def join_group(request, pk):
    id_user = request.user.id
    group = Group.objects.get(id=request.POST.get('group_id'))
    account = Account.objects.get(user_id=id_user)
    
    if group.joined.filter(id=request.user.id).exists():
        group.joined.remove(request.user)
        account.joined_groups.remove(group)
    else: 
        group.joined.add(request.user)
        account.joined_groups.add(group)

    return HttpResponseRedirect(reverse('group_detail', args=[str(pk)]))

And template:

{% if user.is_authenticated %}
  <form action="{% url 'join_group' group.pk %}" method="POST">
    {% if group.closed == True %} 
      <div>Sorry, this group is closed</div>
    {% else %}
    {% csrf_token %}
      {% if joined %}
        <button type="submit" name="group_id" value="{{group.id}}">LEAVE</button>
      {% else %}
        <button type="submit" name="group_id" value="{{group.id}}">JOIN</button>  
      {% endif %}
    {% endif %}
  </form>
{% endif %}

Users joining and leaving groups works fine, the thing I'm trying to implement is when the total_joined == group.max_size a function would be called to close the group. Since, as of now the only way too close a Group is for the User who created it to go and update the Group to change closed=True.

What I'm trying to do is call a function within the template to close the Group when it's reached max capacity ( or max_size). The basic conditional works but I don't know how to call the function or even how to edit the current Group to change closed=True:

{% if group.total_joined == group.max_size %}
   {{ close_group function would go here }}
{% endif %}

the close_group view:

def close_chaburah(request, pk):
    group = get_object_or_404(Group, id=request.POST.get('group_id'))
    chaburah.closed == True
    return HttpResponseRedirect(reverse('group_detail', args=[str(pk)]))

Is there a way to call this function once group.total_joined == group.max_size using my conditional or does it have to triggered in a different way. Or should it be done in the join_group view?

To test the conditional I hardcoded a link to call the function to close a Group:

{% if group.total_joined == group.max_size %}
   <a href="{% url 'close_group' group.pk %}">Close Group</a>
{% endif %}

This worked in the sense that the link showed up when the conditional was met, but when I clicked the link I got an error group.models.Group.DoesNotExist: Group matching query does not exist

Is there a better way to close a Group? Should I be calling the function with the conditional in the view? Or should I call it in the template? Should it be in join_group or in a specific close_group function?

Yehuda
  • 27
  • 15
  • Why don't you "close" the group in your _view_ itself when you add users? Templates are not meant to be used for such purpose. – Abdul Aziz Barkat Jul 18 '22 at 16:40
  • That's what I wanted to do originally, but I couldn't figure out how to set the group to closed from the view of `join_group` – Yehuda Jul 18 '22 at 17:08

2 Answers2

1

I would create a Model method that checks whether the group is full or not, you can then use this in your template and view more easily then recreating any logic.

class Group(models.Model):
   ...

   @property
   def is_full(self):
       return self.total_joined == self.max_size

I would then include a post_save signal that checks the is_full property and updates closed if so:

@receiver(models.signals.post_save, sender=Group)
def set_closed(sender, instance, using, **kwargs):
    print("Signal triggered") # check signal is firing as expected
    if instance.is_full and not instance.closed:
        print("Instance is full and not closed") # check logic works
        instance.closed = True
        instance.save()
    elif not instance.is_full and instance.closed:
        print("Instance is not full but it is closed") # check logic works
        instance.closed = False
        instance.save()
0sVoid
  • 2,547
  • 1
  • 10
  • 25
  • The @reciever goes in the model or in one of the views? – Yehuda Jul 18 '22 at 17:07
  • It's completely separate to a model class or view, it usually goes in the `models.py` so it's close to the model code – 0sVoid Jul 18 '22 at 18:21
  • it says 'self' is not defined – Yehuda Jul 18 '22 at 18:26
  • Sorry, should be `instance.closed` instead of `self.closed` – 0sVoid Jul 18 '22 at 18:36
  • That gets rid of the error but I'm not sure how to call this. Is it called by itself? Every time the Group is updated? Do I access it in my template? Right now the code looks fine, but Group isn't becoming `closed` from having `return self.total_joined() == self.max_size` – Yehuda Jul 18 '22 at 19:06
  • It's based specifically on the post_save signal, you will need to import `from django.dispatch import receiver` - how is `total_joined()` calculated? – 0sVoid Jul 18 '22 at 19:36
  • `def total_joined(self): return self.joined.count()` is in Group model – Yehuda Jul 18 '22 at 19:37
  • Looks okay to me - I would debug this by adding some print statements to the signal to make sure it's getting called and that your functions are returning the expected values. See my updated answer – 0sVoid Jul 19 '22 at 07:10
1

For this, you might want to perform ac check in your join function prior to adding the user and after adding the user. This way, if the group is full, you can return that to the view, or if the user is the last to join the group, you can then close it. something like this may work:

def check_group(group):
    if group.max_size == group.size and group.closed == False:
        # Closes group because it has rached max size
        group.closed = True
        group.save()
        # returns True since group has reached max size
        return group.closed
    elif group.max_size == group.size:
        # Still returns True because the group is closed
        return group.closed
    else:
        # Returns False since group is not max size or closed, new member can be added
        return group.closed

Call this once before trying to add the member to the group. If it returns False, add the member, if it returns true, tell them the group is full. Then call this again after the member is added to see if the group needs to be closed now that they have joined.

Sogetsue
  • 145
  • 10
  • Should it be `and` or `AND`? – Yehuda Jul 18 '22 at 19:10
  • and, sorry about that. I've edited the post to correct it – Sogetsue Jul 18 '22 at 19:14
  • Yeah that got rid of the error, but I'm still not understanding where exactly I'm calling this function – Yehuda Jul 18 '22 at 19:15
  • You can call it within your join_group method, after you get the correct group from your model. You check the group to see if its full, if it returns False then its not full and you can add the person. If it returns true, then you can't add them. Then call it again after adding the person to see if the group is now full after the addition, if so close the group. – Sogetsue Jul 18 '22 at 19:18
  • So you can put this function above your views, it doesn't have to be in them. Then in your join_group method after you do `group=Group.objects.get()` make a variable using `groupFull = check_group(group)` This will return either True of False. `if groupFull == False` Add the person and call `check_group(group)` again. Otherwise, if it returns True then it is already full – Sogetsue Jul 18 '22 at 19:20
  • this worked but I had to add `group.closed = False` and `group.save()` before the last `return group.closed` because when the last User who joined then leaves the Group, the group remained closed. – Yehuda Jul 18 '22 at 19:43
  • Awesome! I'm really glad that worked for you! I'll try to be a bit more concise in future answers to make it easier. – Sogetsue Jul 18 '22 at 19:46
  • 1
    No you were good. It was kind of abstract. I think I'm gonna add a diff field on Group for full, and leave `closed` as a boolean for the creator of the Group to close the Group for whatever reason. Vs full which is a field that goes by how many are joined not the creator's descretion. – Yehuda Jul 18 '22 at 19:47