Skip to content

Commit 20d92cd

Browse files
authored
Support models with primary keys not named "id" (#17)
* Support models with PK field not named "id" * isChoicesFilter is no longer needed, we always use the desired field name * Add a test model with renamed PK * They just released 0.11.0 (pre-commit)
1 parent 30ab158 commit 20d92cd

File tree

10 files changed

+178
-58
lines changed

10 files changed

+178
-58
lines changed

.pre-commit-config.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.7.3
3+
rev: v0.11.0
44
hooks:
55
- id: ruff
66
- repo: local
@@ -9,4 +9,5 @@ repos:
99
name: pytest
1010
entry: pytest -s tests/testproject/testapp -c tests/testproject/pytest.ini
1111
language: system
12-
types: [python]
12+
pass_filenames: false
13+
types_or: [python, html]

src/dalf/admin.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
# ruff: noqa: PLR0913,ARG002,SLF001
2-
import json
32

43
from django import forms
54
from django.contrib import admin
65
from django.contrib.admin.widgets import get_select2_language
76
from django.urls import reverse
87

98
__all__ = [
9+
'DALFChoicesField',
1010
'DALFModelAdmin',
1111
'DALFRelatedField',
12-
'DALFRelatedOnlyField',
13-
'DALFChoicesField',
1412
'DALFRelatedFieldAjax',
13+
'DALFRelatedOnlyField',
1514
]
1615

1716

@@ -47,7 +46,7 @@ def __init__(self, field, request, params, model, model_admin, field_path):
4746
'app_label': model._meta.app_label,
4847
'model_name': model._meta.model_name,
4948
'field_name': field_path,
50-
'is_choices_filter': json.dumps(obj=False),
49+
'lookup_kwarg': self.lookup_kwarg,
5150
}
5251

5352
def choices(self, changelist):
@@ -66,9 +65,7 @@ class DALFRelatedOnlyField(DALFMixin, admin.RelatedOnlyFieldListFilter):
6665

6766

6867
class DALFChoicesField(DALFMixin, admin.ChoicesFieldListFilter):
69-
def __init__(self, field, request, params, model, model_admin, field_path):
70-
super().__init__(field, request, params, model, model_admin, field_path)
71-
self.custom_template_params.update({'is_choices_filter': json.dumps(obj=True)})
68+
pass
7269

7370

7471
class DALFRelatedFieldAjax(admin.RelatedFieldListFilter):
@@ -83,6 +80,7 @@ def __init__(self, field, request, params, model, model_admin, field_path):
8380
'model_name': model._meta.model_name,
8481
'field_name': field_path,
8582
'ajax_url': reverse('admin:autocomplete'),
83+
'lookup_kwarg': self.lookup_kwarg,
8684
}
8785
selected_value = originial_params.get(self.lookup_kwarg, [])
8886
self.selected_value = selected_value[0] if selected_value else None

src/dalf/static/admin/js/django_admin_list_filter.js

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
const appLabel = element.dataset.appLabel;
99
const modelName = element.dataset.modelName;
1010
const fieldName = element.dataset.fieldName;
11+
const lookupKwarg = element.dataset.lookupKwarg;
1112
const selectedValue = $(element).prev('.djal-selected-value').val();
12-
13+
1314
$(element).select2({
1415
ajax: {
1516
data: (params) => {
@@ -25,17 +26,15 @@
2526
}).on('select2:select', function(e){
2627
var data = e.params.data;
2728
var navURL = new URL(window.location.href);
28-
var queryParam = fieldName + "__id__exact";
2929

30-
navURL.searchParams.set(queryParam, decodeURIComponent(data.id));
30+
navURL.searchParams.set(lookupKwarg, decodeURIComponent(data.id));
3131
window.location.href = navURL.href;
3232
}).on("select2:unselect", function(e){
3333
var navURL = new URL(window.location.href);
34-
var queryParam = fieldName + "__id__exact";
35-
navURL.searchParams.delete(queryParam);
34+
navURL.searchParams.delete(lookupKwarg);
3635
window.location.href = navURL.href;
3736
});
38-
37+
3938
if (selectedValue){
4039
$.ajax({
4140
url: ajaxURL,
@@ -57,14 +56,13 @@
5756
}
5857
});
5958
}
60-
59+
6160
});
6261
return this;
6362
};
64-
65-
function getQueryParams(e, isChoicesFilter) {
66-
var fieldName = e.target.name;
67-
var fieldQueryParam = isChoicesFilter ? `${fieldName}__exact` : `${fieldName}__id__exact`;
63+
64+
function getQueryParams(e) {
65+
var fieldQueryParam = $(e.target).data('lookupKwarg');
6866
var data = e.params.data;
6967
var selected = data.id.replace(/\?/, "");
7068
var queryParams = selected.split("&");
@@ -96,7 +94,7 @@
9694
placeholder: getTextSafe("Filter")
9795
}).on("select2:select", function(e){
9896
var navURL = new URL(window.location.href);
99-
let [fieldQueryParam, queryParams] = getQueryParams(e, $(this).data("isChoicesFilter"));
97+
let [fieldQueryParam, queryParams] = getQueryParams(e);
10098
var isAllorEmptyChoice = true;
10199

102100
queryParams.forEach(function(item){
@@ -113,7 +111,7 @@
113111

114112
}).on("select2:unselect", function(e){
115113
var navURL = new URL(window.location.href);
116-
let [fieldQueryParam, queryParams] = getQueryParams(e, $(this).data("isChoicesFilter"));
114+
let [fieldQueryParam, queryParams] = getQueryParams(e);
117115

118116
queryParams.forEach(function(item){
119117
var [field, value] = item.split("=");

src/dalf/templates/admin/filter/django_admin_list_filter.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
<ul>
88
<li>
99
{% with params=choices|last %}
10-
<select class="django-admin-list-filter admin-autocomplete" name="{{ params.field_name }}" data-is-choices-filter="{{ params.is_choices_filter }}" data-theme="admin-autocomplete">
10+
<select
11+
class="django-admin-list-filter admin-autocomplete"
12+
name="{{ params.field_name }}"
13+
data-lookup-kwarg="{{ params.lookup_kwarg }}"
14+
data-theme="admin-autocomplete"
15+
>
1116
{% for choice in choices %}
1217
{% if choice.display %}<option value="{{ choice.query_string|iriencode }}"{% if choice.selected %} selected{% endif %}>{{ choice.display }}</option>{% endif %}
1318
{% endfor %}

src/dalf/templates/admin/filter/django_admin_list_filter_ajax.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
data-allow-clear="true"
1919
data-app-label="{{ params.app_label }}"
2020
data-model-name="{{ params.model_name }}"
21+
data-lookup-kwarg="{{ params.lookup_kwarg }}"
2122
data-theme="admin-autocomplete"
2223
data-field-name="{{ params.field_name }}"></select>
2324
{% endwith %}

tests/testproject/testapp/admin.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
DALFRelatedOnlyField,
99
)
1010

11-
from .models import Category, Post, Tag
11+
from .models import Category, CategoryRenamed, Post, Tag
1212

1313

1414
@admin.register(Post)
@@ -18,6 +18,7 @@ class PostAdmin(DALFModelAdmin):
1818
('author', DALFRelatedField),
1919
('audience', DALFChoicesField),
2020
('category', DALFRelatedFieldAjax),
21+
('category_renamed', DALFRelatedFieldAjax),
2122
('tags', DALFRelatedOnlyField),
2223
)
2324

@@ -28,6 +29,12 @@ class CategoryAdmin(admin.ModelAdmin):
2829
ordering = ('name',)
2930

3031

32+
@admin.register(CategoryRenamed)
33+
class CategoryRenamedAdmin(admin.ModelAdmin):
34+
search_fields = ('name',)
35+
ordering = ('name',)
36+
37+
3138
@admin.register(Tag)
3239
class TagAdmin(admin.ModelAdmin):
3340
search_fields = ('name',)

tests/testproject/testapp/conftest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@
1010
@pytest.fixture
1111
def posts():
1212
return PostFactory.create_batch(10)
13+
14+
@pytest.fixture
15+
def unused_tag():
16+
return TagFactory(name='Unused')

tests/testproject/testapp/factories.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from django.contrib.auth import get_user_model
33
from factory import fuzzy
44

5-
from .models import AudienceChoices, Category, Post, Tag
5+
from .models import AudienceChoices, Category, CategoryRenamed, Post, Tag
66

77
FAKE_USERNAMES = [
88
'vigo',
@@ -47,6 +47,14 @@ class Meta:
4747
name = factory.Iterator(FAKE_CATEGORIES)
4848

4949

50+
class CategoryRenamedFactory(factory.django.DjangoModelFactory):
51+
class Meta:
52+
model = CategoryRenamed
53+
django_get_or_create = ('name',)
54+
55+
name = factory.Iterator(FAKE_CATEGORIES)
56+
57+
5058
class TagFactory(factory.django.DjangoModelFactory):
5159
class Meta:
5260
model = Tag
@@ -62,6 +70,7 @@ class Meta:
6270

6371
author = factory.SubFactory(UserFactory)
6472
category = factory.SubFactory(CategoryFactory)
73+
category_renamed = factory.SubFactory(CategoryRenamedFactory)
6574
title = factory.Sequence(lambda n: f'Book about {FAKE_CATEGORIES[n % len(FAKE_CATEGORIES)]} - {n}')
6675
audience = fuzzy.FuzzyChoice(AudienceChoices.choices, getter=lambda c: c[0])
6776

tests/testproject/testapp/models.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import uuid
2+
13
from django.conf import settings
24
from django.db import models
35

@@ -9,6 +11,14 @@ def __str__(self):
911
return self.name
1012

1113

14+
class CategoryRenamed(models.Model):
15+
renamed_id = models.UUIDField(default=uuid.uuid4, primary_key=True)
16+
name = models.CharField(max_length=255)
17+
18+
def __str__(self):
19+
return self.name
20+
21+
1222
class Tag(models.Model):
1323
name = models.CharField(max_length=255)
1424

@@ -33,6 +43,11 @@ class Post(models.Model):
3343
on_delete=models.CASCADE,
3444
related_name='posts',
3545
)
46+
category_renamed = models.ForeignKey(
47+
to='CategoryRenamed',
48+
on_delete=models.CASCADE,
49+
related_name='posts',
50+
)
3651
tags = models.ManyToManyField(to='Tag', blank=True)
3752
audience = models.CharField(
3853
max_length=100,

0 commit comments

Comments
 (0)