-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
New UI for listing object uses, including in RichText and StreamField. #3961
New UI for listing object uses, including in RichText and StreamField. #3961
Conversation
if isinstance(value, None): | ||
return 'null' | ||
return '"%s"' % re.escape(str(value).replace('"', r'\"') | ||
.replace('|', r'\|')) |
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.
Almost all this could probably be replaced with json.dumps(…)
. I wrote it this way since it seemed more flexible if I needed some special case like models.
% '|'.join([block.name for block in block_path | ||
if getattr(block, 'name', '')])}) | ||
value_filter = ( | ||
Q(**{field.attname '__regex': '(%s)' |
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.
Needs an r
escaping.
@BertrandBordage very interesting! I tried to do something similar in wagtail-inventory, but hadn't considered the similarity to how Django looks up related objects. In wagtail-inventory the page->block relationships are stored in a DB table, updated via page signals, and exposed for query through the admin. I'd (perhaps wrongly) assumed that doing an on-the-fly query wouldn't be scalable for large sites - have you done any performance testing of this approach across many pages/complex streamfields? |
@BertrandBordage Do you think this part would go into core eventually? I’d like to write tests doc for it, but am wondering what would be the correct place for it. An add-on product maybe? Another thing: I tried this in bakerydemo, and tried to find the linked streamfield on the homepage, but it doesn't find that. In
This is something that a test could likely clarify. And in order for this to work with existing templates, we'd have to create a wrapper around it that has the count() method, for example like in https://github.com/wagtail/wagtail/pull/3954/files#diff-e0210a80d3ac707eaa237beb0f6e5cc5R89, right? |
@chosak It’s indeed sensibly slower than having an intermediate table that’s automatically updated.
We might defer doing this until there is some support for @khink Yes, this will go to the core, along with a similar collector for rich text fields/blocks. It indeed needs a lot of tests before being considered stable. |
Bumping this due to this similar request on Stack Overflow looking for a way to find all pages containing a snippet. |
Just improved a lot this pull request by adding a collector for rich text, as well as using Django’s collector for simple ForeignKey/ManyToMany/etc relations. Now, the from wagtail.core.collectors import get_all_uses
get_all_uses(Image.objects.first()) I’m aware that this work is difficult to review: it’s fairly complex and it’s hard to figure out what’s going on without an excellent knowledge of If someone could at least review the tests to check that at least it works as expected, it would be awesome :) |
Following @lb-’s review, I improved this pull request. Now it shows all related objects, including those with other I’m also proud to announce that for the first time in Django history, users can see what will happen when deleting an object. I added a new label to get user attention on the Here are answers to @lb-’s review:
|
By the way, for related StreamField and RichText, I wrote that deleting this object will mark the field as empty (i.e. |
@BertrandBordage @gasman I have tried to think of other use cases for showing the usage view / count beyond when needing deletion and cannot think of any for Images/Documents. There is a case maybe for the navigation aspect in ModelAdmin, let's say you have a Person model and you want to be able to show the Person listing but also provide a link in each row to 'pages that use this Person'. The goal here is to navigate around Admin but not using the Explorer tool. This could be reasonably easily reimplemented, but might be even easier if we kept the snippet model's Also - @BertrandBordage this is such an awesome feature, thanks so much! |
Just loaded up the latest version of this PR, looking awesome! The behaviour on related StreamField and RichText is correct as you have indicated on the delete page. Eg. When an image is deleted the resulting RichText field just shows up nothing, and StreamField or ImageChooserPanel just have empty links shown when you next edit the page that used them. |
Hi i've just added this #4169 I'm less concerned about showing how many times images and documents are used but it feels important to show that snippets are used in more than one place. It could be that we show a warning to say it's used in more than one place with a link to the full list to avoid performance issues. Any thoughts? Also I think this needs some UI design work. I can prioritise this if there is urgency? |
@tmsndrs: This will still have a very important impact on performance for now. The first things I check are related objects through foreign keys, many-to-many and generic relations. To do that, I use the Django Collector class that fetches all related objects at once, triggering dozens to hundreds of queries. That class would have to be written again to use generators instead, which would be a significant effort, especially to test. So it’s not a definitive “no”. |
Also @tmsndrs, feel free to submit a mockup of what you have in mind in terms of UI :) |
Looks like the rich text handling here will need to be substantially reworked now that #4079 is merged :-( Unfortunately c293860 is already pushing the limit of what's feasible to review in one go - I think this could really do with splitting up into multiple PRs:
Shall we bump this to 2.1? (Now that I've seen the full scope of the changes here, it feels like another major codebase-spanning refactor is a bit too much for 2.0, and this way we get some breathing room for @tmsndrs to give it some proper design attention...) |
yes please |
@BertrandBordage excellent work here (as usual). I have some thoughts regarding one of the decisions made above though, which is the one to always exclude With this in mind, can we really be sure those relationships should never be surfaced in this interface? Is there any way we might cater for cases where including them would be preferred? |
@ababic I think there's a similar argument to wagtail/django-modelcluster#89 (comment) to be made here: Just as there's no conceivable reason not to use Anything involving InlinePanel certainly shouldn't be surfaced here: it's redundant to tell the user "deleting this page will cause the 'see also' links on the page to be deleted", or "deleting this menu will cause its submenus to be deleted". (For the casual user who isn't thinking in terms of database schemas, it most likely doesn't even occur to them that the thing in an InlinePanel is an independent object that could exist outside of its container - to them, it's just a repeating field.) |
Sorry, I really should have updated the 'needs review' / 'needs work' labels when I added #3961 (comment) ... I thought this was still on my review queue, but in fact it still needs the rich text bits to be rewritten following #4079, and - ideally - being split up into smaller PRs. (There's a lot going on in c293860 in particular, and we need to make sure that breaking changes such as changing the API of |
Thanks for the feedback @gasman. I'm with you now. Things are slightly muddied here, as we're dealing with surfacing PROTECTED relationships as well as those that would be affected by a delete operation were it to happen. I was thinking somehow that ignoring I'll shut up now :) |
Superseded by #4481. |
This class does a similar job to Django’s collector, except that it works for StreamFields.
It can find:
It relies a lot on generators, which allows for optimizes
any
queries.For example, we may want to know if a given image is used in any StreamField from the database, and stop at the first use found:
Of course, images can be used at other places than StreamField. This is a future work: make the same for
RichTextField
RichTextBlock
, then create a function to call all functions: Django’s ORM relations collector, the StreamField collector and the rich text collector (that would also rely on the StreamField collector, combined with regular expressions).