2015-03-18 75 views
2

我正在創建自定義數據遷移,以便基於兩個不同模型中的現有條目自動在數據庫中創建GenericRelation條目。Django ReverseSingleRelatedObjectDescriptor .__ set__ ValueError

例models.py:

... 
class Place 
    content_type = models.ForeignKey(ContentType) 
    object_id = models.PositiveIntegerField() 
    content_object = generic.GenericForeignKey('content_type', 'object_id') 


class Restaurant 
    name = models.CharField(max_length=60) 
    location = models.CharField(max_length=60) 


class House 
    location = models.CharField(max_length=60) 

例0011_place_data.py:

# -*- coding: utf-8 -*- 
from django.contrib.contenttypes.models import ContentType 
from django.db import models, migrations 

def forwards_func(apps, schema_editor): 
    Restaurant = apps.get_model("simpleapp", "Restaurant") 
    House = apps.get_model("simpleapp", "House") 
    Place = apps.get_model("simpleapp", "Place") 

    db_alias = schema_editor.connection.alias 

    content_type = ContentType.objects.using(db_alias).get(
     app_label="simpleapp", 
     model="restaurant" 
    ) 

    for restaurant in Restaurant.objects.using(db_alias).all(): 
     Place.objects.using(db_alias).create(
      content_type=content_type, 
      object_id=restaurant.id) 

    content_type = ContentType.objects.using(db_alias).get(
     app_label="simpleapp", 
     model="house" 
    ) 
    for house in House.objects.using(db_alias).all(): 
     Place.objects.using(db_alias).create(
      content_type=content_type, 
      object_id=house.id) 


class Migration(migrations.Migration): 

    dependencies = [ 
     ('simpleapp', '0010_place') 
    ] 

    operations = [ 
     migrations.RunPython(
      forwards_func, 
     ), 
    ] 

當我運行這個(Django的1.7.4)我得到

Operations to perform: 
    Apply all migrations: simpleapp, admin, sessions, auth, contenttypes 
Synchronizing apps without migrations: 
    Creating tables... 
    Installing custom SQL... 
    Installing indexes... 
Running migrations: 
    Applying projects.0011_place_data...passing 
Traceback (most recent call last): 
    File "manage.py", line 10, in <module> 
    execute_from_command_line(sys.argv) 
    File ".../lib/python3.4/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line 
utility.execute() 
    File ".../lib/python3.4/site-packages/django/core/management/__init__.py", line 377, in execute 
self.fetch_command(subcommand).run_from_argv(self.argv) 
    File ".../lib/python3.4/site-packages/django/core/management/base.py", line 288, in run_from_argv 
self.execute(*args, **options.__dict__) 
    File ".../lib/python3.4/site-packages/django/core/management/base.py", line 338, in execute 
output = self.handle(*args, **options) 
    File ".../lib/python3.4/site-packages/django/core/management/commands/migrate.py", line 161, in handle 
executor.migrate(targets, plan, fake=options.get("fake", False)) 
    File ".../lib/python3.4/site-packages/django/db/migrations/executor.py", line 68, in migrate 
self.apply_migration(migration, fake=fake) 
    File ".../lib/python3.4/site-packages/django/db/migrations/executor.py", line 102, in apply_migration 
migration.apply(project_state, schema_editor) 
    File ".../lib/python3.4/site-packages/django/db/migrations/migration.py", line 108, in apply 
operation.database_forwards(self.app_label, schema_editor, project_state, new_state) 
    File ".../lib/python3.4/site-packages/django/db/migrations/operations/special.py", line 117, in database_forwards 
self.code(from_state.render(), schema_editor) 
    File ".../simpleapp/migrations/0011_place_data.py", line 19, in forwards_func 
object_id=restaurant.id) 
    File ".../lib/python3.4/site-packages/django/db/models/query.py", line 370, in create 
obj = self.model(**kwargs) 
    File ".../lib/python3.4/site-packages/django/db/models/base.py", line 440, in __init__ 
setattr(self, field.name, rel_obj) 
    File ".../lib/python3.4/site-packages/django/db/models/fields/related.py", line 598, in __set__ 
self.field.rel.to._meta.object_name, 
ValueError: Cannot assign "<ContentType: restaurant>": "Place.content_type" must be a "ContentType" instance. 

如果我註釋掉節aising Django模塊中的值錯誤(django.db.models.fields.related.ReverseSingleRelatedObjectDescriptor。 設置)它按預期工作:

... 

    elif value is not None and not isinstance(value, self.field.rel.to): 
     print('skipping') 
     #raise ValueError(
     # 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' % (
     #  value, 
     #  instance._meta.object_name, 
     #  self.field.name, 
     #  self.field.rel.to._meta.object_name, 
     # ) 
     #) 

... 

如果這種例外首先提高?這是Django中的錯誤還是我做錯了?

+0

我不明白你正在評論什麼。並且請你能顯示* full * traceback? – 2015-03-18 21:09:56

回答

1

編輯

下面的解決方案不實際工作;它只是最終沒有運行forwards_func,所以沒有錯誤。新的解決方案歡迎:


我能解決這個問題,使用Django's post_migrate signal

這也修復了從這些類型的遷移(引用ContentType表的數據遷移)到來的一些其他問題。

我的理解是,本質上,問題是ContentType表不會創建,直到遷移的最後纔會出於性能原因。這意味着我沒有實際檢索關係模塊正在檢查的同一類型的ContentType對象。

的解決方案是運行這種類型的數據遷移作爲回調:

# -*- coding: utf-8 -*- 
from django.db.models.signals import post_migrate 
from django.contrib.contenttypes.models import ContentType 
from django.db import models, migrations 

def forwards_func(apps, schema_editor): 
    Restaurant = apps.get_model("simpleapp", "Restaurant") 
    House = apps.get_model("simpleapp", "House") 
    Place = apps.get_model("simpleapp", "Place") 

    db_alias = schema_editor.connection.alias 

    def add_stuffs(*args, **kwargs) 
     content_type = ContentType.objects.using(db_alias).get(
      app_label="simpleapp", 
      model="restaurant" 
     ) 

     for restaurant in Restaurant.objects.using(db_alias).all(): 
      Place.objects.using(db_alias).create(
       content_type=content_type, 
       object_id=restaurant.id) 

     content_type = ContentType.objects.using(db_alias).get(
      app_label="simpleapp", 
      model="house" 
     ) 
     for house in House.objects.using(db_alias).all(): 
      Place.objects.using(db_alias).create(
       content_type=content_type, 
       object_id=house.id) 

    post_migrate.connect(add_stuffs) 


class Migration(migrations.Migration): 

    dependencies = [ 
     ('simpleapp', '0010_place') 
    ] 

    operations = [ 
     migrations.RunPython(
      forwards_func, 
     ), 
    ] 
0

看起來這有點與此相關的Django bug/wontfix

我已經當試圖重新分配權限類似的錯誤我的遷移中的用戶組。要解決它,我已經描述here

這裏是我的移民代碼示例手動發射post_migrate信號:

# -*- coding: utf-8 -*- 
from __future__ import unicode_literals 

from django.db import migrations 
from django.core.management.sql import emit_post_migrate_signal 


def trigger_post_migrate(db_alias): 
    try: 
     # Django 1.9 
     emit_post_migrate_signal(2, False, db_alias) 
    except TypeError: 
     # Django < 1.9 
     try: 
      # Django 1.8 
      emit_post_migrate_signal(2, False, 'default', db_alias) 
     except TypeError: 
      # Django < 1.8 
      emit_post_migrate_signal([], 2, False, 'default', db_alias) 


def up(apps, schema_editor): 
    # trigger post migrate to make sure all permissions and contenttypes are in place 
    trigger_post_migrate(schema_editor.connection.alias) 

    # migration code here... 


class Migration(migrations.Migration): 
    dependencies = [ 
     ('accounts', '0008_auto_20160712_0608'), 
    ] 

    operations = [ 
     migrations.RunPython(up), 
    ]