Reputation: 76766
I have a django model like this:
class Something(models.Model):
title = models.CharField(max_length=200, default=u'')
text = models.CharField(max_length=250, default=u'', blank=True)
photo = models.ImageField(upload_to=u'something')
def photo_thumb(self):
if self.photo:
return u'<img src="%s" />' % (settings.MEDIA_URL + '/thumbs/?h=64&w=80&c=50x0&p=' + self.photo.name)
else:
return u'(no photo)'
photo_thumb.short_description = u'Photo'
photo_thumb.allow_tags = True
photo_thumb.admin_order_field = 'photo'
def __unicode__(self):
return self.title;
class SomethingElse(models.Model):
name = models.CharField(max_length=200, default=u'')
foo = models.CharField(max_length=250, default=u'', blank=True)
photo = models.ImageField(upload_to=u'something_else')
def photo_thumb(self):
if self.photo:
return u'<img src="%s" />' % (settings.MEDIA_URL + '/thumbs/?h=64&w=80&c=50x0&p=' + self.photo.name)
else:
return u'(no photo)'
photo_thumb.short_description = u'Photo'
photo_thumb.allow_tags = True
photo_thumb.admin_order_field = 'photo'
def __unicode__(self):
return self.title;
I feel like this violates DRY, for obvious reasons. My question is, can I stick this somewhere else:
# ...
def photo_thumb(self):
if self.photo:
return u'<img src="%s" />' % (settings.MEDIA_URL + '/thumbs/?h=64&w=80&c=50x0&p=' + self.photo.name)
else:
return u'(no photo)'
photo_thumb.short_description = u'Photo'
photo_thumb.allow_tags = True
photo_thumb.admin_order_field = 'photo'
# ...
And then include it in relevant model classes with a single line of code? Or can photo_thumb be dynamically added to the appropriate classes somehow? I've tried classical and parasitic inheritance, but I may not be doing it right... I'm new to Django and fairly new to python also. Any help is appreciated.
Upvotes: 1
Views: 243
Reputation: 33225
Another solution may be to create a subclass of ImageField
and override the contribute_to_class
method:
class ImageWithThumbnailField(ImageField):
def contribute_to_class(self, cls, name):
super(ImageWithThumbnailField, self).contribute_to_class(cls, name)
def photo_thumb(self):
photo = getattr(self, name, None)
if photo:
return u'<img src="%s" />' % (settings.MEDIA_URL + '/thumbs/?h=64&w=80&c=50x0&p=' + photo.name)
else:
return u'(no photo)'
photo_thumb.short_description = u'Photo'
photo_thumb.allow_tags = True
photo_thumb.admin_order_field = 'photo'
setattr(cls, 'photo_thumb', photo_thumb);
I think this is better because on calling the photo_thumb
method you are expecting the existence of self.photo
which is not guaranteed if you are using the other solution that use an abstract model.
EDIT: Note that you can use getattr(self, name)
to dynamically access the field. So yes, it is guaranteed that we have some photo field.
Upvotes: 1
Reputation: 33680
I agree with @Gintautas. The general rule of thumb is to create an abstract model class if you need to reuse model fields and meta options; use a simple class if you only need to reuse other properties and methods.
In your case I'd go with the abstract class (because of the photo
model field):
class PhotoModels(models.Model):
photo = models.ImageField(upload_to=u'something')
def photo_thumb(self):
if self.photo:
return u'<img src="%s" />' % (settings.MEDIA_URL +
'/thumbs/?h=64&w=80&c=50x0&p=' + self.photo.name)
else:
return u'(no photo)'
photo_thumb.short_description = u'Photo'
photo_thumb.allow_tags = True
photo_thumb.admin_order_field = 'photo'
class meta:
abstract = True
class Something(PhotoModels):
title = models.CharField(max_length=200, default=u'')
text = models.CharField(max_length=250, default=u'', blank=True)
class SomethingElse(PhotoModels):
name = models.CharField(max_length=200, default=u'')
foo = models.CharField(max_length=250, default=u'', blank=True)
photo.upload_to = u'something_else'
def __unicode__(self):
return self.title;
... although this would be legal just as well:
class PhotoModels:
def photo_thumb(self):
if self.photo:
return u'<img src="%s" />' % (settings.MEDIA_URL +
'/thumbs/?h=64&w=80&c=50x0&p=' + self.photo.name)
else:
return u'(no photo)'
photo_thumb.short_description = u'Photo'
photo_thumb.allow_tags = True
photo_thumb.admin_order_field = 'photo'
class Something(models.Model, PhotoModels):
title = models.CharField(max_length=200, default=u'')
text = models.CharField(max_length=250, default=u'', blank=True)
photo = models.ImageField(upload_to=u'something')
class SomethingElse(models.Model, PhotoModels):
name = models.CharField(max_length=200, default=u'')
foo = models.CharField(max_length=250, default=u'', blank=True)
photo = models.ImageField(upload_to=u'something_else')
def __unicode__(self):
return self.title;
Upvotes: 3
Reputation: 7892
Sure you can reuse the code. Just factor it out into a base class, and make both your classes inherit from that base class. That should work just fine. Just don't forget that the base class either needs to inherit from models.Model itself (then I would suggest making it abstract), or you can put the reusable code in a mixin; that means that your both classes will be inheriting from both models.Model and the new mixin base class.
Upvotes: 1
Reputation: 76766
Maybe I asked too soon... I think abstract base classes may be the answer.
http://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes
I'll check it out and confirm.
Upvotes: 0