I have an interesting design decision to be made in the context of a Python Django model that I'm planning to eventually release.
The classes model an ApprovalRequest, which represents a question / request asked for by a user that can be voted on by another user group, which can also be appealed, which can then be voted on again by a "higher level" group, etc.
I have what amounts to the following model tree:
- ApprovalRequest (Single request)
- User requesting action
- Action type* / Additional Data
- ApprovalStage (Possibly many stages of voting, sequential in time)
- Associated ApprovalRequest
- Vote type (simple majority, one vote needed, etc)
- Users that can vote*
- ApprovalVote (A single vote, cast by a single voter)
- Associated ApprovalStage
My first question is in relation to "Action Type". Since my intent is that this becomes a reusable module that anyone can use, I'd like it to emit signals when events take place. However, this model works for a number of things - moderation, user promotion, user ban appeal decisions, etc. As such, some sort of 'type' parameter is needed. A classic way of solving this in OOP design is to allow subclasses to be created and have custom actions, which is fine, but Django (on particular) has no simple way of getting the "correct" subclass back from the DB. For example, using the built in model inheritance, there would be a main ApprovalRequest db table and N additional db tables for each child's data.
My initial thought here is to create a subclass "registry", which would assign a unique id for my type field so I can reliably downcast to a registered type when I need to take type specific action. This would force subclasses to choose a (project wide) id for their subclasses, and generally seems inelegant.
My second question is related to "Users that can vote". I expect most of these ApprovalRequest objects to actually be created by code, rather than through, eg, the Django admin. Suppose the following actions take place:
- A user requests a promotion (or some other action)
- The ApprovalRequest object is created, specifying that all users matching a certain code-dependant group (ie. a complex queryset) are allowed to vote
- Time passes without any action taken on the ApprovalRequest
- Ten new users now meet the criteria of the complex queryset (that is, had the ApprovalRequest been created now, these users would also be a part of the voting group)
- I want these new users to be able to vote
The question is, more or less, how do I store this complex queryset in a way that is secure? I can delegate this work to subclasses based on type, but that seems inelegant. I could try storing the queryset in the database somehow, but that seems a bit hacky. What is the standard route to choose?
Thanks for any advice you may have on these questions.