Django ModelAdmin использует пользовательский запрос - PullRequest
0 голосов
/ 17 марта 2020

Привет, в настоящее время у меня проблемы с добавлением пользовательского раздела ModelAdmin на стороне администратора приложения без использования какой-либо определенной модели в models.py

Например, у меня есть 3 модели (пополнение счета, снятие средств, переводы) и Я хотел бы добавить отдельный раздел транзакций ModelAdmin, который представляет собой комбинацию из этих трех моделей, потому что мне нравится это подкачка страниц, список изменений и подробный просмотр.

Мои модели:

#TOPUP
class TopUp(SafeDeleteModel):
    class Meta:
        db_table = "topups"
        verbose_name = 'TopUp Request'
        verbose_name_plural = 'TopUp Requests'

    user = models.ForeignKey("backend.User", null=True, blank=True, related_name='user_toptup', on_delete=models.CASCADE)
    currency = models.ForeignKey("backend.Currency", null=True, blank=True, related_name='user_topup_currency', on_delete=models.SET_NULL)

    TOPUP_METHOD_CHOICES = [
        (1, 'method 1'),
        (2, 'method 2')
    ]
    method = models.PositiveSmallIntegerField("Method", choices=TOPUP_METHOD_CHOICES)

    amount = models.DecimalField("Amount", max_digits=65, decimal_places=0, default=0)
    fee = models.DecimalField("Fee", max_digits=65, decimal_places=0, default=0)

    TOPUP_STATUS_CHOICES = [
        (0, 'Pending'),
        (1, 'Success'),
        (2, 'Failed'),
    ]
    status = models.PositiveSmallIntegerField("Status", choices=TOPUP_STATUS_CHOICES, default=0)
    created = models.DateTimeField(auto_now_add=True)
    received = models.DateTimeField(null=True, blank=True)

# WITHDRAWALS
class Withdrawals(SafeDeleteModel):
    class Meta:
        db_table = "withdrawals"
        verbose_name = 'Withdraw Request'
        verbose_name_plural = 'Withdraw Requests'

    user = models.ForeignKey("backend.User", null=True, blank=True, related_name='user_withdrawal', on_delete=models.CASCADE)
    currency = models.ForeignKey("backend.Currency", null=True, blank=True, related_name='user_withdrawal_currency', on_delete=models.SET_NULL)

    WITHDRAWAL_METHOD_CHOICES = [
        (1, 'method 1'),
        (2, 'method 2')
    ]
    method = models.PositiveSmallIntegerField("Method", choices=WITHDRAWAL_METHOD_CHOICES)
    to_bank = models.ForeignKey("backend.UserBank", null=True, blank=True, related_name='user_withdrawal_userbank', on_delete=models.SET_NULL, db_column='to_bank')
    to_address = models.CharField("To address", max_length=255, null=True, blank=True, db_column='to_address')
    to_card = models.ForeignKey("backend.CardBinding", null=True, blank=True, related_name='user_withdrawal_to_card', on_delete=models.SET_NULL, db_column='to_card')
    amount = models.DecimalField("Amount", max_digits=65, decimal_places=0, default=0)
    fee = models.DecimalField("Fee", max_digits=65, decimal_places=0, default=0)

    WITHDRAWAL_STATUS_CHOICES = [
        (0, 'Pending'),
        (1, 'success'),
        (2, 'failed')
    ]
    status = models.PositiveSmallIntegerField("Status", choices=WITHDRAWAL_STATUS_CHOICES, default=0)
    created = models.DateTimeField(auto_now_add=True)
    submitted = models.DateTimeField(null=True, blank=True)
    confirmed = models.DateTimeField(null=True, blank=True)

# TRANSFERS

class Transfers(SafeDeleteModel):
    class Meta:
        db_table = "transfers"
        verbose_name = 'Transfer'
        verbose_name_plural = 'Transfers'

    user = models.ForeignKey("backend.User", null=True, blank=True, related_name='user_transfer', on_delete=models.CASCADE)
    currency = models.ForeignKey("backend.Currency", null=True, blank=True, related_name='user_transfer_currency', on_delete=models.SET_NULL)

    TRANSFER_METHOD_CHOICES = [
        (2, 'method 1'),
        (3, 'method 2')
    ]
    method = models.PositiveSmallIntegerField("Method", choices=TRANSFER_METHOD_CHOICES)

    to_address = models.CharField("To Address", max_length=255, null=True, blank=True, db_column='to_address')
    to_account = models.ForeignKey("backend.User", null=True, blank=True, related_name='user_transfer_to_account', on_delete=models.SET_NULL, db_column='to_account')
    amount = models.DecimalField("Amount", max_digits=65, decimal_places=0, default=0)
    fee = models.DecimalField("Fee", max_digits=65, decimal_places=0, default=0)

    TRANSFER_STATUS_CHOICES = [
        (0, 'Pending'),
        (1, 'Success'),
        (2, 'Failed')
    ]
    status = models.PositiveSmallIntegerField("Status", choices=TRANSFER_STATUS_CHOICES, default=0)
    created = models.DateTimeField(auto_now_add=True)
    submitted = models.DateTimeField(null=True, blank=True)
    confirmed = models.DateTimeField(null=True, blank=True)

Так что если у меня есть запрос, например, так:

        cursor = connection.cursor()
        cursor.execute('''
            SELECT * FROM 
                (SELECT id,
                    user_id, 
                    'top up'  AS transaction_type, 
                    method, 
                    NULL     AS to_bank, 
                    NULL     AS to_address, 
                    user_id  AS to_account, 
                    NULL     AS to_card, 
                    currency_id, 
                    amount, 
                    fee,
                    status, 
                    created AS created, 
                    received AS confirmed 
                FROM   topups 
                WHERE deleted IS NULL
                UNION ALL 
                SELECT id,
                    user_id, 
                    'transfer' AS transaction_type, 
                    method, 
                    NULL       AS to_bank, 
                    to_address, 
                    to_account, 
                    NULL       AS to_card, 
                    currency_id, 
                    amount, 
                    fee,
                    status, 
                    created AS created, 
                    confirmed  AS confirmed 
                FROM   transfers 
                WHERE deleted IS NULL
                UNION ALL 
                SELECT id,
                    user_id, 
                    'withdrawal' AS transaction_type, 
                    method, 
                    to_bank, 
                    to_address, 
                    NULL         AS to_account, 
                    to_card, 
                    currency_id, 
                    amount, 
                    fee,
                    status, 
                    created AS created, 
                    confirmed    AS confirmed 
                FROM   withdrawals
                WHERE deleted IS NULL
                ) AS T
            ORDER BY created DESC'''
        )

        row = namedtuplefetchall(cursor)

Он возвращает UNION из 3 таблиц и с такими столбцами:

  {
    "user_id": 120,
    "transaction_type": "transfer",
    "method": 3,
    "to_bank" null,
    "to_card" null,
    "to_address" null,
    "to_account": 170,
    "currency_id": 1,
    "amount": "-10000",
    "fee": "100000000",
    "status": 2,
    "created": 1582272307,
    "confirmed": 1582272307
  },

Как мне сделать ModelAdmin для использования этого запроса? я не нашел решения для административной секции, в котором вместо модели

используется только необработанный запрос

1 Ответ

1 голос
/ 19 марта 2020

Для лучшего опыта (фильтрация, правильное определение типа) в ModelAdmin необходимо Model назначено.

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

class Transaction(models.Model):
    # all fields of the result of UNION
    user = models.ForeignKey(User)
    transaction_type = models.CharField(max_length=128)
    method = models.IntegerField()
    ...

    class Meta:
        managed = False  # django will not create db table
        db_table = "myapp_transaction_view"  # if accessing database view

Теперь вы можете создать представление базы данных - вымышленная таблица в базе данных, сгенерированная только при обращении к ней - ярлык для пользовательского SELECT.

Вы можете создать django миграцию базы данных и создайте в нем представление:

...
    migrations.RunSQL(
        """
        CREATE VIEW myapp_transaction_view AS
            SELECT * FROM .....; /* your UNION SELECT */
        """,
        """
        DROP VIEW IF EXISTS myapp_transaction_view;
        """,
    )
...

Теперь Transaction модель связывается с этим видом и выбирает из него запуск настраиваемого объединения, выбирается автоматически. И эта модель может быть просто передана в ModelAdmin, как обычно.


Или вы можете избежать создания представления в базе данных - вместо этого переопределите метод get_queryset() для ModelAdmin и предоставьте в нем запрос. - таким образом, он может быть более настраиваемым, или вы можете использовать Django ORM для построения запроса.

Более подробно об этом - пользовательский sql можно поместить в диспетчер пользовательских моделей как более подходящее место для sql / queryset чем ModelAdmin.

...