Универсальные many-many отношения

Я продолжал получать эту ошибку при использовании flyway для развертывания изменений базы данных. Сначала я делаю ручную настройку, например, создаю базу данных, так что flyway не понадобятся эти разрешения супер-администратора.

Мое исправление

Я должен был убедиться, что пользователь базы данных, который использовал задание на прохождении, имел права собственности на общедоступную схему, чтобы затем пользователь мог назначить право на использование Схема для других ролей.

Дополнительные сведения о настройке

Я использую AWS RDS (как обычную, так и Aurora), и они не позволяют суперпользователям в базах данных. RDS резервирует суперпользователей только для использования AWS, так что потребители не могут сломать встроенную репликацию. Однако есть ловушка-22, которую вы должны быть владельцем postgres, чтобы иметь возможность ее изменять.

Мое решение состояло в том, чтобы создать роль, которая действует как владелец («роль владельца»), а затем назначить и моего администратора, и пользователя flyway для роли владельца, и использовать сценарии ALTER для каждого объекта, чтобы назначить владелец объекта в роли владельца.

Я пропустил общедоступную схему, поскольку она была создана автоматически при создании сценария базы данных вручную. В общедоступной схеме по умолчанию используется моя роль администратора, а не роль общего владельца. Поэтому, когда пользователь пролетного пути пытался назначить права доступа к общей схеме другим ролям, у него не было на это полномочий. Однако ошибка не возникла во время выполнения пролетного пути.

46
задан Noel Evans 11 May 2016 в 08:47
поделиться

2 ответа

Вы можете реализовать это, используя общие отношения, вручную создав таблицу соединений между сообщением и получателем:

from django.db import models
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType

class Client(models.Model):
    city = models.CharField(max_length=16)

    # These aren't required, but they'll allow you do cool stuff
    # like "person.sent_messages.all()" to get all messages sent
    # by that person, and "person.received_messages.all()" to
    # get all messages sent to that person.
    # Well...sort of, since "received_messages.all()" will return
    # a queryset of "MessageRecipient" instances.
    sent_messages = generic.GenericRelation('Message',
        content_type_field='sender_content_type',
        object_id_field='sender_id'
    )
    received_messages = generic.GenericRelation('MessageRecipient',
        content_type_field='recipient_content_type',
        object_id_field='recipient_id'
    )

    class Meta:
        abstract = True

class PersonClient(Client):
    first_name = models.CharField(max_length=16)
    last_name = models.CharField(max_length=16)
    gender = models.CharField(max_length=1)

    def __unicode__(self):
        return u'%s %s' % (self.last_name, self.first_name)

class CompanyClient(Client):
    name = models.CharField(max_length=32)
    tax_no = models.PositiveIntegerField()

    def __unicode__(self):
        return self.name

class Message(models.Model):
    sender_content_type = models.ForeignKey(ContentType)
    sender_id = models.PositiveIntegerField()
    sender = generic.GenericForeignKey('sender_content_type', 'sender_id')
    msg_body = models.CharField(max_length=1024)

    def __unicode__(self):
        return u'%s...' % self.msg_body[:25]

class MessageRecipient(models.Model):
    message = models.ForeignKey(Message)
    recipient_content_type = models.ForeignKey(ContentType)
    recipient_id = models.PositiveIntegerField()
    recipient = generic.GenericForeignKey('recipient_content_type', 'recipient_id')

    def __unicode__(self):
        return u'%s sent to %s' % (self.message, self.recipient)

Вы бы использовали вышеуказанные модели следующим образом:

>>> person1 = PersonClient.objects.create(first_name='Person', last_name='One', gender='M')
>>> person2 = PersonClient.objects.create(first_name='Person', last_name='Two', gender='F')
>>> company = CompanyClient.objects.create(name='FastCompany', tax_no='4220')
>>> company_ct = ContentType.objects.get_for_model(CompanyClient)
>>> person_ct = ContentType.objects.get_for_model(person1) # works for instances too.

# now we create a message:

>>> msg = Message.objects.create(sender_content_type=person_ct, sender_id=person1.pk, msg_body='Hey, did any of you move my cheese?')

# and send it to a coupla recipients:

>>> MessageRecipient.objects.create(message=msg, recipient_content_type=person_ct, recipient_id=person2.pk)
>>> MessageRecipient.objects.create(message=msg, recipient_content_type=company_ct, recipient_id=company.pk)
>>> MessageRecipient.objects.count()
2

Как видите, это это гораздо более подробное (сложное?) решение. Я бы, вероятно, не усложнил задачу и выбрал решение Prariedogg, приведенное ниже.

57
ответ дан 26 November 2019 в 20:33
поделиться

Эту проблему можно обойти, упростив схему, включив в нее одну таблицу Client с флагом, указывающим тип клиента, вместо двух отдельных моделей. .

from django.db import models
from django.utils.translation import ugettext_lazy as _

class Client(models.Model):
    PERSON, CORPORATION = range(2)
    CLIENT_TYPES = (
                    (PERSON, _('Person')),
                    (CORPORATION, _('Corporation')),
                   )
    type = models.PositiveIntegerField(choices=CLIENT_TYPES, default=PERSON)
    city = models.CharField(max_length=16)
    first_name = models.CharField(max_length=16, blank=True, null=True)
    last_name = models.CharField(max_length=16, blank=True, null=True)
    corporate_name = models.CharField(max_length=16, blank=True, null=True)
    tax_no = models.PositiveIntegerField(blank=True, null=True)

    def save(self, *args, **kwargs):
        """
        Does some validation ensuring that the person specific fields are
        filled in when self.type == self.PERSON, and corporation specific
        fields are filled in when self.type == self.CORPORATION ...

        """
        # conditional save logic goes here
        super(Client, self).save(*args, **kwargs)

Если вы делаете что-то таким образом, возможно, вам вообще не придется возиться с универсальными внешними ключами. В качестве дополнительного удобства вы также можете написать собственные менеджеры для модели клиента, например Client.corporate.all () , Client.person.all () , чтобы возвращать предварительно отфильтрованные наборы запросов, содержащие только тот тип клиентов, который вам нужен.

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

5
ответ дан 26 November 2019 в 20:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: