[Mailman-Developers] Problem Passing Parameters to the REST API

Stephen J. Turnbull stephen at xemacs.org
Sat Jun 13 08:13:02 CEST 2015


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


More information about the Mailman-Developers mailing list