Как создать общую модель в Django? - PullRequest
2 голосов
/ 05 июня 2019

У меня есть пара моделей, которым нужен общий набор полей. Это в основном набор различных типов контактов:

# models I would like to share
class Address(models.Model):
    label = models.CharField(_('label'), max_length=50, blank=True)
    street1 = models.CharField(_('street1'), max_length=125, blank=True)
    street2 = models.CharField(_('street2'), max_length=125, blank=True)
    city = models.CharField(_('city'), max_length=50, blank=True)
    state = models.CharField(_('state'), max_length=2, blank=True)
    zip_code = models.CharField(_('zip_code'), max_length=10, blank=True)

class Phone(models.Model):
    label = models.CharField(_('label'), max_length=50, blank=True)
    phone = models.CharField(_('phone'), max_length=50, blank=True)


# these are the models that I would like to have addresses and phone numbers
class Contact(models.Model):
    name = models.CharField(_('name'), max_length=50, blank=False)

class Organization(models.Model):
    name = models.CharField(_('name'), max_length=255, blank=False)
    email = models.CharField(_('email'), max_length=100, blank=True)
    website = models.CharField(_('website'), max_length=255, blank=True)

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    photo = models.CharField(_('photo'), max_length=255, blank=True)

Я хотел бы поделиться моделями Phone и Address с моделями Contact, Organization и UserProfile. Моей первой попыткой было добавить ForeignKey к каждому из Contact, Organization и UserProfile, но после дополнительных исследований я считаю, что это наоборот, поэтому я переместил ForeignKey в Address и Phone, но затем обнаружил, что ForeignKey может принадлежать одной и только одной модели. В дополнение к разделению этой структуры данных между несколькими различными типами контактов, я хотел бы иметь возможность добавлять к контакту более одного адреса или номера телефона. Например, у контакта может быть домашний адрес, рабочий адрес, номер мобильного телефона и рабочий номер. Итак, у меня есть в основном 2 вопроса:

1) Разумно ли делиться моделью таким способом?
2) Как бы я занялся настройкой моделей?

Ответы [ 2 ]

2 голосов
/ 06 июня 2019

Если я правильно понял; все модели Contact, Organization и UserProfile должны иметь поля для Address и Phone. Более того, каждый из них может иметь более одного адреса / телефона.

Разумно ли это? Мне так кажется.

Как вы могли бы заняться настройкой моделей? Родовые отношения возникают у меня на уме.

Рассмотрим только Contact и Address на данный момент. Ваша вторая попытка верна: у нас есть отношение Много-к-одному (один контакт, много адресов), поэтому нам нужно сделать Contact полем ForeignKey в модели Address, например, так:

class Address(models.Model):
    <other_fields>
    contact = models.ForeignKey('Contact', on_delete=models.CASCADE)

Это позволяет назначать несколько адресов каждому контакту.

Продолжая, вы, по сути, хотите, чтобы ваша модель Address имела несколько внешних ключей: Contact, Organization и UserProfile. Одним из способов достижения этого является использование общих отношений. Это использует встроенную в Django структуру «contenttypes» и позволяет вам создавать GenericForeignKey полей, которые указывают на более чем одну модель. Я рекомендую вам прочитать связанные документы, поскольку родовые отношения не так тривиальны. В итоге у вас будет что-то вроде:

class Address(models.Model):
    label = models.CharField(max_length=50, blank=True)
    street_1 = models.CharField(max_length=125, blank=True)
    street_2 = models.CharField(max_length=125, blank=True)
    etc...

    models_with_address = models.Q(app_label='app_name', model='contact') | \
                          models.Q(app_label='app_name', model='organization') | \
                          models.Q(app_label='app_name', model='userprofile')

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to=models_with_address)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey()

При этом вы можете создать несколько адресов для каждой из моделей, указанных в запросе models_with_address. Чтобы иметь возможность запрашивать адреса для данного контакта / организации / и т. Д., Вам потребуется обратное родовое отношение . Настройка включает добавление строки address = GenericRelation(Address) к соответствующим моделям.

Для дальнейшего обобщения вы можете создать ContactableModel (или любой другой) класс:

class ContactableModel(models.Model):
    address = GenericRelation('Address')
    phone = GenericRelation('Phone')

Любая модель с адресом и номером телефона (контакт, организация и т. Д.) Может наследовать это, чтобы вам не приходилось многократно включать эти два поля. Вы также можете улучшить предел models_with_address, чтобы у нас было что-то вроде limit_choices_to=<subclasses_of_ContactableModel>.

Надеюсь, это поможет!

1 голос
/ 06 июня 2019

Простым решением было бы добавить ManyToManyField(Address|Phone) (т.е. два поля M2M) к Contact, Organization и UserProfile.

Но это означало бы, что разные контакты / организации / пользователи могут обмениваться адресами.Хотя это выглядит заманчиво, проблема с таким решением состоит в том, что если кто-то редактирует адрес контакта, он также меняет адрес для всех оставшихся объектов в системе!


Другое возможное решение, которое позволяет избежатьПриведенная выше проблема не требует полей M2M или общих отношений: наследование нескольких таблиц :

class Address(models.Model):
  entity = models.ForeignKey(Entity)
  ...

class Entity(models.Model):
  pass

class Contact(Entity):
  ...

class Organization(Entity):
  ...

(Под капотом Django создает неявные OneToOneField -ы, которыеточка от Contact & Organization до Entity.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...