Модель множественного внешнего ключа Django
Вот мой код, есть ли более эффективный способ его написания? Я не крут с этим.
Как правило, модели как компании, так и поставщика должны иметь возможность иметь несколько контактов с несколькими телефонными номерами.
class Contact(models.Model):
company = models.ForeignKey(Company, related_name='contact',
blank=True, null=True)
supplier = models.ForeignKey(Supplier, related_name='contact',
blank=True, null=True)
name = models.CharFields(max_length=50, blank=True, null=True)
class Phone(models.Model):
contact = models.ForeignKey(Contato, related_name='phone')
number = models.CharFields(max_length=50, blank=True, null=True)
2 ответа
Существует как минимум четыре подхода к решению проблемы "обе компании типа X и компании типа Y имеют контакты":
- Две таблицы: компании и контакты. В компаниях есть перечислимое свойство со значениями X и Y, и у каждого контакта есть один внешний ключ для компании.
- Три таблицы: одна таблица X для X-компаний, другая таблица Y для Y-компаний и одна таблица для контактов C, где C имеет внешние ключи для X и Y. Внешние ключи могут иметь значение NULL.
- Четыре таблицы: X, Y, Cx и Cy, отслеживающие два разных контакта для двух разных типов компаний по отдельности. (Таким образом, у Cx есть внешний ключ к X, а у Cy есть внешний ключ к Y).
- Пять таблиц: вы начинаете с этих трех таблиц X, Y и C, но вместо добавления обнуляемых указателей на C вы добавляете две соединяющие таблицы "многие ко многим" XC и YC.
У них разные требования к базовым данным. Вы сейчас используете решение с тремя столами (X, Y, C) = (Company, Supplier, Contact)
, Это замечательно, если некоторые контакты будут передаваться между компаниями и поставщиками, поэтому вам иногда нужно спросить "кто является контактом между этой компанией и этим поставщиком?". Я поддерживаю базу данных, в которой используется решение с двумя таблицами, и когда оно было первоначально принято, оно было хорошим решением (зачем дублировать всю эту логику об адресах и контактах, когда нам это не нужно?), Но сегодня это кажется неуклюжим (потому что таблица "Company" содержит поля, которые имеют смысл только для X и Y отдельно).
Вероятно, в моем случае проще всего было бы решить проблему, если бы мы мигрировали, - решение за четырьмя столами: хранить контакты для компаний X-типа полностью отдельно от контактов для компаний Y-типа. Если вы начнете с вашего текущего подхода, тогда решение из пяти таблиц будет очевидным обобщением, если вы столкнетесь с подобными проблемами роста в вашем приложении.
Что касается отслеживания телефонных номеров, у вас есть некоторые ограниченные возможности:
- Сохраните несколько столбцов в таблице контактов, по одному для каждого отдельного номера телефона. Это становится ужасно быстро, но это простой и быстрый способ сделать это. Это называется "денормализованными" данными.
- Сохраните JSON в текстовом поле в списке контактов. Телефонные номера вряд ли будут обыскивать слишком часто; просто не очень часто говорят: "У меня есть этот номер, кому он принадлежит?", так что вы можете легко денормализовать. Это также позволяет вам делать такие вещи, как
{"mon thru thurs": 12025551234, "fri, sat": 12025554321}
, сохраняя простые пользовательские аннотации для чисел. - Создайте телефонный стол, как вы сделали сейчас. Это наиболее общий способ сделать это, и если вам нужны такие аннотации, вы можете добавить другое текстовое поле в эту таблицу.
Если вы смешаете вариант 3 здесь с вариантом 3 выше (четыре таблицы плюс явная таблица телефона), то вы, вероятно, захотите иметь отдельные таблицы телефона, а также отдельные таблицы контактов; Px и Py каждый с внешним ключом для Cx и Cy.
Мне Company
а также Supplier
модели могут быть одинаковыми. Так как большинство поставщиков являются компаниями, верно? Если они в основном совпадают, чем слияние Company
а также Supplier
модели как это:
class Company(models.Model):
name = models.CharFields(max_length=50)
is_supplier = models.BooleanField(default=False)
suppliers = models.ManyToManyField("self",
limit_choices_to={'is_supplier': True})
class Contact(models.Model):
name = models.CharFields(max_length=50)
company = models.ForeignKey(Company)
class Phone(models.Model):
number = models.CharFields(max_length=50)
contact = models.ForeignKey(Contact)
Это решение Криса Дростса "2 стола". Если вам нужны специфичные для поставщика поля, добавьте модель поставщика и свяжите ее с компанией с помощью OneToOne:
class Supplier(models.Model):
... # Some supplier specific fields.
company = models.OneToOneField(Company)