@@ -25,7 +25,7 @@ def lookup_needs_distinct(opts, lookup_path):
2525 Returns True if 'distinct()' should be used to query the given lookup path.
2626 """
2727 field_name = lookup_path .split ('__' , 1 )[0 ]
28- field = opts .get_field_by_name (field_name )[ 0 ]
28+ field = opts .get_field (field_name )
2929 if hasattr (field , 'get_path_info' ) and any (path .m2m for path in field .get_path_info ()):
3030 return True
3131 return False
@@ -265,7 +265,7 @@ def model_ngettext(obj, n=None):
265265def lookup_field (name , obj , model_admin = None ):
266266 opts = obj ._meta
267267 try :
268- f = opts . get_field ( name )
268+ f = _get_non_gfk_field ( opts , name )
269269 except FieldDoesNotExist :
270270 # For non-field values, the value is either a method, property or
271271 # returned via a callable.
@@ -291,6 +291,17 @@ def lookup_field(name, obj, model_admin=None):
291291 return f , attr , value
292292
293293
294+ def _get_non_gfk_field (opts , name ):
295+ """
296+ For historical reasons, the admin app relies on GenericForeignKeys as being
297+ "not found" by get_field(). This could likely be cleaned up.
298+ """
299+ field = opts .get_field (name )
300+ if field .is_relation and field .one_to_many and not field .related_model :
301+ raise FieldDoesNotExist ()
302+ return field
303+
304+
294305def label_for_field (name , model , model_admin = None , return_attr = False ):
295306 """
296307 Returns a sensible label for a field name. The name can be a callable,
@@ -301,7 +312,7 @@ def label_for_field(name, model, model_admin=None, return_attr=False):
301312 """
302313 attr = None
303314 try :
304- field = model ._meta . get_field_by_name ( name )[ 0 ]
315+ field = _get_non_gfk_field ( model ._meta , name )
305316 try :
306317 label = field .verbose_name
307318 except AttributeError :
@@ -349,11 +360,10 @@ def label_for_field(name, model, model_admin=None, return_attr=False):
349360def help_text_for_field (name , model ):
350361 help_text = ""
351362 try :
352- field_data = model ._meta . get_field_by_name ( name )
363+ field = _get_non_gfk_field ( model ._meta , name )
353364 except FieldDoesNotExist :
354365 pass
355366 else :
356- field = field_data [0 ]
357367 if hasattr (field , 'help_text' ):
358368 help_text = field .help_text
359369 return smart_text (help_text )
@@ -425,19 +435,21 @@ def reverse_field_path(model, path):
425435 parent = model
426436 pieces = path .split (LOOKUP_SEP )
427437 for piece in pieces :
428- field , model , direct , m2m = parent ._meta .get_field_by_name (piece )
438+ field = parent ._meta .get_field (piece )
429439 # skip trailing data field if extant:
430440 if len (reversed_path ) == len (pieces ) - 1 : # final iteration
431441 try :
432442 get_model_from_relation (field )
433443 except NotRelationField :
434444 break
435- if direct :
445+
446+ # Field should point to another model
447+ if field .is_relation and not (field .auto_created and not field .concrete ):
436448 related_name = field .related_query_name ()
437449 parent = field .rel .to
438450 else :
439451 related_name = field .field .name
440- parent = field .model
452+ parent = field .related_model
441453 reversed_path .insert (0 , related_name )
442454 return (parent , LOOKUP_SEP .join (reversed_path ))
443455
@@ -458,7 +470,7 @@ def get_fields_from_path(model, path):
458470 parent = get_model_from_relation (fields [- 1 ])
459471 else :
460472 parent = model
461- fields .append (parent ._meta .get_field_by_name (piece )[ 0 ] )
473+ fields .append (parent ._meta .get_field (piece ))
462474 return fields
463475
464476
0 commit comments