You’re right, if you define the membership table explicitly then you don’t need to use a ManyToManyField.
The only real advantage to having it is if you’d find the related manager convenient. That is, this:
group.members.all() # Persons in the group
looks nicer than this:
Person.objects.filter(membership_set__group=group) # Persons in the group
In practice, I think the main reason for having both is that often people start with a plain ManyToManyField; realize they need some additional data and add the table explicitly; and then continue to use the existing manager because it’s convenient.