我在models.py文件中使用了以下代码:
创建指向foreignkey的超链接
class ModelAdminWithForeignKeyLinksMetaclass(MediaDefiningClass):
def __getattr__(cls, name):
def foreign_key_link(instance, field):
target = getattr(instance, field)
return u'<a href="../../%s/%s/%s">%s</a>' % (
target._meta.app_label, target._meta.module_name, target.id, unicode(target))
if name[:8] == 'link_to_':
method = partial(foreign_key_link, field=name[8:])
method.__name__ = name[8:]
method.allow_tags = True
setattr(cls, name, method)
return getattr(cls, name)
raise AttributeError
在admin.py list_display中,我已将link_to添加到我想要外键链接的每个字段的开头。这非常有效,但是当我关闭调试时,我得到一个属性错误。有什么建议吗?
答案 0 :(得分:12)
我偶然发现了同样的问题,幸运的是,我已经修好了。
原始解决方案(您使用的解决方案)来自this question,我的解决方案基于它:
class ForeignKeyLinksMetaclass(MediaDefiningClass):
def __new__(cls, name, bases, attrs):
new_class = super(
ForeignKeyLinksMetaclass, cls).__new__(cls, name, bases, attrs)
def foreign_key_link(instance, field):
target = getattr(instance, field)
return u'<a href="../../%s/%s/%d/">%s</a>' % (
target._meta.app_label, target._meta.module_name,
target.id, unicode(target)
)
for name in new_class.list_display:
if name[:8] == 'link_to_':
method = partial(foreign_key_link, field=name[8:])
method.__name__ = name[8:]
method.allow_tags = True
setattr(new_class, name, method)
return new_class
嗯,您唯一需要的是用上面的那个替换原来的 ModelAdminWithForeignKeyLinksMetaclass 。
然而,这不是结束。最有趣的部分是原始解决方案导致问题的原因。这个问题的答案在于here(第31行)和here(第244行)。
当DEBUG开启时,Django会尝试验证所有已注册的ModelAdmins(第一个链接)。 cls 是类 SomeAdmin(即其元类的一个实例)。当调用 hasattr 时,python会尝试在类 SomeAdmin 或其中一个超类中查找属性 field 。由于不可能,因此调用其类的__getattr__(即 SomeAdmin 的元类),其中将新方法添加到类 SomeAdmin 。因此,在渲染界面时, SomeAdmin 已经打补丁,Django能够找到所需的字段(第二个链接)。
当DEBUG为False时,Django会跳过验证。当界面被渲染时,Django试图找到一个字段(再次,第二个链接),但这次SomeAdmin没有打补丁,而且 model_admin 不是类 SomeAdmin ,它是它的的实例即可。因此,尝试在 model_admin 中找到属性 name ,python无法执行此操作,也无法在其类中找到它( SomeAdmin )以及它的任何超类中都会引发异常。
答案 1 :(得分:1)
我使用了stepank的实现,但是必须稍微改变它以适应我的用例。
我基本上只是添加了对'ModelAdmin.readonly_fields'和'ModelAdmin.fields'的支持,以支持'link_to _'-语法。
对链接创建的轻微更改还允许我支持指向其他APP.Model的链接,在我的例子中是内置的django.contrib.auth.models.user。
感谢@stepank和@Itai Tavor的精彩工作。
我希望这对其他人有用。
DEFAULT_LOGGER_NAME在我的settings.py中定义,我将其用于大部分日志记录。如果您没有定义它,则在使用以下代码时将出现错误。您可以在settings.py中定义自己的DEFAULT_LOGGER_NAME(它只是一个简单的字符串),也可以在下面的代码中删除对记录器的所有引用。
'''
Created on Feb 23, 2012
@author: daniel
Purpose: Provides a 'link_to_<foreignKeyModel>' function for ModelAdmin
implementations. This is based on the following work:
original: http://stackoverflow.com/a/3157065/193165
fixed original: http://stackoverflow.com/a/7192721/193165
'''
from functools import partial
from django.forms import MediaDefiningClass
import logging
from public.settings import DEFAULT_LOGGER_NAME
logger = logging.getLogger(DEFAULT_LOGGER_NAME)
class ForeignKeyLinksMetaclass(MediaDefiningClass):
def __new__(cls, name, bases, attrs):
new_class = super(
ForeignKeyLinksMetaclass, cls).__new__(cls, name, bases, attrs)
def foreign_key_link(instance, field):
target = getattr(instance, field)
ret_url = u'<a href="../../%s/%s/%d/">%s</a>' % (
target._meta.app_label, target._meta.module_name,
target.id, unicode(target)
)
#I don't know how to dynamically determine in what APP we currently
#are, so this is a bit of a hack to enable links to the
#django.contrib.auth.models.user
if "auth" in target._meta.app_label and "user" in target._meta.module_name:
ret_url = u'<a href="/admin/%s/%s/%d/">%s</a>' % (
target._meta.app_label, target._meta.module_name,
target.id, unicode(target)
)
return ret_url
def _add_method(name):
if name is None: return
if isinstance(name, basestring) and name[:8] == 'link_to_':
try:
method = partial(foreign_key_link, field=name[8:])
method.__name__ = name[8:]
method.allow_tags = True
#in my app the "user" field always points to django.contrib.auth.models.user
#and I want my users to see that when they edit "client" data
#"Client" is another model, that has a 1:1 relationship with
#django.contrib.auth.models.user
if "user" in name[8:]:
method.short_description = "Auth User"
setattr(new_class, name, method)
except Exception, ex:
logger.debug("_add_method(%s) failed: %s" % (name, ex))
#make this work for InlineModelAdmin classes as well, who do not have a
#.list_display attribute
if hasattr(new_class, "list_display") and not new_class.list_display is None:
for name in new_class.list_display:
_add_method(name)
#enable the 'link_to_<foreignKeyModel>' syntax for the ModelAdmin.readonly_fields
if not new_class.readonly_fields is None:
for name in new_class.readonly_fields:
_add_method(name)
#enable the 'link_to_<foreignKeyModel>' syntax for the ModelAdmin.fields
if not new_class.fields is None:
for name in new_class.fields:
_add_method(name)
return new_class