Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linking to and getting data from django models in user-entered text

I'm looking for functionality vaguely like that provided by Semantic MediaWiki. In short, I'd like for a user, in an arbitrary text field, to be able to do things like the following (I'm making up the markup as I go).

*Hi, everyone, don't forget that we have [[::AfricanSwallow.count]] African Swallows in our land.

*Did you know that Harry the European Swallow has carried [[::EuropeanSwallow.get(name="harry").coconuts.count]] coconuts back with him?

In addition to these kinds of features, I'd like to be able to autocomplete inline - perhaps when the user starts typing.

I can do all of these things, but I'm hoping that some or all of them have been done. Any idea if that's the case?

like image 547
jMyles Avatar asked Apr 07 '11 14:04

jMyles


People also ask

How do I link models and forms in Django?

models import User class InputUserInfo(forms. Form): phone = forms. CharField(max_length=20) instagram = forms. CharField(max_length=20) facebook = forms.

What is __ str __ in Django?

The __str__ method in Python represents the class objects as a string – it can be used for classes. The __str__ method should be defined in a way that is easy to read and outputs all the members of the class. This method is also used as a debugging tool when the members of a class need to be checked.


1 Answers

I think something like this is feasible but making it universal (allowing full read-only access to the ORM) would be very difficult to make in a secure way.

Here are some ideas:

Limit the actions to a predefined set of explicitly marked methods on a custom manager class. For example:

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()

To refer to models from the markup, you could take advantage of the django.contrib.contenttypes framework and the tags could have the following format: app_label.model_name action or app_label.model_name action arg1 arg2.

Depending on the markup language you choose, you could either use custom tags (if the language provides them), Django template tags, or plain regular expressions. Once you get the contents of a tag, this is how you could replace it with the output of the referred method:

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)

To provide autocomplete, something along these lines would help you to build a list of all the available options:

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__

I haven't tested the code. Hope this helps a bit.

like image 98
Jakub Roztocil Avatar answered Sep 21 '22 09:09

Jakub Roztocil