cazgp
cazgp

Reputation: 1558

Custom model field rendering in Django template

I have extended django-markdown with a custom model field which allows one to define classes:

from django.db import models
from django_markdown.fields import MarkdownField

class MyModel(models.Model):
    text = MarkdownField()

class MySecondModel(models.Model):
    description = MarkdownField()

Now, when it comes to rendering those fields in the template it is possible to do:

{% load markdown_tags %}
{{ model.text|markdown }} {{ model2.description|markdown }}

However, that seems to defeat the object of creating a custom model field in the first place (to enhance DRYness), and is preferably to be avoided at all costs.

So, is there a way to do just {{ model.text }} {{ model2.description }} without loading the template tags and without filtering by somehow defining a render method on the custom field?

A similar question has already been asked: Is there a way to customize how the value for a custom Model Field is displayed in a template?, but the answer entails adding a method to the Model. That would mean adding methods to MyModel and MySecondModel, as well as any subsequent ones. Again this defeats the entire object of DRY!

NB Both model classes are subclasses of something else, so defining a mixin is feasible, but there must surely be a nicer way!

Upvotes: 5

Views: 1447

Answers (1)

Håken Lid
Håken Lid

Reputation: 23064

I'm in the same situation as you. I figured that I could solve it using a mixin to the Model class.

It seems to work as intended, but I'm not really sure if it's the right way to do it. Feels like a dirty hack that I don't fully understand.

You should of course replace the method _shout() with something more useful.

from django.utils.safestring import mark_safe
from django.db import models


class LoudModelMixin(object):

    """ adds the 'html' property to a Model
    Lets regular django models be louder!

    Regular field:
    >>> blogpost.title
    'hello world'

    Same field, but louder.
    >>> blogpost.html.title
    '<strong>HELLO WORLD!</strong>'
    """

    @property
    def html(self):
        return self._HTML(self)

    class _HTML(object):

        def __init__(self, parent):
            self.parent = parent

        def __getattr__(self, attr, *args):
            raw_text = getattr(self.parent, attr, *args)
            assert isinstance(raw_text, str), 'only words can be loud.'
            return mark_safe(self._shout(raw_text))

        def _shout(self, raw, tag='strong'):
            """ Do something useful with the text here. """
            return '<{tag}>{text}!</{tag}>'.format(
                tag=tag, text=raw.upper()
            )


class Blogpost(models.Model, LoudModelMixin):
    title = models.CharField(max_length=50)
    body = models.TextField()

Upvotes: 1

Related Questions