Как добавить связь к сгенерированному полю в Django

Контекст

У меня есть модель:

class Article(models.Model):
    id = models.UUIDField(default=uuid4, editable=False, unique=True, primary_key=True)
    paper_id: UUID | None
    paper = models.ForeignKey["Paper"](
        "Paper",
        on_delete=models.CASCADE,
        related_name="articles",
        null=True,
        blank=True,
    )
    website_id: UUID | None
    website = models.ForeignKey["Website"](
        "Website",
        on_delete=models.CASCADE,
        related_name="articles",
        null=True,
        blank=True,
    )

Тогда у меня есть представление, которое объединяет Paper и Website:

class Source(pg.View):
    sql = """
        SELECT ...
        FROM papers

        UNION ALL

        SELECT ...
        FROM websites
        ;
    """

    id = models.UUIDField(unique=True, primary_key=True)

    # ...

Вопрос

Я бы хотел сделать так, чтобы из модели Source я мог получить модель articles (например, source.articles.all()). Как я могу это сделать?

Что я пробовал?

Я пытался добавить это поле в Article:

    source_id = models.GeneratedField(
        expression=Coalesce(F("paper_id"), F("website_id")),
        output_field=models.UUIDField(),
        db_persist=False,
    )
    source = models.ForeignKey["Source"](
        "Source",
        on_delete=models.DO_NOTHING,
        related_name="articles",
        db_column="source_id",
        to_field="id",
        null=True,
        editable=False,
    )

Но при создании миграции я получаю:

(models.E006) Поле "источник" противоречит полю "source_id" из модели "Article".

Я полагаю, проблема в том, что вы сталкиваетесь с ограничением Django:

Поля ForeignKey автоматически создают атрибут <field>_id на уровне базы данных, поэтому у вас не может быть одновременно поля model с именем source_id и ForeignKey source поле, которое вызывает столкновение.

Как предложил @willeM_VanOnsem, используйте @property в качестве ограниченного обходного пути.

Вернуться на верх