Nafisa Shazia writes:
Hello Barry,
I had the same (roundabout) problem as my peer in regards to passing parameters to the REST API.
Khushboo's solution is going to be different, although most of the following general discussion should apply.
My main question is: How deep do I need to go into the subscription workflow to add a single field?
The short answer is: *you* don't. In Mailman 2, there was no user concept, only subscribers (also called members in that context). A subscription was a relationship between an email address and a mailing list. This was implemented as a one-many attribute of the list. Also, in general it was not possible to identify whether two addresses correspond to the same person.
In Mailman 3, there *is* a user model, and you can be a user without subscribing to any lists (eg, a moderator), but every subscription is a relationship between a user and a mailing list. This is implemented as a many-many relationship:
def __init__(self, essay, role, list_id, subscriber):
self._member_id = uid_factory.new_uid()
self.role = role
self.essay = essay
self.list_id = list_id
[Note: at least in Chrome, Google docs does *not* format your code as Python code, nor does copy-paste give valid Python code.]
Unfortunately you omitted the rest of the function. So your Google doc is pretty, but it makes people trying to help you do a lot of extra work. :-( Here's the rest of that function:
if IAddress.providedBy(subscriber):
self._address = subscriber
# Look this up dynamically.
self._user = None
elif IUser.providedBy(subscriber):
self._user = subscriber
# Look this up dynamically.
self._address = None
else:
raise ValueError('subscriber must be a user or address')
if role in (MemberRole.owner, MemberRole.moderator):
self.moderation_action = Action.accept
elif role is MemberRole.member:
self.moderation_action =
getUtility(IListManager).get_by_list_id(
list_id).default_member_action
else:
assert role is MemberRole.nonmember, (
'Invalid MemberRole: {0}'.format(role))
self.moderation_action =
getUtility(IListManager).get_by_list_id(
list_id).default_nonmember_action
This makes it clear that it's possible to subscribe just an address. However, under the hood Mailman provides a dummy user. (This is implied by the comment "Look this up dynamically.")
Now, in the Systers application, you don't want address-only anonymous subscribers, you want users who have "personalities", ie, user objects with the appropriate attributes. So you should be adding your "essay" attribute to the *users*, not to (list) members. I'm not sure about the UI, there are a couple of possibilities. You have to take account of the fact that there are non-Systers like me who subscribe to the lists, although that could be done by list administrators if preferred.
(IMHO, the term "member" is very confusing, because there's a natural tendency to think of a member as being a person and assuming it has personal attributes, but that's not the case at all in Mailman 2, and in Mailman 3 it might not be the case.)
The long answer is actually shorter<wink />. Most likely you should not need to change the subscription workflow at all. If you do, it's going to require deep surgery and a lot of work. Consider the signature of on_post. It is the same for *all* classes that implement it. (This is what Pythonistas call "duck-typing" or "protocol".) To change it for one class, you need to change it for all.
Instead, you want to find the *object* (typically only one) that needs to change and change its model. This typically requires changing an interface (under interfaces) as well as the model itself (under model), and often other code used in the implementation. In the case of adding the essay, you need to add the attribute in interfaces/user.py, and initialize it in model/user.py. For some reason there's no rest/user.py, but probably in rest/users.py you will want to add code in an appropriate class's on_post method to retrieve the "essay" parameter from the request and attach it to the appropriate AUser object.
Note that an __init__ method is *not* a public protocol (you never see code that invokes it as x.__init__(arg1, arg2, ...)), so it can (and does) vary a lot from class to class. The only requirement is that the first argument refer to the object itself (conventionally "self", but some programmers use "s" or "me" or "this") so that it can be called using object.method() syntax. The on_* methods on the other hand are invoked by various workflows in exactly the same way for any object they receive, regardless of type. (That's implicitly the definition of "protocol" -- "do it this way and it just works".)
Regards, Steve