在Django模型中重新排序字段
我想在我的Django应用程序中的每个模型里添加几个字段。这些字段是created_at
(创建时间)、updated_at
(更新时间)和notes
(备注)。对于20多个模型来说,重复写这些代码感觉很傻。所以,我决定使用一个抽象基类来添加这些字段。不过,问题是从抽象基类继承的字段在管理界面的字段列表中会排在最前面。对于每个ModelAdmin类声明字段顺序并不是一个好办法,这样做的重复代码比手动声明字段还要多。
在我的最终解决方案中,我修改了模型的构造函数,在创建新实例之前重新排列了_meta中的字段顺序:
class MyModel(models.Model):
# Service fields
notes = my_fields.NotesField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
last_fields = ("notes", "created_at", "updated_at")
def __init__(self, *args, **kwargs):
new_order = [f.name for f in self._meta.fields]
for field in self.last_fields:
new_order.remove(field)
new_order.append(field)
self._meta._field_name_cache.sort(key=lambda x: new_order.index(x.name))
super(MyModel, self).__init__(*args, **kwargs)
class ModelA(MyModel):
field1 = models.CharField()
field2 = models.CharField()
#etc ...
这样做效果不错,但我在想,是否有更好的方法来实现我的目标呢?
3 个回答
我也不太喜欢其他的解决方案,所以我直接修改了迁移文件。
每当你在models.py里创建一个新表时,你需要运行“python manage.py makemigrations”(我记得这是在Django版本大于等于1.7.5时的做法)。完成后,打开在your_app_path/migrations/目录下新创建的迁移文件,然后简单地调整里面的行顺序,按照你想要的顺序排列。接着运行“python manage.py migrate”。太好了!通过进入“python manage.py dbshell”,你可以看到列的顺序正是你想要的样子!
这种方法的缺点是:你必须为每个创建的表手动操作,不过幸运的是,这样的工作量不大。而且这种方法只能在你创建新表的时候使用,不能用来修改已有的表。
我之前也遇到过同样的问题,不过我觉得这些解决办法不太好用,所以我采取了以下做法:
class BaseAdmin(admin.ModelAdmin):
def get_fieldsets(self, request, obj = None):
res = super(BaseAdmin, self).get_fieldsets(request, obj)
# I only need to move one field; change the following
# line to account for more.
res[0][1]['fields'].append(res[0][1]['fields'].pop(0))
return res
在管理后台修改字段组对我来说更合理,而不是在模型里改字段。
如果你主要是为了在Django的后台管理中调整显示顺序,你可以通过继承Django的管理类来创建一个“通用”的管理类。你可以查看这个链接了解如何自定义后台字段的显示:http://docs.djangoproject.com/en/dev/intro/tutorial02/#customize-the-admin-form。你可以重写管理类的 __init__
方法,这样在创建管理实例的时候就可以按照你的需求设置字段或字段组。例如,你可以这样做:
class MyAdmin(admin.ModelAdmin):
def __init__(self, model, admin_site):
general_fields = ['notes', 'created_at', 'updated_at']
fields = [f.name for f in self.model._meta.fields if f.name not in general_fields]
self.fields = fields + general_fields
super(admin.ModelAdmin, self).__init__(model, admin_site)
另外,我觉得修改(私有的) _field_name_cache
不是一个好习惯!