django-invitations
django-invitations copied to clipboard
Feature Request: User Instance Creation on Invitation Acceptance
Hello,
I'm exploring the django-invitations library for a project and I have a specific requirement. I want to be able to invite a user such that when they accept the invitation, a new User instance is automatically created with a foreign key to another model. Additionally, I would like to specify the instance of the associated model at the time the invitation is created.
Is this functionality supported by the library? If not, could you provide guidance on how this might be implemented?
Thank you for your assistance.
I am doing something similar with a custom InvitationModel and allauth's user_signed_up signal. here are some snippets from my code.
class CustomInvitation(TimestampedUUIDModel, AbstractBaseInvitation):
email = models.EmailField(
unique=True,
verbose_name=_("e-mail address"),
max_length=app_settings.EMAIL_MAX_LENGTH,
)
created = models.DateTimeField(verbose_name=_("created"), default=timezone.now)
first_name = models.CharField(max_length=30, verbose_name=_("first name"))
last_name = models.CharField(max_length=30, verbose_name=_("last name"))
phone_number = models.CharField(
max_length=25,
verbose_name=_("phone number"),
blank=True,
)
driving_school = models.ForeignKey(DrivingSchool, on_delete=models.CASCADE, verbose_name=_("Driving School"))
role = models.CharField(max_length=30, choices=User.ROLE_CHOICES, verbose_name=_("Role"))
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
object_id = models.UUIDField(null=True, blank=True, verbose_name=_("Object ID"))
related_object = GenericForeignKey("content_type", "object_id")
invitation = CustomInvitation.create(
self.cleaned_data["contact_email"],
inviter=self.user, # You need to provide the user who sends the invitation
first_name=self.cleaned_data["contact_first_name"],
last_name=self.cleaned_data["contact_last_name"],
phone_number=self.cleaned_data["contact_phone_number"],
driving_school=driving_school,
role=User.ROLE_DRIVING_INSTRUCTOR,
related_object=instructor,
content_type=ContentType.objects.get_for_model(instructor),
object_id=instructor.pk,
)
invitation.sent = timezone.now()
invitation.save()
invitation.send_invitation(self.request)
from allauth.account.signals import user_signed_up
@receiver(user_signed_up)
def create_user_instance_on_signup(sender, **kwargs):
user = kwargs["user"]
try:
invitation = CustomInvitation.objects.get(email__iexact=user.email)
except CustomInvitation.DoesNotExist:
return
# get the role string from the invitation and add the user to the group with the same name
role = invitation.role
group = Group.objects.get(name=role)
user.groups.add(group)
user.first_name = invitation.first_name
user.last_name = invitation.last_name
user.phone_number = invitation.phone_number
user.role = role
# special case for driving school contacts, they are staff members and are allowed to access the admin
if role == User.ROLE_DRIVING_SCHOOL_CONTACT:
user.is_staff = True
elif role == User.ROLE_LEARNER_DRIVER:
license = invitation.related_object.license
# set status to STATUS_REDEEMED
license.status = license.STATUS_REDEEMED
license.redeemed_on = timezone.now()
license.save()
user.save()
related_object = invitation.related_object
related_object.user = user
related_object.save()
Thank you @krystofbe for sharing those snippets They've help me a lot in building my solution
I'm am curious though why you choose to subclass AbstractBaseInvitation as apposed to Invitation?
along those lines I'm curious how you went about implementing the create send_invitation and key_expired methods, did you just copy from the Invitation model?
Hi @abe-101,
There's no particular reason for choosing to subclass AbstractBaseInvitation over Invitation. It was a design choice that could have gone either way. You're absolutely correct that subclassing Invitation would have been just as viable.
Regarding the implementation of the create, send_invitation, and key_expired methods, yes, I initially copied them from the Invitation model. However, I customized them to fit the specific needs of the application I was working on. The idea was to leverage existing functionality while making necessary adjustments to align with the new subclass's requirements.
You've made a good point, and it's definitely something to consider for future design decisions. Subclassing directly from Invitation could simplify the process, especially if the modifications are minimal.
Thanks for your insight!
It looks like you've found a solution to your problem, and you don't need any changes to be made to django-allauth. I'm closing this issue. Let us know if I misunderstood.
@Flimm If you'd liek I can a section to the docs explaining this technique. We can keep this issue open or just open a new one