Admin changelist shows no rows with a non-zero count where ForeignKeys are used in list_display and data is bad
|Reported by:||afitzpatrick||Owned by:||nobody|
|Cc:||tonightslastsong@…, timograham@…||Triage Stage:||Accepted|
|Has patch:||yes||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||no|
If you have a record with a foreign key pointing to a non-existent object and that foreign key is included is list_display, the record will not be shown in the admin changelist.
The qs.select_related() call on line 210 of admin/views/main.py in Django 1.0.2-final won't return any rows with bad FK references, as expected.
However, whilst admin tool knows that there are X records to display, select_related() will return (X - number_of_bad_fks) for display. The tool should be able to figure out whether X != (X - number_of_bad_fks) and say "Hey, I'm about to tell you there are 10 records but only show you 5 and not explain why...".
I've been plagued by this problem whilst working on a fairly large application where the data is getting hacked about by a bunch of people. I wrote a little hacky patch for 1.0.2, which I'm sure reviewers won't like. I'll be happy to update for HEAD and fix the way I raise the exception if there's a better way to do this:
--- main.py 2009-07-11 18:24:11.450376926 +0100 +++ /home/afitzpatrick/main-patch.py 2009-07-11 18:24:07.138416529 +0100 @@ -197,8 +197,6 @@ class ChangeList(object): # Use select_related() if one of the list_display options is a field # with a relationship. - result_count = len(qs) - if self.list_select_related: qs = qs.select_related() else: @@ -212,9 +210,6 @@ class ChangeList(object): qs = qs.select_related() break - if len(qs) != result_count: - raise ValueError( 'select_related() returned %d records but %d were expected; there is likely a foreign key mismatch' % (len(qs), result_count) ) - # Set ordering. if self.order_field: qs = qs.order_by('%s%s' % ((self.order_type == 'desc' and '-' or ''), self.order_field))
To replicate the problem, assume something like this:
class Country(models.Model): iso = models.CharField(max_length=2, primary_key=True) class Company(models.Model): name = models.CharField(max_length=128) country = models.ForeignKey(Country) class CompanyAdmin(admin.ModelAdmin): list_display = ['name', 'company']`
Create a single record in the Company table where the country_id field references a Country that doesn't exist, then try to view all Company objects in the admin tool. It'll say "1 Companys" but none will be shown. Django's generated query code (below) will return no records, as it should:
SELECT `app_company`.`id`, `app_country`.`iso` INNER JOIN `app_company` ON (`app_country`.`id` = `app_company`.`iso`)
Change History (16)
comment:10 Changed 6 years ago by
|Component:||contrib.admin → Documentation|
|Patch needs improvement:||set|
|Triage Stage:||Design decision needed → Accepted|