paubins
paubins

Reputation: 151

Add attribute to custom fields in Django

I'll be as brief as possible.

I want to able to do this

{{ video.youtube_url.video_id }}

by implementing something like the following custom field:

class YouTubeURLField(URLField):
    description = _("YouTubeURL")

    def _video_id(self):
      return re.search('(?<=\?v\=)[\w-]+', self.value)
    video_id = property(_video_id)

    def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
      super(YouTubeURLField, self).__init__(**kwargs)
      kwargs['max_length'] = kwargs.get('max_length', 200)
      CharField.__init__(self, verbose_name, name, **kwargs)
      self.validators.append(YouTubeURLValidator(verify_exists=verify_exists))

This:

    def _video_id(self):
      return re.search('(?<=\?v\=)[\w-]+', self.value)
    video_id = property(_video_id)

Does not sucessfully add a "video_id" attribute to my custom YouTubeURLField.

Everything else works flawlessly.

I understand there maybe better design considerations in terms of the YouTube custom field, but I'd rather just understand, first, why this doesn't work.

Upvotes: 1

Views: 1652

Answers (3)

paubins
paubins

Reputation: 151

I wanted to do it this way, because it seems it makes more sense from a design stand point. The video id is an attribute of the YouTube URL and not of the model itself.

I figured it out. I overrode the to_python function to return a YouTubeURL object.

class YouTubeURL(object):
  def __init__(self, value):
    self.value = value

  @property
  def video_id(self):
    regex = re.compile(r'/v/([A-Za-z0-9\-_]+)', re.IGNORECASE)

    id = regex.search(self.value)
    return id.group(1)

  def __unicode__(self):
    return "%s" % (self.value,)

  def __str__(self):
    return "%s" % (self.value,)

  def __len__(self):
    return len(self.value)

class YouTubeURLField(URLField):
    description = _("YouTubeURL")

    __metaclass__ = SubfieldBase

    def to_python(self, value):
      return YouTubeURL(value)

    def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
      super(YouTubeURLField, self).__init__(**kwargs)
      kwargs['max_length'] = kwargs.get('max_length', 200)
      CharField.__init__(self, verbose_name, name, **kwargs)
      self.validators.append(YouTubeURLValidator(verify_exists=verify_exists))

Upvotes: -1

David Miller
David Miller

Reputation: 2219

Is there a reason you can't have it as a property of the model?

In order to access data from an object not directly contained within the fields I frequently implement a pattern along the lines of:

class: Sheep(models.Model):
    name = models.CharField(max_length=200)

    @property
    def sheep_says(self):
        return "Baa... my name is %s ... baa" % self.name

Which you would then access in the template with:

{{ sheep.sheep_says }}

Upvotes: 0

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 799110

Django fields are descriptors, which means that accessing them does not return the field, but rather the field value. You will need to override the Django field methods in order to return an object that has the attributes you care about, as well as a sanely-defined __unicode__() method.

Upvotes: 2

Related Questions