jet-admin / jet-django

Jet Bridge (Django) for Jet Admin – Admin panel framework for your application
https://app.jetadmin.io/demo
Other
192 stars 32 forks source link

[Bug] PATCH - GenericForeignKey relationship fails with "django.core.exceptions.FieldDoesNotExist: StudentSolution has no field named 'object_id'" #7

Open Vadorequest opened 5 years ago

Vadorequest commented 5 years ago

I have the following dynamic entity in one of my models:

# Dynamic relationship to either Org, Institution or Campus entities
  generic_owner_content_type = models.ForeignKey(
    ContentType,
    on_delete=models.CASCADE,  # TODO check if good thing
    limit_choices_to=(
      models.Q(app_label='tfp', model='org') |
      models.Q(app_label='tfp', model='institution') |
      models.Q(app_label='tfp', model='campus')
    ),
    # null=True,  # Allow a solution not to belong to any owner,
    # blank=True,
  )
  generic_owner_object_id = models.PositiveIntegerField()
  generic_owner_content_object = fields.GenericForeignKey(
    'generic_owner_content_type',
    'generic_owner_object_id'
  )

When I update the entity through the Jet Admin UI such as: image

I get the following error on the backend logs:

[1551913953167] Jet view exception
[1551913953167] Traceback (most recent call last):
[1551913953167] File "/var/task/django/db/models/options.py", line 564, in get_field
[1551913953167] return self.fields_map[field_name]
[1551913953167] KeyError: 'object_id'
[1551913953167] During handling of the above exception, another exception occurred:
[1551913953167] Traceback (most recent call last):
[1551913953167] File "/var/task/jet_django/deps/rest_framework/views.py", line 480, in dispatch
[1551913953167] response = handler(request, *args, **kwargs)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/mixins.py", line 84, in partial_update
[1551913953167] return self.update(request, *args, **kwargs)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/mixins.py", line 69, in update
[1551913953167] serializer.is_valid(raise_exception=True)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/serializers.py", line 236, in is_valid
[1551913953167] self._validated_data = self.run_validation(self.initial_data)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/serializers.py", line 434, in run_validation
[1551913953167] value = self.to_internal_value(data)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/serializers.py", line 488, in to_internal_value
[1551913953167] validated_value = field.run_validation(primitive_value)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/fields.py", line 776, in run_validation
[1551913953167] return super(CharField, self).run_validation(data)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/fields.py", line 524, in run_validation
[1551913953167] self.run_validators(value)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/fields.py", line 538, in run_validators
[1551913953167] validator(value)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/validators.py", line 79, in __call__
[1551913953167] queryset = self.filter_queryset(value, queryset)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/validators.py", line 66, in filter_queryset
[1551913953167] return qs_filter(queryset, **filter_kwargs)
[1551913953167] File "/var/task/jet_django/deps/rest_framework/validators.py", line 31, in qs_filter
[1551913953167] return queryset.filter(**kwargs)
[1551913953167] File "/var/task/django/db/models/manager.py", line 82, in manager_method
[1551913953167] return getattr(self.get_queryset(), name)(*args, **kwargs)
[1551913953167] File "/var/task/django/db/models/query.py", line 844, in filter
[1551913953167] return self._filter_or_exclude(False, *args, **kwargs)
[1551913953167] File "/var/task/modeltranslation/manager.py", line 304, in _filter_or_exclude
[1551913953167] new_key = rewrite_lookup_key(self.model, key)
[1551913953167] File "/var/task/modeltranslation/manager.py", line 51, in rewrite_lookup_key
[1551913953167] fields_to_trans_models = get_fields_to_translatable_models(model)
[1551913953167] File "/var/task/modeltranslation/manager.py", line 141, in get_fields_to_translatable_models
[1551913953167] related_model = get_model_from_relation(f)
[1551913953167] File "/var/task/django/contrib/admin/utils.py", line 446, in get_model_from_relation
[1551913953167] return field.get_path_info()[-1].to_opts.model
[1551913953167] File "/var/task/django/contrib/contenttypes/fields.py", line 396, in get_path_info
[1551913953167] object_id_field = opts.get_field(self.object_id_field_name)
[1551913953167] File "/var/task/django/db/models/options.py", line 566, in get_field
[1551913953167] raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name))
[1551913953167] django.core.exceptions.FieldDoesNotExist: StudentSolution has no field named 'object_id'
[1551913953167] [ERROR] 2019-03-06T23:12:33.148Z 9a1e71e4-30ce-4e99-94b4-8d6f57257a74 Jet view exception
Traceback (most recent call last):
  File "/var/task/django/db/models/options.py", line 564, in get_field
  return self.fields_map[field_name]
KeyError: 'object_id'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/var/task/jet_django/deps/rest_framework/views.py", line 480, in dispatch
  response = handler(request, *args, **kwargs)
  File "/var/task/jet_django/deps/rest_framework/mixins.py", line 84, in partial_update
  return self.update(request, *args, **kwargs)
  File "/var/task/jet_django/deps/rest_framework/mixins.py", line 69, in update
  serializer.is_valid(raise_exception=True)
  File "/var/task/jet_django/deps/rest_framework/serializers.py", line 236, in is_valid
  self._validated_data = self.run_validation(self.initial_data)
  File "/var/task/jet_django/deps/rest_framework/serializers.py", line 434, in run_validation
  value = self.to_internal_value(data)
  File "/var/task/jet_django/deps/rest_framework/serializers.py", line 488, in to_internal_value
  validated_value = field.run_validation(primitive_value)
  File "/var/task/jet_django/deps/rest_framework/fields.py", line 776, in run_validation
  return super(CharField, self).run_validation(data)
  File "/var/task/jet_django/deps/rest_framework/fields.py", line 524, in run_validation
  self.run_validators(value)
  File "/var/task/jet_django/deps/rest_framework/fields.py", line 538, in run_validators
  validator(value)
  File "/var/task/jet_django/deps/rest_framework/validators.py", line 79, in __call__
  queryset = self.filter_queryset(value, queryset)
  File "/var/task/jet_django/deps/rest_framework/validators.py", line 66, in filter_queryset
  return qs_filter(queryset, **filter_kwargs)
  File "/var/task/jet_django/deps/rest_framework/validators.py", line 31, in qs_filter
  return queryset.filter(**kwargs)
  File "/var/task/django/db/models/manager.py", line 82, in manager_method
  return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/var/task/django/db/models/query.py", line 844, in filter
  return self._filter_or_exclude(False, *args, **kwargs)
  File "/var/task/modeltranslation/manager.py", line 304, in _filter_or_exclude
  new_key = rewrite_lookup_key(self.model, key)
  File "/var/task/modeltranslation/manager.py", line 51, in rewrite_lookup_key
  fields_to_trans_models = get_fields_to_translatable_models(model)
  File "/var/task/modeltranslation/manager.py", line 141, in get_fields_to_translatable_models
  related_model = get_model_from_relation(f)
  File "/var/task/django/contrib/admin/utils.py", line 446, in get_model_from_relation
  return field.get_path_info()[-1].to_opts.model
  File "/var/task/django/contrib/contenttypes/fields.py", line 396, in get_path_info
  object_id_field = opts.get_field(self.object_id_field_name)
  File "/var/task/django/db/models/options.py", line 566, in get_field
  raise FieldDoesNotExist("%s has no field named '%s'" % (self.object_name, field_name))
django.core.exceptions.FieldDoesNotExist: StudentSolution has no field named 'object_id'

I do not experience this issue when I use the Django Admin interface.


Solution

Renaming the model fields to the following fixed the issue:

# Dynamic relationship to either Org, Institution or Campus entities
  # XXX https://simpleisbetterthancomplex.com/tutorial/2016/10/13/how-to-use-generic-relations.html
  #  Do not rename "object_id", Jet Admin failed to update field when used a different variable name for some reason
  content_type = models.ForeignKey(
    ContentType,
    on_delete=models.CASCADE,  # TODO check if good thing
    limit_choices_to=(
      models.Q(app_label='tfp', model='org') |
      models.Q(app_label='tfp', model='institution') |
      models.Q(app_label='tfp', model='campus')
    ),
    # null=True,  # Allow a solution not to belong to any owner,
    # blank=True,
  )
  object_id = models.PositiveIntegerField()
  content_object = fields.GenericForeignKey(
    'content_type',
    'object_id'
  )

I guess something is hardcoded on the jet-django package and it doesn't handle custom model variable for dynamic relationships. I only have one in my case so it's not blocking, just annoying, but this should be fixed.

Vadorequest commented 5 years ago

Note that this may actually be the right way to do it, I've not tested it yet though: https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/#generic-relations-in-admin

danielhammer commented 5 years ago

+1 I can confirm this issue. Same happening when calling a genericforeignkey object via manage.py shell.

In my case I had two genericforeignkeys in one model, both defined by using individual attribute names (e.g. "x_content_type" instead of "content_type", "x_object_id" instead of "object_id").

Workaround: Define at least one genericforeignkey by using the default attribute names "content_type" and "object_id" and everything works magically.

Satcheel commented 1 year ago

I am also facing this issue still. For limited things that I have tried - seems like GenericRelation is causing the problem (which is required if I want to filter my based on GenericForeignKey).

Getting the generic fk entity itself is working after filtering even if object_id is not the name. So I think GenericRelation is the problem

Satcheel commented 1 year ago

Regarding the above comment, sorry if this doesn't belong here. I am using normal django only. Not sure what jet-django is. But seems like the same issue. Redirect to the correct issue if there is one.