Skip to main content
 首页 » 编程设计

python之为什么 GenericRelation 字段在数据迁移中不起作用(Django)

2024年10月24日31luoye11

我想进行数据迁移,以便在数据库中添加用户阅读帖子。有这样的代码:

def user_read_posts(apps, schema_editor): 
    User = apps.get_model("main", "User") 
    Post = apps.get_model("main", "Post") 
    Comment = apps.get_model("comments", "Comment") 
 
    comments = Comment.objects.all() 
    for comment in comments: 
        print (comment.content_object.__class__.__name__) 
        if isinstance(comment.content_object, Post): 
            comment.user.read_posts.add(comment.content_object) 
 
class Migration(migrations.Migration): 
 
    dependencies = [ 
        ('main', '0039_auto_20160314_0906'), 
    ] 
 
    operations = [ 
        migrations.RunPython(user_read_posts), 
    ] 

并在 print (comment.content_object.__class__.__name__) 行中 django 引发错误: AttributeError: 'Comment' 对象没有属性 'content_object'

评论模型:

class GuidaComment(GenericRelationModel): 
 
    user = models.ForeignKey(GuidaUser) 
    text = models.TextField() 

那我该怎么办呢?

谢谢。

请您参考如下方法:

在 RunPython 迁移脚本 apps.get_models() 中将获取模型的历史版本,而不是您在源代码中拥有的最新 django 模型。这些历史模型非常有限。摘自django documentation :

historical models will not have any custom methods that you have defined. They will, however, have the same fields, relationships, managers (limited to those with use_in_migrations = True) and Meta options (also versioned, so they may be different from your current ones).

但这并不意味着您不能仅通过导入和使用它们来使用最新的和功能齐全的模型。在这种情况下,您冒着迁移脚本将来无法正确运行的风险——因为模型的最新版本可能会更改(例如,模型方法可以重命名、删除、逻辑更改等)。所以,这是可能的,但你需要意识到使用这样的模型的风险。

Content types framework很老,很少更改 django contrib 应用程序和恕我直言,像这样使用它是足够安全的。

灵感来自 answer以及我制作和使用的解决方案,我制作了以下草稿,希望您会觉得有用:

def user_read_posts(apps, schema_editor): 
    User = apps.get_model("main", "User") 
    Post = apps.get_model("main", "Post") 
    Comment = apps.get_model("comments", "Comment") 
 
    from django.contrib.contenttypes.models import ContentType 
 
    comments = Comment.objects.all() 
    for comment in comments: 
        try: 
            # assuming that Comment has object_id field holding reference to PK 
            # of the referenced object  
            ct = ContentType.objects.get(model=comment.content_type.model,  
                                         app_label=comment.content_type.app_label) 
            content_object = ct.get_object_for_this_type(pk=comment.object_id) 
        except Exception, ex:  
            # TODO: can happen if some content type / model is deleted. 
            continue 
 
        print (content_object.__class__.__name__) 
        if isinstance(content_object, Post): 
            # TODO: maybe you will need to copy/adapt read_posts.add method's logic here 
            comment.user.read_posts.add(content_object)