Skip to content

Conversation

@bazubii
Copy link
Contributor

@bazubii bazubii commented Feb 13, 2023

Support nested children past the first level:

  1. View doesn't work as a parent, it is set to the first component and never updated. Instead, use an implicit parent based on the unicorn context (setting the current component at each level)
  2. Handle cleanup/restore of fields after caching
@bazubii
Copy link
Contributor Author

bazubii commented Feb 13, 2023

I think this is a step in the right direction if it doesn't do all that is required. I'm still testing if it works, but I'm having issues getting the parent and grandchildren to update on the client side correctly so maybe there are js or clientside assumptions that need to be fixed as well.

@adamghill
Copy link
Owner

Thanks for this initial step! I'll take a look in the next few days and let you know what I run into. :)

@bazubii
Copy link
Contributor Author

bazubii commented Feb 14, 2023

Sounds good. I think I've found a client side fix I'll add shortly (or init_js at least). I'm going to update an example too since that helps debug. I'm sure there'll be feed back to improve the example, but I wanted to get it working quick as a proof.

@adamghill
Copy link
Owner

I'm going to update an example too since that helps debug

💯 I have found that having both tests and examples is invaluable to make sure all the pieces work together as expected. I really appreciate all the work you are doing for this PR! Looking forward to seeing the JS portion and digging into the code as soon as I can.

…re. Yet to do, send grandparent etc. changes to the client and update the client code to handle that.
@bazubii
Copy link
Contributor Author

bazubii commented Feb 14, 2023

I think I found the client changes needed. At least the example (with my ugly change) is working. I'll stop for now until you get a chance to take a look. I won't be surprised if I'll need to clean it all up some even if it is the right approach. :P

"component_id": self.component_id,
"component_name": self.component_name,
"component_key": self.component_key,
"component": self,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm relatively new to Django (in the last year) so I'm not too sure on how bad this would be to add to the context.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might be fine, but I do wonder if it exposes some data that shouldn't be available in the template. What data did you need in the template? If it was just parent and children, we could explicitly add them to the context? The other approach is add component like you have, but deprecate component_id, component_key, component_name since that would be duplicative going forward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of deprecating those three component pieces, but figured it might break anyone dependent on them. If you're going to bump the minor version, it seems like a safer change to deprecate them.

On what I need component for: I'm using it to set the 'implied' parent. Basically I found that view was always only set to the highest level component, so it wouldn't work to pass in parent=view past the first level. Since this unicorn info is set in the context as it goes down the tree, it was the easiest way to get the parent to each child at each level. I could have used component_id/key/name to look up the parent again, but that seems inefficient when the parent was just called before the child.

It does expose the whole component to the template when all I really need it for is to set the parent of each child. However I didn't think of a cleaner way that wouldn't add look ups or complications.

calls = []

def __init__(self, **kwargs):
# set all these as instance variables instead of just class variables. Otherwise, calling UnicornView()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this was really required, but my tests were awkward without it. They'd have to call construct_component(<args>) instead of just UnicornView(<args>) but I assume in non-test it was working right.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure most of these class variables should be moved into the init, especially the ones that use {} because otherwise all instances will share the same dictionary which I'm pretty sure is not intended. Good catch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did originally remove them from the class variables, but there is an odd check in Django views that says you can't set kwargs for a view that don't match a class variable: https://github.com/django/django/blob/main/django/views/generic/base.py#L89-L94 so I left the class variables but set the instance ones to override them.

def get_cacheable_component(
component: "django_unicorn.views.UnicornView",
) -> "django_unicorn.views.UnicornView":
class CacheableComponent:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll be interested in what you think of this approach. I found having to create the cacheable component, then restore back the extra_context/request everywhere was scary. Meaning I could forget a spot and not notice. I would hope the enter/exit/with approach would help with that, but maybe its awkward.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a much better pattern and using a context manager totally makes sense for this use case.

@adamghill
Copy link
Owner

From my initial testing with the example this seems to be working as intended! 🤩 This is such an awesome PR, thank you again for all your work on this. I'm going to dig into the tests next and make that change to the class variables I mentioned in a comment above, but overall your code additions look really good.

One other note: I will probably bump to the next minor version (not just a patch) because this touches so many different parts of Unicorn. But, I can handle that when I prepare the next release. 👍

@bazubii
Copy link
Contributor Author

bazubii commented Feb 17, 2023

From my initial testing with the example this seems to be working as intended! 🤩 This is such an awesome PR, thank you again for all your work on this. I'm going to dig into the tests next and make that change to the class variables I mentioned in a comment above, but overall your code additions look really good.

One other note: I will probably bump to the next minor version (not just a patch) because this touches so many different parts of Unicorn. But, I can handle that when I prepare the next release. 👍

Awesome! Glad its holding up to testing besides my own 😄. Let me know if I can make any changes or add anything to help it along.

The minor version bump makes sense to me.

I'd be interested to know around how long you think until the next release. I know this is a side project so I understand the answer may be something like "when I can find the time", but just checking in case you have an idea. As soon as it lands I'll start using it since I'm already using my fork for now to unblock a use case I have.

@adamghill
Copy link
Owner

know around how long you think until the next release

As long as I don't run into any gotchas or concerns, I'll publish a new release tonight. 👍

@adamghill
Copy link
Owner

@all-contributors add @bazubii for code and tests

@allcontributors
Copy link
Contributor

@adamghill

I've put up a pull request to add @bazubii! 🎉

@adamghill adamghill merged commit 14beb71 into adamghill:main Feb 18, 2023
@bazubii bazubii deleted the george/nested_children branch February 18, 2023 00:16
@adamghill
Copy link
Owner

@bazubii 0.50.0 has been published to pypi at https://pypi.org/project/django-unicorn/. Let me know if you run into any issues with it!

@bazubii
Copy link
Contributor Author

bazubii commented Feb 18, 2023

@bazubii 0.50.0 has been published to pypi at https://pypi.org/project/django-unicorn/. Let me know if you run into any issues with it!

Awesome! I'll try it out and let you know if anything comes up.

@bazubii
Copy link
Contributor Author

bazubii commented Feb 20, 2023

Found one small thing so far. unicorn:loading doesn't work on children/parents. I.e. if parent P has two children, C1 and C2, unicorn:loading on an element in parent will only trigger for actions in parent, not in C1 or C2. I think conceptually, if a child action can cause changes to a parent component, then loading tags in the parent should trigger for children actions.

@bazubii
Copy link
Contributor Author

bazubii commented Feb 20, 2023

Also found that the children aren't being set correctly for a parent. These lines: https://github.com/adamghill/django-unicorn/blob/main/django_unicorn/components/unicorn_view.py#L366-L375 The self.parent seems set correctly, but somehow children are not being added to their parent correctly. I'll see if I can create a PR to fix that.

@bazubii
Copy link
Contributor Author

bazubii commented Feb 20, 2023

Also found that the children aren't being set correctly for a parent. These lines: https://github.com/adamghill/django-unicorn/blob/main/django_unicorn/components/unicorn_view.py#L366-L375 The self.parent seems set correctly, but somehow children are not being added to their parent correctly. I'll see if I can create a PR to fix that.

Actually I think its more around the caching, still investigating...

@bazubii
Copy link
Contributor Author

bazubii commented Feb 21, 2023

So I think this PR fixes the caching: #511

The behavior I was seeing was that components retrieved from the cache had partial children set. They were cached while rendering or during init, so the first child would have a parent with only one child, the second would have a parent with two children, etc.

I'm going to have to test it more in my app as well, but I think that PR has a test to prove it works

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants