Django ModelAdmin 使用自定义查询
Django ModelAdmin use custom query
您好,目前我在不使用 models.py
中的任何已定义模型的情况下向应用管理端添加自定义 ModelAdmin 部分时遇到问题
例如,我有 3 个模型(充值、取款、转账),我想添加一个单独的 ModelAdmin 事务部分,它是这 3 个模型的组合,因为我喜欢它的分页、更改列表和详细信息视图。
我的模特:
#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)
它 return 3 个表的 UNION 和像这样的列:
{
"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 使用此查询?我还没有找到任何仅使用原始查询而不是模型的管理部分的解决方案
为了在 ModelAdmin
中获得最佳体验(过滤、正确的类型检测)需要 Model
分配。
使用必填字段创建 Model
。告诉 Django 不要管理模型 - 不要为它创建 db table - 所以它会假设 db table 因为它已经存在。
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
现在,您可以创建 database view - 虚构 table 在数据库中仅在访问时生成 - 自定义 SELECT
.
的快捷方式
您可以创建 django 数据库迁移并在其中创建视图:
...
migrations.RunSQL(
"""
CREATE VIEW myapp_transaction_view AS
SELECT * FROM .....; /* your UNION SELECT */
""",
"""
DROP VIEW IF EXISTS myapp_transaction_view;
""",
)
...
现在,Transaction
模型自动链接到此视图和 select 来自它的 运行 自定义联合 select。这个模型可以像往常一样传递给 ModelAdmin
。
或者,您可以避免在数据库中创建视图 - 相反,在 ModelAdmin
上重新定义 get_queryset()
方法并在其中提供查询 - 这样它可能更可定制,或者您可以使用 Django ORM构建查询。
进一步扩展 - 自定义 sql 可以放置在自定义模型管理器中,作为 sql / 查询集比 ModelAdmin 更合适的位置。
您好,目前我在不使用 models.py
中的任何已定义模型的情况下向应用管理端添加自定义 ModelAdmin 部分时遇到问题例如,我有 3 个模型(充值、取款、转账),我想添加一个单独的 ModelAdmin 事务部分,它是这 3 个模型的组合,因为我喜欢它的分页、更改列表和详细信息视图。
我的模特:
#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)
它 return 3 个表的 UNION 和像这样的列:
{
"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 使用此查询?我还没有找到任何仅使用原始查询而不是模型的管理部分的解决方案
为了在 ModelAdmin
中获得最佳体验(过滤、正确的类型检测)需要 Model
分配。
使用必填字段创建 Model
。告诉 Django 不要管理模型 - 不要为它创建 db table - 所以它会假设 db table 因为它已经存在。
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
现在,您可以创建 database view - 虚构 table 在数据库中仅在访问时生成 - 自定义 SELECT
.
您可以创建 django 数据库迁移并在其中创建视图:
...
migrations.RunSQL(
"""
CREATE VIEW myapp_transaction_view AS
SELECT * FROM .....; /* your UNION SELECT */
""",
"""
DROP VIEW IF EXISTS myapp_transaction_view;
""",
)
...
现在,Transaction
模型自动链接到此视图和 select 来自它的 运行 自定义联合 select。这个模型可以像往常一样传递给 ModelAdmin
。
或者,您可以避免在数据库中创建视图 - 相反,在 ModelAdmin
上重新定义 get_queryset()
方法并在其中提供查询 - 这样它可能更可定制,或者您可以使用 Django ORM构建查询。
进一步扩展 - 自定义 sql 可以放置在自定义模型管理器中,作为 sql / 查询集比 ModelAdmin 更合适的位置。