Django 模型设计——多对多字段

Django Model Design - Many-To-Many Fields

来自 php 背景,我知道我会怎么做 'by hand' 但我似乎无法理解如何使用 Django ORM 构建我的数据库。

该应用以清单为中心。清单有一些识别特征,比方说 NameDateOfBirthChecklistType

有几个ChecklistType,每个都有很多ChecklistOption。一个 ChecklistOption 可能在几个 ChecklistType 中,也可能不在

我最初的想法是为 ChecklistTypeChecklistTypeOptions 创建一个模型来存储清单的 'configuration',并为 Checklist 和 [= 创建一个单独的模型23=](分别)到 link 到 Customer 模型。我想我会手动查找配置并在创建 Customer 的视图中创建后面的模型条目。然后我意识到这对 ORM 的失败!

我确信这个问题可以通过正确理解在模型中使用多对多字段的方式来解决,但是 Django Documentation 似乎只处理这个的简单应用.如果我在 ChecklistChecklistOption 中创建 ManyTomany 字段(使用 ChecklistType 作为 ChoiceField),我将无法指定哪些选项适用于该类型。

我确信这种事情必须定期进行,但我无法理解它。

提前致谢,

澄清 - 示例数据

清单选项

Cheese {boolean}
Tomato {boolean}
Bacon {boolean}
Chicken {boolean}
Coffee {boolean}
Pepperoni {boolean}
Salami {boolean}
Mushrooms {boolean}
Eggs {boolean}

清单类型

ThingsToPutOnPizza: {options: Cheese, Tomato, Bacon, Chicken, Salami, Mushrooms}
ThingsToEatAtBreakfast: {options: Cheese, Bacon, Coffee, Eggs}

客户

John Smith: {Name: "John Smith", DOB: 1984-12-17, Checklist: ThingsToEatAtBreakfast}
Jane Jones: {Name: "Jane Jones", DOB: 1987-07-22, Checklist: ThingsToPutOnPizza}
Frank Allen {Name: "Frank Allen", DOB: 1990-04-01, Checklist: ThingsToPutOnPizza}

工作流程

用户创建客户,并选择清单类型。另一个用户必须 'complete' 核对表,因此从列表中选择客户,并显示该核对表类型中选项的复选框列表,在这些选项中,他们可能会勾选一些而不是其他选项。在保存 form/model 他们的用户时,时间会根据清单记录以将其标记为完成。

让我们从基本要求开始:

The app centres around a checklist. A Checklisthas some identifying features, lets say Name, DateOfBirth and ChecklistType

There are several ChecklistTypes, each of which has many ChecklistOptions. A ChecklistOption may or may not be in several ChecklistTypes

以下模型可以满足您的初始要求:

class CheckListOption(models.Model):
    name = models.CharField(max_length=200)

class CheckListType(models.Model):
    name = models.CharField(max_length=200)
    options = models.ManyToManyField(CheckListOption)

class Checklist(models.Model):
    name = models.CharField(max_length=200)
    dob = models.DateField()
    type = models.ForeignKey(CheckListType)

现在,考虑到这些;让我们看看您打算如何使用这些模型:

A user creates a customer, and selects the checklist type. Another user has to 'complete' the checklist, so selects the customer from list and is presented with a list of checkboxes for the options in that checklist type, and of those options they may tick some and not others. On saving the form/model their user and the time is logged against the checklist to mark it as complete.

为了处理您的跟踪,您需要向 Checklist 模型添加更多字段:

class Checklist(models.Model):
    name = models.CharField(max_length=200)
    dob = models.DateField()
    type = models.ForeignKey(CheckListType)
    created_by = models.ForeignKey(User, name='creator')
    created_on = models.DateTimeField(auto_now_add=True)
    completed_by = models.ForeignKey(User,
                                     name='maker', blank=True, null=True)
    completed_on = models.DateTimeField(blank=True, null=True)

    def is_complete(self):
        return self.completed_on

为了满足您 "and of those options they may tick some and not others" 的要求,您需要修改 CheckListType 模型,方法是创建自定义 through model;这将允许您指定关系的选项:

class CheckListOptionConfig(models.Model):
    checklisttype = models.ForeignKey('CheckListType')
    optiontype = models.ForeignKey('CheckListOption')
    is_required = models.BooleanField(default=False)

class CheckListType(models.Model):
    name = models.CharField(max_length=200)
    options = models.ManyToManyField(CheckListOption, through='CheckListOptionConfig')

您的工作流程的其余部分描述了用户界面;如果没有勾选正确的选项,它可以很容易地配置为显示 alerts/warnings 的所有方式。

这可能是最简单的解决方案:

models.py

from django.db import models

class CheckList(models.Model):
    name = models.CharField(max_length=255)
    checklist_type = models.ForeignKey('CheckListType')
    options = models.ManyToManyField('CheckListOption', blank=True)

    def __unicode__(self):
        return self.name

class CheckListType(models.Model):
    name = models.CharField(max_length=255)
    options = models.ManyToManyField('CheckListOption')

    def __unicode__(self):
        return self.name

class CheckListOption(models.Model):
    name = models.CharField(max_length=255)

    def __unicode__(self):
        return self.name

forms.py

from django import forms

from .models import CheckList, CheckListOption

class CheckListForm(forms.ModelForm):
    class Meta:
        model = CheckList
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(CheckListForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            self.fields['options'].queryset = CheckListOption.objects.filter(
                checklisttype=self.instance.checklist_type_id
            )
        else:
            self.fields['options'].queryset = CheckListOption.objects.none()

admin.py

from django.contrib import admin

from .forms import CheckListForm
from .models import CheckList, CheckListType, CheckListOption

class CheckListAdmin(admin.ModelAdmin):
    form = CheckListForm

admin.site.register(CheckList, CheckListAdmin)
admin.site.register(CheckListType)
admin.site.register(CheckListOption)

只有一个缺点 - 当您已经保存了一个 CheckList 实例并且您想要更改 checklist_type 时,您暂时不会获得新的 options。进行更改的用户应该取消选择所选的选项(这是一种可选的,但如果不这样做,所选的选项将保留到下一次保存),保存模型并编辑它再次选择新选项。