thornomad
thornomad

Reputation: 6777

Pythonic Way to set Multiple Properties Based on another class

Is there an elegant way to handle all get requests of a class when I just want to pass the get to another object?

This is a Django project where I have a Video model; the Video can be from different sites (e.g., YouTube, Vimeo, etc). The model is generic and will grab the appropriate backend to parse the data (which will be repr differently depending on the site). All Videos will have the same properties but I have a different backend to parse them correctly. Here is the example:

BACKEND_VIDEO_TYPE = (
    (1, 'YouTube'),
    (2, 'Vimeo'),
)

class Video(ItemBase):
    video_id = models.CharField(max_length=25)
    video_type = models.PositiveIntegerField(choices=BACKEND_VIDEO_TYPE)
    video_data = models.TextField(editable=False)

    class Meta:
        unique_together = ("video_id", "video_type")

    def __init__(self, *args, **kwargs):
        super(Video, self).__init__(*args, **kwargs)

        for k, v in BACKEND_VIDEO_TYPE:
            if k == self.video_type:
                VideoBackend = import_string( 'video.backends.' + v )
                # load the backend that matches the video type and supply it with the data to parse
                self.backend = VideoBackend(self.video_id, self.video_data)

    @property
    def link(self):
        return self.backend.link

    @property
    def thumbnail(self):
        return self.backend.thumbnail

    @property
    def is_public(self):
        return self.backend.is_public()

    @property
    def duration(self):
        return self.backend.duration

I don't feel like this is the best way to handle this but I am too new to know what to look for. AFter I identify which backend class to implement, is there an easy way to return self.backend.property without having to add each property that is in the backend? The list is going to get rather long. A generic __get__ or similar?

Thanks for your help.

Upvotes: 0

Views: 89

Answers (1)

101
101

Reputation: 8999

You could override __getattr__ on the Video class so that it fetches an attribute of backend.

def __getattr__(self, attr):
    try:
        return getattr(self.backend, attr)
    except:
        raise AttributeError('No attribute named ' + attr)

__getattr__ will only call if the attribute is not found on the Video object (e.g. it wouldn't call at all if the user asked for Video.video_id) so it's a neat way to extend the exposed attributes of the Video class. The downside is that it may not be obvious which attributes are available, or it may expose some attributes you don't wish to. You could get around this by having the VideoBackend class provide a list of allowed attributes.

def __getattr__(self, attr):
    if attr in self.backend.get_allowed_attr():
        return getattr(self.backend, attr)
    else:
        raise AttributeError('No attribute named ' + attr)

Upvotes: 1

Related Questions