このシナリオに対処するために実装したモデルを次に示します。
class ConsortiumRule(OrganizationModel):
BY_EMPLOYEE = 1
BY_CLIENT = 2
BY_OCCUPATION = 3
BY_CLASSIFICATION = 4
TYPES = (
(BY_EMPLOYEE, 'Include a specific employee'),
(BY_CLIENT, 'Include all employees of a specific client'),
(BY_OCCUPATION, 'Include all employees of a speciified client ' + \
'that have the specified occupation'),
(BY_CLASSIFICATION, 'Include all employees of a specified client ' + \
'that have the specified classifications'))
consortium = models.ForeignKey(Consortium, related_name='rules')
type = models.PositiveIntegerField(choices=TYPES, default=BY_CLIENT)
negate_rule = models.BooleanField(default=False,
help_text='Exclude people who match this rule')
class ConsortiumRuleParameter(OrganizationModel):
""" example usage: two of these objects one with "occupation=5" one
with "occupation=6" - both FK linked to a single Rule
"""
rule = models.ForeignKey(ConsortiumRule, related_name='parameters')
key = models.CharField(max_length=100, blank=False)
value = models.CharField(max_length=100, blank=False)
最初は、他のオブジェクトへの参照を CharField に格納するという考えが気に入らなかったので、この解決策には抵抗がありました (最も用途が広いため、CharField が選択されました。名は「じょう」で始まります)。ただし、この種のマッピングをリレーショナル データベースに格納するには、これが最適なソリューションだと思います。これが良いアプローチである理由の 1 つは、ぶら下がっている参照をきれいにするのが比較的簡単だからです。たとえば、会社が削除された場合、次のことを行うだけで済みます。
ConsortiumRuleParameter.objects.filter(key='company', value=str(pk)).delete()
パラメータがシリアル化されたオブジェクト (たとえば、コメントで提案されている Q オブジェクト) として格納されている場合、これははるかに困難で時間がかかります。