Я думаю, что-то подобное возможно, но сделать его универсальным (разрешить полный доступ только для чтения к ORM) было бы очень трудно сделать безопасным способом.
Вот несколько идей:
Ограничить действия заранее заданным набором явно помеченных методов в пользовательском классе менеджера.Например:
from django.db import models
class MarkupAccessManager(models.Manager):
def count(self):
return super(MarkupAccessManager, self).count()
count.expose_to_markup = True
class AfricanSwallow(models.Model):
objects = MarkupAccessManager()
Чтобы обратиться к моделям из разметки, вы можете воспользоваться структурой django.contrib.contenttypes
, и теги могут иметь следующий формат: app_label.model_name action
или app_label.model_name action arg1 arg2
.
В зависимости от языка разметки, который вы выбираете, вы можете использовать пользовательские теги (если они есть в языке), теги шаблонов Django или обычные регулярные выражения.Как только вы получите содержимое тега, вы можете заменить его выводом указанного метода:
from django.contrib.contenttypes.models import ContentType
def replace_tag(tag):
"""
'birds.africanswallow count' => birds.models.AfricanSwallow.objects.count()
"""
bits = tag.split()
model_ref = bits[0]
action = bits[1]
args = bits[2:]
try:
ct = ContentType.objects.get_by_natural_key(*model_ref.split('.'))
except ContentType.DoesNotExist:
return 'Invalid model reference.'
model = ct.model_class()
method = getattr(model._base_manager, action, None)
if not method or not method.expose_to_markup:
return 'Invalid action.'
return method(*args)
Чтобы обеспечить автозаполнение, что-то в этом духе поможет вам составить список всехдоступные опции:
from django.db.models.loading import get_models
from django.contrib.contenttypes.models import ContentType
def model_refs():
for model in get_models():
if isinstance(model._base_manager, MarkupAccessManager):
ct = ContentType.objects.get_for_model(model)
yield '%s.%s' % (ct.app_label, ct.model)
def actions():
for attr_name in dir(MarkupAccessManager):
attr = getattr(MarkupAccessManager, attr_name)
if attr.expose_to_markup:
yield attr.__name__
Я не проверял код.Надеюсь, это немного поможет.