【问题标题】:Resuable model members in djangodjango 中可重用的模态成员
【发布时间】:2010-08-12 23:29:57
【问题描述】:

我有一个这样的 django 模型:

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;

我觉得这违反了 DRY,原因很明显。我的问题是,我可以把它贴在别的地方吗:

    # ...
    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' 
    # ...

然后用一行代码将它包含在相关的模型类中?或者可以以某种方式将 photo_thumb 动态添加到适当的类中?我尝试过经典继承和寄生继承,但我可能做得不对……我是 Django 新手,对 python 也很陌生。任何帮助表示赞赏。

【问题讨论】:

    标签: python django inheritance django-models dry


    【解决方案1】:

    我同意@Gintautas。如果需要重用模型字段和元选项,一般的经验法则是创建一个抽象模型类;如果您只需要重用其他属性和方法,请使用简单的类。

    在你的情况下,我会选择抽象类(因为 photo 模型字段):

    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;
    

    ...虽然这也是合法的:

    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;
    

    【讨论】:

    • 您的第二个示例似乎运行良好,这让我想知道 Django 抽象类的原因是什么……如果正常继承有效,似乎没有必要。我认为我之前尝试这个时犯的错误是用我的基类扩展 model.Model 而不是什么都不扩展。
    • 我认为答案在于 Django 模型元类的血腥细节。我很确定,因为 _meta 框架(请记住模型字段在类初始化期间如何更改)、继承约束和元选项需要将 Models 作为基类一切都可以正确歌唱——在这种情况下,如果它不是抽象的,则会创建一个数据库模式定义。我自己从未尝试过,但我敢打赌,如果你从一个模型字段没有 Model 基数的类继承,应该会发生一些奇怪的事情。
    • 确实发生了一些奇怪的事情......我刚刚发现了这一点。方法似乎可以正常工作,但属性 model.* 属性不行。
    【解决方案2】:

    另一种解决方案可能是创建ImageField 的子类并覆盖contribute_to_class 方法:

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

    我认为这更好,因为在调用 photo_thumb 方法时,您期望存在 self.photo,如果您使用的是使用抽象模型的其他解决方案,则无法保证。

    编辑:请注意,您可以使用getattr(self, name) 动态访问该字段。所以是的,可以保证我们有一些 photo 字段。

    【讨论】:

    • 但是不能保证ImageField 也有photo 属性,是吗?不过,这是一个有趣的解决方案,谢谢。
    • @no,你可以使用getattr来获取你声明的字段,你不必在任何地方都将字段命名为photo
    • 这会将缩略图方法添加到照片字段,而不是模型类本身,对吗?该方法是否不需要属于模型类才能显示在管理列表中?
    • @no,不,它不会被添加到照片字段本身。在调用contribute_to_class 的上下文中,cls 指的是模型类,因此该方法将被添加到模型类中。详细请看这篇文章或者直接看django.db.models.base.ModelBase.add_to_class的源码:lazypython.blogspot.com/2008/11/…
    【解决方案3】:

    当然可以重复使用代码。只需将其分解为基类,并使您的两个类都从该基类继承。那应该工作得很好。只是不要忘记基类要么需要从models.Model本身继承(然后我建议making it abstract),要么您可以将可重用代码放在mixin中;这意味着您的两个类都将继承自两个模型。模型新的 mixin 基类。

    【讨论】:

      【解决方案4】:

      也许我问得太早了……我认为抽象基类可能是答案。

      http://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes

      我会检查并确认。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-16
        • 1970-01-01
        • 1970-01-01
        • 2011-01-10
        • 2011-01-13
        • 1970-01-01
        相关资源
        最近更新 更多