Модель множественного внешнего ключа 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 имеют контакты":

  1. Две таблицы: компании и контакты. В компаниях есть перечислимое свойство со значениями X и Y, и у каждого контакта есть один внешний ключ для компании.
  2. Три таблицы: одна таблица X для X-компаний, другая таблица Y для Y-компаний и одна таблица для контактов C, где C имеет внешние ключи для X и Y. Внешние ключи могут иметь значение NULL.
  3. Четыре таблицы: X, Y, Cx и Cy, отслеживающие два разных контакта для двух разных типов компаний по отдельности. (Таким образом, у Cx есть внешний ключ к X, а у Cy есть внешний ключ к Y).
  4. Пять таблиц: вы начинаете с этих трех таблиц X, Y и C, но вместо добавления обнуляемых указателей на C вы добавляете две соединяющие таблицы "многие ко многим" XC и YC.

У них разные требования к базовым данным. Вы сейчас используете решение с тремя столами (X, Y, C) = (Company, Supplier, Contact), Это замечательно, если некоторые контакты будут передаваться между компаниями и поставщиками, поэтому вам иногда нужно спросить "кто является контактом между этой компанией и этим поставщиком?". Я поддерживаю базу данных, в которой используется решение с двумя таблицами, и когда оно было первоначально принято, оно было хорошим решением (зачем дублировать всю эту логику об адресах и контактах, когда нам это не нужно?), Но сегодня это кажется неуклюжим (потому что таблица "Company" содержит поля, которые имеют смысл только для X и Y отдельно).

Вероятно, в моем случае проще всего было бы решить проблему, если бы мы мигрировали, - решение за четырьмя столами: хранить контакты для компаний X-типа полностью отдельно от контактов для компаний Y-типа. Если вы начнете с вашего текущего подхода, тогда решение из пяти таблиц будет очевидным обобщением, если вы столкнетесь с подобными проблемами роста в вашем приложении.

Что касается отслеживания телефонных номеров, у вас есть некоторые ограниченные возможности:

  1. Сохраните несколько столбцов в таблице контактов, по одному для каждого отдельного номера телефона. Это становится ужасно быстро, но это простой и быстрый способ сделать это. Это называется "денормализованными" данными.
  2. Сохраните JSON в текстовом поле в списке контактов. Телефонные номера вряд ли будут обыскивать слишком часто; просто не очень часто говорят: "У меня есть этот номер, кому он принадлежит?", так что вы можете легко денормализовать. Это также позволяет вам делать такие вещи, как {"mon thru thurs": 12025551234, "fri, sat": 12025554321}, сохраняя простые пользовательские аннотации для чисел.
  3. Создайте телефонный стол, как вы сделали сейчас. Это наиболее общий способ сделать это, и если вам нужны такие аннотации, вы можете добавить другое текстовое поле в эту таблицу.

Если вы смешаете вариант 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)
Другие вопросы по тегам