Django ключ OnToOne к родительскому классу с неверным именем - PullRequest
0 голосов
/ 04 марта 2020

Моя Django 2.2 модель (обрезана, чтобы включить только то, что я считаю соответствующими разделами) выглядит следующим образом:

class DataSet(models.Model):
    name = models.CharField(..)
      :::

    class Meta:
        abstract = False

class SurveyRunDataSet(DataSet):
    status = models.CharField(..)
      :::

    class Meta:
        app_label = 'dataset'

class RestrictedDataSet(SurveyRunDataSet):
    date_start = models.DateField(null=True)
    date_end = models.DateField(null=True)
      :::

    # Spoonfeed DJango with parent link
    # parent_link = models.OneToOneField('SurveyRunDataSet', parent_link=True, db_column='dataset_ptr'),

    class Meta:
        app_label = 'dataset'

После очистки всех файлов миграции и запуска "django -admin makemigrations" соответствующие классы в файле 0001_initial.py выглядят следующим образом:

operations = [
        migrations.CreateModel(
            name='DataSet',
            fields=[
                ('id', models.AutoField(auto_created=True,
                           primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(..)),
                    :::
            ],
            options={
                'abstract': False,
            },
        ),
        migrations.CreateModel(
            name='SurveyRunDataSet',
            fields=[
                ('dataset_ptr',
                      models.OneToOneField(auto_created=True,
                            on_delete=django.db.models.deletion.CASCADE, parent_link=True,
                            primary_key=True, serialize=False, to='dataset.DataSet')),
                ('status', models.CharField(..)),
                    :::
            ],
            bases=('dataset.dataset',),
        ),
        migrations.CreateModel(
            name='RestrictedDataSet',
            fields=[
                ('surveyrundataset_ptr', models.OneToOneField(auto_created=True,
                      on_delete=django.db.models.deletion.CASCADE, parent_link=True,
                      primary_key=True, serialize=False, to='dataset.SurveyRunDataSet')),
                ('date_start', models.DateField(null=True)),
                ('date_end', models.DateField(null=True)),
                    :::
            ],
            bases=('dataset.surveyrundataset',),
        ),

Но когда я запускаю тест, происходит сбой с ошибкой:

FieldError: Cannot resolve keyword 'surveyrundataset_ptr' into field.
  Choices are: dataset_ptr, dataset_ptr_id, datasets, flush_date, id,
  name, primary_dataset, report, report_id, source_data_set

, где эти имена полей перечислите все те, которые включены в классы DataSet и SurveyRunDataSet (некоторые для краткости опущены выше.

Я понимаю, Django автоматически создает OneToOneField, и для этого выбирается имя поля в качестве имени родительского класса, за которым следует '_ptr ', в приведенном выше примере' surveyrundataset_ptr '. Но очевидно, что существует несоответствие со всеми доступными полями, в которых оно пытается найти имя!

Я также понял или подумал, что можно создать OneToOneField вручную (закомментированная строка кода выше с комментарием «Spoonfeed DJango с родительской ссылкой»), что помешает Django создать ссылку по умолчанию. Но когда я попробовал это, с каждой возможной комбинацией логических аргументов, он проигнорировал мое определение OneToOneField и все еще создает свое собственное!

Так есть ли способ убедить Django создать в классе RestrictedDataSet (в этом пример) OneToOneField с именем поля 'dataset_ptr', которое будет правильно указывать на родительский класс SurveyRunDataSet?

Кроме того, я немного озадачен, почему такая же проблема не возникает с именем dataset_ptr класса SurveyRunDataSet класса OneToOneField указывая на его родительский класс 'DataSet'. Но тогда, возможно, это вызвало бы аналогичную ошибку, если бы код переноса Django не ошибся до достижения этого!

Любые предложения приветствуются.

Спасибо в ожидании

PS Обратите внимание, что это обновление с версии 1.x Django, которая раньше работала.

Ответы [ 2 ]

0 голосов
/ 05 марта 2020

Сделано немного больше прогресса, хотя это только делает проблему более трудной!

Изменение RestrictedDataClass для запуска следующим образом действительно не позволяет Django создать свое собственное поле OneToOne:

    class RestrictedDataSet(SurveyRunDataSet):
        """
        This acts like a regular survey run dataset except that it imposes date,
        answer and schema restrictions. These are designed to be permanent.
        """

        # Spoonfeed Django with specifically named OneToOne field
        #
        dataset_ptr_id = models.OneToOneField('SurveyRunDataSet', to_field='dataset_ptr',
                 parent_link=True, primary_key=True, related_name='restricteddataset',
                 on_delete=models.CASCADE)

Но тогда «django -admin makemigrations» выдает следующие ошибки, что понятно, поскольку RestrictedDataSet наследуется от SurveyRunDataSet и, следовательно, его атрибуты включают в себя все последние:

    SystemCheckError: System check identified some issues:

    ERRORS:
    dataset.RestrictedDataSet.dataset_ptr_id: (models.E006) The field 'dataset_ptr_id'
    clashes with the field 'dataset_ptr' from model 'dataset.surveyrundataset'.

Но если я дам RestrictedDataSet уникальное имя, например dataset2_ptr_id, затем я возвращаюсь к исходной ошибке:

django.core.exceptions.FieldError: Cannot resolve keyword 'dataset2_ptr_id' into field.
Choices are: dataset_ptr, dataset_ptr_id, datasets, flush_date, id, name,
primary_dataset, report, report_id, source_data_set

Короче говоря, в любом случае я получаю ошибку, и я попадаю между дьяволом и синим море!

0 голосов
/ 05 марта 2020

В ответ на запрос Шиллингта следующая трассировка полного стека:

Traceback (most recent call last):
  File "manage.py", line 37, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/commands/test.py", line 23, in run_from_argv
    super().run_from_argv(argv)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/base.py", line 323, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/base.py", line 364, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.6/dist-packages/django/core/management/commands/test.py", line 53, in handle
    failures = test_runner.run_tests(test_labels)
  File "/usr/local/lib/python3.6/dist-packages/django_nose/runner.py", line 308, in run_tests
    result = self.run_suite(nose_argv)
  File "/usr/local/lib/python3.6/dist-packages/django_nose/runner.py", line 245, in run_suite
    addplugins=plugins_to_add)
  File "/usr/local/lib/python3.6/dist-packages/nose/core.py", line 121, in __init__
    **extra_args)
  File "/usr/lib/python3.6/unittest/main.py", line 95, in __init__
    self.runTests()
  File "/usr/local/lib/python3.6/dist-packages/nose/core.py", line 207, in runTests
    result = self.testRunner.run(self.test)
  File "/usr/local/lib/python3.6/dist-packages/nose/core.py", line 50, in run
    wrapper = self.config.plugins.prepareTest(test)
  File "/usr/local/lib/python3.6/dist-packages/nose/plugins/manager.py", line 99, in __call__
    return self.call(*arg, **kw)
  File "/usr/local/lib/python3.6/dist-packages/nose/plugins/manager.py", line 167, in simple
    result = meth(*arg, **kw)
  File "/usr/local/lib/python3.6/dist-packages/django_nose/plugin.py", line 82, in prepareTest
    self.old_names = self.runner.setup_databases()
  File "/usr/local/lib/python3.6/dist-packages/django_nose/runner.py", line 495, in setup_databases
    return super(NoseTestSuiteRunner, self).setup_databases()
  File "/usr/local/lib/python3.6/dist-packages/django/test/runner.py", line 554, in setup_databases
    self.parallel, **kwargs
  File "/usr/local/lib/python3.6/dist-packages/django/test/utils.py", line 174, in setup_databases
    serialize=connection.settings_dict.get('TEST', {}).get('SERIALIZE', True),
  File "/usr/local/lib/python3.6/dist-packages/django/db/backends/base/creation.py", line 80, in create_test_db
    self.connection._test_serialized_contents = self.serialize_db_to_string()
  File "/usr/local/lib/python3.6/dist-packages/django/db/backends/base/creation.py", line 123, in serialize_db_to_string
    serializers.serialize("json", get_objects(), indent=None, stream=out)
  File "/usr/local/lib/python3.6/dist-packages/django/core/serializers/__init__.py", line 128, in serialize
    s.serialize(queryset, **options)
  File "/usr/local/lib/python3.6/dist-packages/django/core/serializers/base.py", line 90, in serialize
    for count, obj in enumerate(queryset, start=1):
  File "/usr/local/lib/python3.6/dist-packages/django/db/backends/base/creation.py", line 120, in get_objects
    yield from queryset.iterator()
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/query.py", line 341, in _iterator
    yield from self._iterable_class(self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size)
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/query.py", line 55, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/sql/compiler.py", line 1120, in execute_sql
    sql, params = self.as_sql()
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/sql/compiler.py", line 474, in as_sql
    extra_select, order_by, group_by = self.pre_sql_setup()
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/sql/compiler.py", line 55, in pre_sql_setup
    order_by = self.get_order_by()
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/sql/compiler.py", line 330, in get_order_by
    field, self.query.get_meta(), default_order=asc))
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/sql/compiler.py", line 704, in find_ordering_name
    field, targets, alias, joins, path, opts, transform_function = self._setup_joins(pieces, opts, alias)
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/sql/compiler.py", line 734, in _setup_joins
    field, targets, opts, joins, path, transform_function = self.query.setup_joins(pieces, opts, alias)
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/sql/query.py", line 1504, in setup_joins
    names[:pivot], opts, allow_many, fail_on_missing=True,
  File "/usr/local/lib/python3.6/dist-packages/django/db/models/sql/query.py", line 1420, in names_to_path
    "Choices are: %s" % (name, ", ".join(available)))
django.core.exceptions.FieldError: Cannot resolve keyword 'surveyrundataset_ptr' into field. Choices are: dataset_ptr, dataset_ptr_id, datasets, flush_date, id, name, primary_dataset, report, report_id, source_data_set

, а следующее - определение таблицы базы данных RestrictedDataSet, которая, как ни странно, выглядит правильно:

postgres=# \d dataset_restricteddataset
   Table "public.dataset_restricteddataset"
         Column          |  Type   | Modifiers
-------------------------+---------+-----------
 surveyrundataset_ptr_id | integer | not null
 date_start              | date    |
 date_end                | date    |
 restricted_schema_keys  | text    | not null
 filter_expression       | text    | not null
Indexes:
    "dataset_restricteddataset_pkey" PRIMARY KEY, btree (surveyrundataset_ptr_id)
Foreign-key constraints:
    "dataset_restrictedda_surveyrundataset_ptr_9ee743c7_fk_dataset_s" FOREIGN KEY (surveyrundataset_ptr_id) REFERENCES dataset_surveyrundataset(dataset_ptr_id) DEFERRABLE INITIALLY DEFERRED

и для справки, хотя я сомневаюсь, что это уместно, ниже приводится таблица базы данных «SurveyRunDataSet»:

postgres=# \d dataset_surveyrundataset
      Table "public.dataset_surveyrundataset"
     Column     |         Type          | Modifiers
----------------+-----------------------+-----------
 dataset_ptr_id | integer               | not null
 status         | character varying(10) | not null
 base_dataset   | boolean               | not null
 schema_mapping | text                  | not null
 survey_run_id  | integer               |
Indexes:
    "dataset_surveyrundataset_pkey" PRIMARY KEY, btree (dataset_ptr_id)
    "dataset_surveyrundataset_survey_run_id_8e235a8a" btree (survey_run_id)
Foreign-key constraints:
    "dataset_surveyrundat_dataset_ptr_id_beadb771_fk_dataset_d" FOREIGN KEY (dataset_ptr_id) REFERENCES dataset_dataset(id) DEFERRABLE INITIALLY DEFERRED
    "dataset_surveyrundat_survey_run_id_8e235a8a_fk_survey_su" FOREIGN KEY (survey_run_id) REFERENCES survey_surveyrun(id) DEFERRABLE INITIALLY DEFERRED
Referenced by:
    TABLE "dataset_aggregatedataset_datasets" CONSTRAINT "dataset_aggregatedat_surveyrundataset_id_be16b27e_fk_dataset_s" FOREIGN KEY (surveyrundataset_id) REFERENCES dataset_surveyrundataset(dataset_ptr_id) DEFERRABLE INITIALLY DEFERRED
    TABLE "dataset_restricteddataset" CONSTRAINT "dataset_restrictedda_surveyrundataset_ptr_9ee743c7_fk_dataset_s" FOREIGN KEY (surveyrundataset_ptr_id) REFERENCES dataset_surveyrundataset(dataset_ptr_id) DEFERRABLE INITIALLY DEFERRED

Одна вещь, которую он показывает, - указатель внешнего ключа обратно на таблицу «RestrictedDataSet».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...