Python中如何精简处理不同Model对象的相似操作?

现在的项目中遇到一个问题,项目使用 flask 完成的,现在有几个数据表,有同一个 order 字段,并且都需要对这几个表的数据进行查询和对 order 字段的值进行调整。

低效率的方法是,为这几个表的 Model 类分别实现操作函数,但是这样写出来的代码,几个函数之间只有几个关键字是不一样的,感觉代码很冗余

所以请教大佬们,有什么精简的实现方法呢?


Python中如何精简处理不同Model对象的相似操作?
5 回复

你把伪代码贴出来也好分析呀,空对空怎么讲呢


核心思路:用基类封装通用逻辑,用继承避免重复代码。

直接上代码示例,假设你有多个Django/DRF的Model类都需要类似的操作:

from django.db import models

# 1. 创建抽象基类
class BaseModelManager(models.Manager):
    """统一的管理器扩展"""
    def active(self):
        return self.filter(is_active=True)
    
    def by_user(self, user_id):
        return self.filter(created_by_id=user_id)

class TimestampMixin(models.Model):
    """时间戳混入类"""
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        abstract = True

class AuditMixin(models.Model):
    """审计信息混入类"""
    created_by = models.ForeignKey('User', on_delete=models.SET_NULL, null=True, related_name='+')
    updated_by = models.ForeignKey('User', on_delete=models.SET_NULL, null=True, related_name='+')
    
    class Meta:
        abstract = True

# 2. 具体Model继承这些混入类
class Product(TimestampMixin, AuditMixin):
    name = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    is_active = models.BooleanField(default=True)
    
    objects = BaseModelManager()  # 使用统一的管理器
    
    def get_display_info(self):
        """统一的显示信息方法"""
        return f"{self.name} - ${self.price}"

class Order(TimestampMixin, AuditMixin):
    order_number = models.CharField(max_length=50)
    total_amount = models.DecimalField(max_digits=10, decimal_places=2)
    is_active = models.BooleanField(default=True)
    
    objects = BaseModelManager()  # 同样的管理器
    
    def get_display_info(self):
        """统一的显示信息方法"""
        return f"Order #{self.order_number} - ${self.total_amount}"

# 3. 使用示例
# 所有Model都有相同的时间戳字段
product = Product.objects.create(name="Laptop", price=999.99, created_by=user)
print(product.created_at)  # 自动填充
print(product.updated_at)  # 自动更新

# 所有Model都有相同的查询方法
active_products = Product.objects.active()
user_orders = Order.objects.by_user(user_id=1)

# 所有Model都有统一的接口
for obj in [product, order]:
    print(obj.get_display_info())  # 多态调用

# 4. 如果需要更灵活的操作,可以用函数+反射
def bulk_update_status(model_class, ids, status):
    """批量更新状态 - 通用函数"""
    if hasattr(model_class, 'is_active'):
        model_class.objects.filter(id__in=ids).update(is_active=status)
        return f"Updated {len(ids)} {model_class.__name__} records"
    return "Model does not have is_active field"

# 使用通用函数
result = bulk_update_status(Product, [1, 2, 3], False)
print(result)

关键点:

  1. 抽象基类(Abstract Base Classes):把共同的字段和方法提到父类,用abstract=True避免创建单独的表
  2. 混入类(Mixins):把功能模块化,可以灵活组合
  3. 统一管理器:自定义Manager给所有Model提供相同的查询方法
  4. 多态设计:让不同Model实现相同的方法名,用统一接口调用
  5. 通用函数:对于跨Model的操作,写接收Model类作为参数的函数

总结:用继承和混入把重复代码提到父类,用多态保持接口一致。

补充一下描述,

python<br>def change_docorder():<br> reqdata = request.json<br> op = reqdata["op"]<br> docid = reqdata["docid"]<br><br> # 如果是上移文档顺序<br> if op == "up":<br> another = DocItem.query.filter(and_(DocItem.category_id == docitem.category_id,<br> DocItem.order &lt; docitem.order)).order_by(DocItem.order.desc()).first()<br> if another is None:<br> return error_response(u"无法继续移动该文档顺序!")<br> tmp = another.order<br> another.order = docitem.order<br> docitem.order = tmp<br> another.save()<br> docitem.save()<br> elif op == "down":<br> another = DocItem.query.filter(and_(DocItem.category_id == docitem.category_id,<br> DocItem.order&gt;docitem.order)).order_by(DocItem.order.asc()).first()<br> if another is None:<br> return error_response(u"无法继续移动该文档顺序!")<br> tmp = another.order<br> another.order = docitem.order<br> docitem.order = tmp<br> another.save()<br> docitem.save()<br> else:<br> return make_response(u"非法的操作!")<br> return make_response(json.dumps({<br> "status": "success",<br> "msg": u"修改文档顺序成功!"<br> }))<br>

这是针对 DocItem 这个 model 进行的调整顺序操作,还有其他的几个 model 对象需要进行相同的操作,如何如果都分别写一个函数来实现功能,最终代码只有类名 DocItem 是不一样的。

以上是对问题的补充描述。

这个你可以写一个闭包方法,把不同的参数传进去就可以,

v2ex 贴代码太不方便了,我把问题移至 segmentfault 了, https://segmentfault.com/q/1010000014261968

回到顶部