Django:"matching query does not exist" 在 save() 方法上

Django : "matching query does not exist" on save() method

我正在构建一个获取 API 并用获取的数据填充数据库的应用程序。 我正在尝试为处理的每个 JSON 行保存到数据库。当我最后调用 obj_to_insert.save() 时,出现错误:geotrek.trekking.models.DoesNotExist: POI matching query does not exist. POI 是我的模型之一,它已正确定义和导入。 Topology是另外一个,我觉得是他们两个的关系我处理不好。 这是他们的 类:

class POI(StructureRelated, PicturesMixin, PublishableMixin, MapEntityMixin, Topology):

    topo_object = models.OneToOneField(Topology, parent_link=True, on_delete=models.CASCADE)

class Topology(ZoningPropertiesMixin, AddPropertyMixin, AltimetryMixin,
               TimeStampedModelMixin, NoDeleteMixin, ClusterableModel):
     geom = models.GeometryField(editable=(not settings.TREKKING_TOPOLOGY_ENABLED),
                                srid=settings.SRID, null=True,
                                default=None, spatial_index=False)

这是处理函数的结构(我把它简化了,我使用的所有变量都在真实代码中正确定义等):

with transaction.atomic():
        for datatype_name, datatype_details in dict.items():
            ##fetching the API
            r = response.json()["results"]

            for index in range(len(r)):
                dict_to_insert = {}
                
                for f in normal_fields: #normal_fields are all of the model fields that aren't a primary key or a foreign key
                    dict_to_insert[f.name] = other_dict['key'][f.name]

                obj_to_insert = DataType(**dict_to_insert)

                for f in fkeys_fields: #fkeys_fields are all the model fields that are related to another model
                    fk_to_insert = {}

                    if condition:
                        fk_to_insert[f.name] = other_dict['key'][f.name]
                        new_topo = Topology.objects.create(**fk_to_insert)
                        obj_to_insert.topo_object = new_topo
                    elif condition:
                        obj_to_insert.structure = Structure.objects.get(name = AUTHENT_STRUCTURE)
                    elif condition:
                        fk_to_insert[f.name] = other_dict['key'][f.name] ##simplified
                        setattr(obj_to_insert, fk_field, f.related_model.objects.get(**fk_to_insert))

                obj_to_insert.save()

一切顺利,直到最后的 .save(),这给出了 models.DoesNotExist 错误。就好像 Django 得到了 obj_to_insert,试图在 DB 中找到相应的行但没有找到它(正常:我正在尝试创建它)。

当我 print(vars(obj_to_insert)) 在尝试保存之前,这是输出,对我来说看起来非常好:

{'_state': <django.db.models.base.ModelState object at 0x7f2cd58ab0a0>, 'id': 84909, 'date_insert': None, 'date_update': None, 'deleted': False, 'geom_3d': None, 'length': 0.0, 'ascent': 0, 'descent': 0, 'min_elevation': 0, 'max_elevation': 0, 'slope': 0.0, 'offset': 0.0, 'kind': 'POI', 'geom_need_update': False, 'geom': None, 'uuid': UUID('6e37ea3e-ec49-4eab-a719-6037b3d61ccb'), 'structure_id': 7, 'published': True, 'published_en': False, 'published_fr': True, 'publication_date': None, 'name': 'Refuge de la Lavey', 'name_en': '', 'name_fr': 'Refuge de la Lavey', 'review': False, 'topo_object_id': 84909, 'description': 'Test refuge', 'description_en': '', 'description_fr': 'Test refuge', 'type_id': 8, 'eid': None}

完整的错误信息如下:

Traceback (most recent call last):
  File "/usr/sbin/geotrek", line 20, in <module>
    execute_from_command_line(sys.argv)
  File "/opt/geotrek-admin/lib/python3.8/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/opt/geotrek-admin/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/opt/geotrek-admin/lib/python3.8/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/opt/geotrek-admin/lib/python3.8/site-packages/django/core/management/base.py", line 371, in execute
    output = self.handle(*args, **options)
  File "/opt/geotrek-admin/lib/python3.8/site-packages/geotrek/common/management/commands/import.py", line 55, in handle
    parser.parse(options['shapefile'], limit=limit)
  File "/opt/geotrek-admin/var/conf/parsers.py", line 17, in parse
    spec.loader.exec_module(module)
  File "<frozen importlib._bootstrap_external>", line 848, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/opt/geotrek-admin/var/conf/gag_app/custom_geotrek_shell.py", line 169, in <module>
    test()
  File "/opt/geotrek-admin/var/conf/gag_app/custom_geotrek_shell.py", line 166, in test
    obj_to_insert.save()
  File "/opt/geotrek-admin/lib/python3.8/site-packages/geotrek/trekking/models.py", line 744, in save
    super().save(*args, **kwargs)
  File "/opt/geotrek-admin/lib/python3.8/site-packages/geotrek/common/mixins.py", line 282, in save
    super().save(*args, **kwargs)
  File "/opt/geotrek-admin/lib/python3.8/site-packages/geotrek/common/utils/postgresql.py", line 23, in wrapped
    r = f(*args, **kwargs)
  File "/opt/geotrek-admin/lib/python3.8/site-packages/geotrek/core/models.py", line 574, in save
    existing = self.__class__.objects.get(pk=self.pk)
  File "/opt/geotrek-admin/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/opt/geotrek-admin/lib/python3.8/site-packages/django/db/models/query.py", line 429, in get
    raise self.model.DoesNotExist(
geotrek.trekking.models.DoesNotExist: POI matching query does not exist.

我是否错过了使用 save() 方法或处理外键填充的内容?

全功能:

def test():
    from django.apps import apps
    from django.db import transaction
    from django.contrib.contenttypes.models import ContentType
    from django.contrib.gis.geos import GEOSGeometry, WKBWriter
    from gag_app.env import fk_not_integrated, specific, source_cat_to_gag_cat, core_topology, common, list_label_field
    from config.config import API_BASE_URL, AUTHENT_STRUCTURE, SRID
    from gag_app.utils import geom4326_to_wkt, camel_case, get_fk_row, create_topology, get_api_field, deserialize_translated_fields

    with transaction.atomic():        
        print("Inspecting...")
        coretopology_fields = Topology._meta.get_fields()

        for model_name, model_specifics in specific.items():

            api_model = model_name.lower() # ex: Trek => trek
            url = API_BASE_URL + api_model

            print("Fetching API...")
            response = requests.get(url)
            r = response.json()["results"]
            
            app_name = ContentType.objects.get(model = model_name.lower()).app_label
            print('app_name: ', app_name)
            model = apps.get_model(app_name, model_name)
            print('model: ', model)
            all_fields = model._meta.get_fields(include_parents = False) # toutes les colonnes du modèle
            # print('all_fields: ', all_fields)

            fkeys_fields = [f for f in all_fields if f.many_to_one or f.one_to_one]

            #print(fkeys_fields)

            pkey_field = [f for f in all_fields if hasattr(f, 'primary_key')]
            normal_fields = [f for f in all_fields if f.is_relation is False]

            print('normal_fields: ', normal_fields)

            for index in range(len(r)):
                dict_to_insert = {}

                for f in normal_fields:
                    f_name = f.name
                    print('f_name: ', f_name)
                    if f_name in model_specifics:
                        dict_to_insert = get_api_field(r, index, f_name, model_specifics, dict_to_insert)
                    elif f_name in common['db_column_api_field']:
                        dict_to_insert = get_api_field(r, index, f_name, common['db_column_api_field'], dict_to_insert)
                    elif f_name in common['languages']:
                        dict_to_insert = deserialize_translated_fields(r[index], f_name, dict_to_insert, normal_fields)
                    elif f_name in common['default_values']:
                        dict_to_insert[f_name] = common['default_values'][f_name]
                
                #main_class = eval('models.' + camel_case(model_name))
                print('dict_to_insert: ', dict_to_insert)
                obj_to_insert = model(**dict_to_insert)
                print(vars(obj_to_insert))
                
                for f in fkeys_fields:
                    print(f)
                    fk_to_insert = {}
                    obj_content_type = ContentType.objects.get_for_model(f.related_model)
                    f_related_table = obj_content_type.app_label + '_' + obj_content_type.model
                    related_model_fields = f.related_model._meta.get_fields()
                    related_model_normal_fields_name = [f.name for f in related_model_fields if f.is_relation is False and f.name in list_label_field]
                    print('related_model_normal_fields_name: ', related_model_normal_fields_name)
                    fk_field = f.name
                    print("fk_field: ", fk_field)

                    if len(related_model_normal_fields_name) == 1:
                        name_field = related_model_normal_fields_name[0]
                    elif f_related_table != 'core_topology':
                        print('related_model_fields:', related_model_fields)
                        print('related_model_normal_fields_name:', related_model_normal_fields_name)
                        raise Exception("len(related_model_normal_fields_name) !=1 whereas exactly one field amongst {} should exist in {} table".format(list_label_field, f_related_table))
                    
                    if f_related_table == 'core_topology':
                        print(model_name, ': topology exists')
                        fk_to_insert['kind'] = api_model.upper()

                        geom = GEOSGeometry(str(r[index]['geometry'])) #default SRID of GEOSGeometry is 4326
                        geom.transform(SRID)
                        geom = WKBWriter().write(geom)
                        fk_to_insert['geom'] = geom

                        for ctf in coretopology_fields:
                            ctf_name = ctf.name
                            if ctf_name in core_topology['db_column_api_field']:
                                fk_to_insert[ctf_name] = r[index][core_topology['db_column_api_field'][ctf_name]]
                            elif ctf_name in core_topology['default_values']:
                                fk_to_insert[ctf_name] = core_topology['default_values'][ctf_name]

                        print(fk_to_insert)
                        #obj_to_insert.topo_object = Topology(**fk_to_insert)
                        new_topo = Topology.objects.create(**fk_to_insert)
                        obj_to_insert.topo_object = new_topo

                    elif f_related_table == 'authent_structure':
                        obj_to_insert.structure = Structure.objects.get(name = AUTHENT_STRUCTURE)

                    elif f_related_table in fk_not_integrated and r[index][fk_not_integrated[f_related_table]["api_field"]] is not None:
                        api_main_route = fk_not_integrated[f_related_table]["api_main_route"]
                        url = API_BASE_URL + api_main_route + '/' + str(r[index][fk_not_integrated[f_related_table]["api_field"]])

                        params = {"language" : GAG_BASE_LANGUAGE}

                        print("Fetching API for referred table route...")
                        response2 = requests.get(url, params = params)
                        r2 = response2.json()
                        print('r2:', r2)

                        api_labels = [r2k for r2k in r2.keys() if r2k in list_label_field]

                        if len(api_labels) == 1:
                            api_label = ''.join(api_labels)
                        elif f_related_table != "core_topology":
                            print('API response keys:', r2.keys())
                            print('api_labels:', api_labels)
                            raise Exception("len(api_labels) !=1 whereas exactly one column amongst {} should exist in {} API route".format(list_label_field, api_main_route))

                        old_value = r2[api_label]
                        new_value = source_cat_to_gag_cat[f_related_table][old_value]
                        fk_to_insert[name_field] = new_value
                        setattr(obj_to_insert, fk_field, f.related_model.objects.get(**fk_to_insert))

                    elif fk_field in specific[model_name]:
                        api_field = specific[model_name][fk_field]

                        if type(api_field) is list:
                            old_value = r[index][api_field[0]][api_field[1]]
                        else:
                            old_value = r[index][api_field]
                        
                        new_value = source_cat_to_gag_cat[f_related_table][old_value]

                        fk_to_insert[name_field] = new_value
                        print('fk_to_insert: ', fk_to_insert)
                        setattr(obj_to_insert, fk_field, f.related_model.objects.get(**fk_to_insert))

                print(vars(obj_to_insert))
                obj_to_insert.save()
                print("{} object n°{} inserted!".format(api_model, index))

问题是我的处理顺序。由于 POITopology 的子对象,我无法创建 POI 对象并将其 link 到之后创建的 POI 对象。当我从 SQLAlchemy 切换我的应用程序时,这种事情的逻辑是不同的。