Skip to content

Instantly share code, notes, and snippets.

@AphonicChaos
Last active January 8, 2016 14:56
Show Gist options
  • Save AphonicChaos/0aa32ae0de80bb31d8b1 to your computer and use it in GitHub Desktop.
Save AphonicChaos/0aa32ae0de80bb31d8b1 to your computer and use it in GitHub Desktop.

Before

This snippet represents the code as it exists in QC now. Invitations have a send method on them which constructs an email based on the model instance's current values. The User.send_invite method was originally written as a convenience method so that view code could create and send an invitation as a one-stop shop, optionally assigning roles as necessary. Similar things have been done for the registration, myemails, and postajob apps, so nothing is novel about this approach.

My concern with it, however (despite ironically being the person who implemented it in this instance) is that it becomes ambiguous which code should be responsible for sending invitation emails. In particular, I could easily invision someone forgetting that User.send_invite exists, or deeming it inadequate as it also deals with roles, despite that field being optional. As such, one of the two methods is bound to get more advanced logic while the other bit rots. This is not simply paranoia, as they both already deal with the reason argument slightly differently.

After

Here' I'm suggesting that User only be concerned with the drugery involved in actually creating the invitation instance. The actual sending and customizing of body text would remain the responsibility of the actual Invitation object itself. So long as we return the created invitation instance, it would then be easy enough to chain a call to send immediately after it.

The problem I see for this is that it is unpythonic in the sense that not a lot of Python APIs are renouned for returning chainable objects. Mutation seems to be ingrained in the culture. As such, this solution could be considered unidiomatic, and thus undesireable.

# 1. An invitation can be created alone and sent with a custom reason
class Invitation(Model):
def send(self, reason=""):
# ...
# 2. For convenience, a user may invite another with a custom reason for a specific role
class User(Model):
def send_invite(self, email, company, reason="", role=None):
# ...
# client code somewhere
user = User.objects.first()
ibm = Company.objects.get(name="IBM")
user.send_invite("[email protected]", ibm, "to party")
# or manually
user = User.objects.first()
invitee = User.objects.get(email="[email protected]")
ibm = Company.objects.get(name="IBM")
invitation = Invitation.objects.create(inviting_user=user, invitee=invitee, inviting_company=ibm)
invitation.send("to party")
class Inviation(Model):
def send(self, reason=""):
# ...
class User(Model):
def create_invitation(self, email, company):
# ...
invitation = Invitation.objects.create(...)
return invitation
# client code somewhere
user = User.objects.first()
ibm = Company.objects.get(name="IBM")
user.create_invitation("[email protected]", ibm).send("to party")
@tdphillips
Copy link

Invitations aren't always created with a user. user.create_invitation works if user is the inviting user.

@AphonicChaos
Copy link
Author

Ah, if the Invitation takes care of creating the user if it doesn't exist, that's one less thing User would have to worry about.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment