New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed #14370 -- Added select2 widget for related object fields in admin. #6385
Conversation
@jpic this is a first draft, I tested it manually, it seems fine |
bcaa59d
to
1ba7b5f
Compare
django/contrib/admin/views/main.py
Outdated
@@ -384,3 +389,97 @@ def url_for_result(self, result): | |||
self.opts.model_name), | |||
args=(quote(pk),), | |||
current_app=self.model_admin.admin_site.name) | |||
|
|||
|
|||
class ForeignKeyJsonView(BaseListView): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't there be a LoginRequiredMixin
to prevent unauthorized access?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my bad, wrap
does it (thanks @codingjoe)
Nice ! Did you try it on dynamically added formset rows ? |
@jpic no that can't work yet, but I'm on it today! |
@codingjoe we use DOMNodeInserted event for dynamically added selects: https://github.com/yourlabs/django-autocomplete-light/blob/master/src/dal/static/autocomplete_light/autocomplete.init.js#L25-L27 We also have a really small snippet for option renaming using the edit button which you might like: https://github.com/yourlabs/django-autocomplete-light/blob/master/src/dal_select2/static/autocomplete_light/select2.js#L100-L106 |
10bc3a2
to
c183593
Compare
I might be able to try test this out later this week. Seems to me we should give this a release or two in the real world before deprecating raw_id_fields. Like, make sure it's good enough in practice to replace raw_id_fields. |
Does Edit: Actually, maybe we just need to make sure that you have the change permission for the Model that has the foreign key. That way it follows the permissions of a normal ChoiceField. |
@collinanderson good point, that's what I thought. Lets see how it performs. |
There is a small typo in the diff of docs/ref/models/fields.txt: you mean |
@codingjoe first: Thank you for this great improvement. Next: It would be nice if the autocomplete would be easily re-usable outside the admin, too. Do you plan to support this? |
@guettli no, not really. We have I don't see a way to get this into core. This should remain a admin only feature for now, just like the raw ID field. |
@codingjoe btw, at some point we'll need to eventually get django-developers mailing list approval for vendoring select2. Also, you've been mentioning raw_id_fields. I'm also hoping we can eventually use select2 to replace filter_horizontal/filter_vertical for m2m's. |
@codingjoe you said:
Yes, this is true, since the django admin interface needs a generic ajax server part. But I still think it would be great to have a autocomplete component in django which can be used in django apps and the django admin. You can make a BaseWidget available which gets subclassed once in the admin interface and once for the usage in custom apps. I like small systems and having select2 twice in my static directory gives me a bad feeling. Yes it works and does not hurt, but somehow I think "less is more - avoid redundancy". |
You don't need to have it twice, you can reuse it. For example, this public
facing-app reuses admin scripts:
https://github.com/jonashaag/django-addanother/blob/master/django_addanother/widgets.py#L44-L52
Correct me if I'm wrong but the idea here is to first incorporate
autocompletion in the admin first and then perhaps extract it into
django.forms.
|
3c862f0
to
a674a36
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This completes my detailed review of the patch:
- Make sure a selenium test covers the changes in RelatedObjectLookups.js and all lines of autocomplete.js (e.g. as per Collin's comment, formset tests look missing).
- test_select_multiple selenium test is erroring
- I don't see any test failures if AutocompleteJsonView.paginate_by/get_paginator,and self.paginator_class=... are removed. Also, there aren't any tests that result in 'pagination': {'more': True}, I think.
- test_search_use_distinct may be adding test coverage but it doesn't fail if
qs = qs.distinct()
is commented out as it should to be a good test.
django/contrib/admin/widgets.py
Outdated
return attrs | ||
|
||
def optgroups(self, name, value, attr=None): | ||
"""Return only selected options and set QuerySet from `ModelChoiceIterator`.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does "set QuerySet" mean?
docs/ref/contrib/admin/index.txt
Outdated
in the drop-down. | ||
|
||
``autocomplete_fields`` is a list of fields that you would like to change | ||
to `Select2 <contrib-admin-select2>`_ autocomplete inputs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link is broken, I'm not sure what you intended to link to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems to be old. I remove it.
docs/ref/contrib/admin/index.txt
Outdated
:func:`ModelAdmin.get_search_results` implementation based on a | ||
full text index search. | ||
|
||
It's also advisable to change the ```Paginator`` on very large tables |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This leaves me wondering how to do that.
docs/ref/models/fields.txt
Outdated
@@ -1329,8 +1329,9 @@ The possible values for :attr:`~ForeignKey.on_delete` are found in | |||
|
|||
If ``limit_choices_to`` is or returns a :class:`Q object | |||
<django.db.models.Q>`, which is useful for :ref:`complex queries | |||
<complex-lookups-with-q>`, then it will only have an effect on the choices | |||
available in the admin when the field is not listed in | |||
<complex-lookups-with-q>`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you remind me why this change is related to this patch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't remember writing this. I don't believe it's too. I will revert this.
@@ -957,3 +963,12 @@ def __str__(self): | |||
|
|||
class RelatedWithUUIDPKModel(models.Model): | |||
parent = models.ForeignKey(ParentWithUUIDPK, on_delete=models.SET_NULL, null=True, blank=True) | |||
|
|||
|
|||
class Author(models.Model): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are new models really needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe so, yes. I can go ahead and alter other models more and more, eventually it lead to many side effects and will make the test suite hard to maintain. I removed as many models as I deemed unnecessary without making thing overly complex.
# We can not know if the field is an autocomplete field, | ||
# since `get_autocomplete_fields` requires a request. | ||
# The final url is therefore not available during the url setup. | ||
resolve('/test_admin/admin/admin_views/answer/autocomplete/wrong_field/') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this test isn't working as expected -- it's resolving to RedirectView
, same with test_inline_urls
-- probably the result of the resolve should be checked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe things moved around here, did you already fix it @timgraham ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this comment is still relevant. The comments at the "top" of this review that aren't connected to any specific lines are also still outstanding. Did you see those?
@timgraham I added a lot new selenium tests overing all the cases you described and all the cases I can think of. Puh, that took the the whole day, but it's done. Just in time, sorry for that :/ |
c579f9e
to
e127c72
Compare
elem = self.selenium.find_element_by_css_selector('.select2-selection') | ||
elem.click() | ||
results = self.selenium.find_element_by_css_selector('.select2-results') | ||
self.assertTrue(results.is_displayed()) | ||
elem = results.find_element_by_css_selector('.select2-results__option') | ||
elem.click() | ||
|
||
def test_inline_add(self): | ||
self.selenium.get(self.live_server_url + reverse('admin:admin_views_question_add')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this test complete?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, just a leftover. I currently going over my changelog. I seem to have missed this. I'll remove it.
87dacd9
to
55c8898
Compare
|
||
def test_select_multiple(self): | ||
self.selenium.get(self.live_server_url + reverse('admin:admin_views_lecture_add')) | ||
self.selenium.get(self.live_server_url + reverse('admin:admin_views_question_add')) | ||
elem = self.selenium.find_element_by_css_selector('.select2-selection') | ||
elem.click() | ||
results = self.selenium.find_element_by_css_selector('.select2-results') | ||
self.assertTrue(results.is_displayed()) | ||
elem = results.find_element_by_css_selector('.select2-results__option') | ||
elem.click() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this test complete? What's the purpose of clicking on "No result found."?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does tests no results, which I didn't test in the test before. I will add it to the test before and test multiple select here in more detail including selecting many.
aed1a72
to
c838620
Compare
@timgraham is this happening in 2.0? I'll watch my notifications closely today, incase anything comes up. But I am CEST. |
c838620
to
d596a9e
Compare
…nd ManyToManyField in the admin. Thanks Florian Apolloner and Tim Graham for review and contributing to the patch.
d596a9e
to
94cd8ef
Compare
@codingjoe thanks for all your hard work and patience |
<https://select2.org/>`_ autocomplete inputs. | ||
|
||
By default, the admin uses a select-box interface (``<select>``) for fields | ||
that are . Sometimes you don't want to incur the overhead of selecting all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like not finished sentence
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thx :) see #9109
Finished incomplete sentense in `autocomplete_fields` documenation. Ref django#6385 Ref django#14370
Thanks everyone for all the support, be it mental or though reviews. This has been a crazy but also amazing experience for me. Thanks to @jpic and @apollo13 for your feedback and the help during the DjangoCon and DUTH sprints. Special thanks to @kevin-brown who build the foundation this feature rests upon. I hope this will also send more contributors your way to make Select2 even better. Last but not least, special thanks to @timgraham. Thank you for your guidance and thoroughness and seemingly limitless patience. Thank you <3 |
Hi @codingjoe , I just migrated my test project to django 2a1, and got to test your feature. Congrats for having it merged! For me, the whole point of this autocomplete kind of control is to better handle long lists of items, and list_filter has always been a pain for that. My current workaround is to use an approach like https://stackoverflow.com/a/39618385 , but it'd be much better to have it available out of the box, as part of django 2, now that select2 has been vendored :) In any case, keep up the good work 👍 |
@rom1dep you could propose something like that here: https://code.djangoproject.com/ticket/1873 |
@rom1dep love the idea! Lets put that into a separate ticket. Now that we have a version of Select2 vendored in Django's admin, it should be simple and not invoice too much discussion 👍 |
Added this ticket to facilitate discussion - https://code.djangoproject.com/ticket/29111#ticket |
@paultiplady There isn't really much discussion needed (in the sense that we'd want that feature), if you feel up to the task a PR would be great! |
I agree. Just build it, this would be an amazing addition and actually not too hard to implement. |
Good to know, thanks -- just wanted to check whether anyone else was already looking at it. I'm unlikely to have time in the next couple weeks but my team needs this feature so I'll hopefully find time to have a crack at it soon. |
def test_has_change_permission_required(self): | ||
""" | ||
Users require the change permission for the related model to the | ||
autocomplete view for it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requirement for the change
permission means the autocomplete doesn't work for a user with only the new view
permission on the related model.
See https://code.djangoproject.com/ticket/29502
This should probably be weakened. (No extra permission is needed here if you're not using autocomplete.)
@codingjoe: Your thoughts welcomed! 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't even aware of the view permission. This is fine.
Adds jQuery Select2 version 4 to support async select inputs
including a search feature.
I split the PR in two commits, one is vendoring select2, one contains my code.
Links & Discussions
Changes:
jQuery noConflict is set to false, jQuery itself does not get removed form globalthe new select2 widget is automatically used if the related objecthas a registered admin and defines search fields
Todo:
Possible deprecation of raw_id field?MODEL_change
permission to json view