Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions django_unicorn/static/js/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ export class Component {

this.document = args.document || document;
this.walker = args.walker || walk;
this.window = args.window || window;

this.root = undefined;
this.modelEls = [];
this.dbEls = [];
this.loadingEls = [];
this.keyEls = [];
this.errors = {};
this.return = {};
this.poll = {};

this.actionQueue = [];
Expand Down
13 changes: 12 additions & 1 deletion django_unicorn/static/js/messageSender.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function send(component, callback) {
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest",
};
headers[component.csrfTokenHeaderName] = getCsrfToken();
headers[component.csrfTokenHeaderName] = getCsrfToken(component);

fetch(component.syncUrl, {
method: "POST",
Expand All @@ -57,6 +57,16 @@ export function send(component, callback) {
throw Error(responseJson.error);
}

// Redirect to the specified url if it is set
// TODO: For turbolinks support look at https://github.com/livewire/livewire/blob/f2ba1977d73429911f81b3f6363ee8f8fea5abff/js/component/index.js#L330-L336
if (responseJson.redirect && responseJson.redirect.url) {
component.window.location.href = responseJson.redirect.url;

if (isFunction(callback)) {
callback([], null, null);
}
}

// Remove any unicorn validation messages before trying to merge with morphdom
component.modelEls.forEach((element) => {
// Re-initialize element to make sure it is up to date
Expand All @@ -67,6 +77,7 @@ export function send(component, callback) {
// Get the data from the response
component.data = responseJson.data || {};
component.errors = responseJson.errors || {};
component.return = responseJson.return || {};
const rerenderedComponent = responseJson.dom;

morphdom(component.root, rerenderedComponent, MORPHDOM_OPTIONS);
Expand Down
8 changes: 5 additions & 3 deletions django_unicorn/static/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ export function $(selector, scope) {
/**
* Get the CSRF token used by Django.
*/
export function getCsrfToken() {
export function getCsrfToken(component) {
// Default to looking for the CSRF in the cookie
const cookieKey = "csrftoken=";
const csrfTokenCookie = document.cookie
const csrfTokenCookie = component.document.cookie
.split(";")
.filter((item) => item.trim().startsWith(cookieKey));

Expand All @@ -72,7 +72,9 @@ export function getCsrfToken() {
}

// Fall back to check for the CSRF hidden input
const csrfElements = document.getElementsByName("csrfmiddlewaretoken");
const csrfElements = component.document.getElementsByName(
"csrfmiddlewaretoken"
);

if (csrfElements && csrfElements.length > 0) {
return csrfElements[0].getAttribute("value");
Expand Down
28 changes: 23 additions & 5 deletions django_unicorn/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from django.db.models import Model
from django.http import HttpRequest, JsonResponse
from django.http.response import HttpResponseRedirect
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.http import require_POST

Expand All @@ -12,6 +13,7 @@
from .call_method_parser import InvalidKwarg, parse_call_method_name, parse_kwarg
from .components import UnicornField, UnicornView
from .errors import UnicornViewError
from .serializer import dumps
from .utils import generate_checksum


Expand Down Expand Up @@ -159,10 +161,9 @@ def _get_property_value(component: UnicornView, property_name: str) -> Any:

def _call_method_name(
component: UnicornView, method_name: str, params: List[Any]
) -> None:
) -> Any:
"""
Calls the method name with parameters.
Also updates the data dictionary which gets set back as part of the payload.

Args:
param component: Component to call method on.
Expand All @@ -174,9 +175,9 @@ def _call_method_name(
func = getattr(component, method_name)

if params:
func(*params)
return func(*params)
else:
func()
return func()


class ComponentRequest:
Expand Down Expand Up @@ -257,6 +258,7 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
component.hydrate()

is_reset_called = False
return_value = None

for action in component_request.action_queue:
action_type = action.get("type")
Expand Down Expand Up @@ -373,14 +375,28 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
validate_all_fields = True
else:
component.calling(method_name, params)
_call_method_name(component, method_name, params)
return_value = _call_method_name(component, method_name, params)
component.called(method_name, params)
else:
raise UnicornViewError(f"Unknown action_type '{action_type}'")

# Re-load frontend context variables to deal with non-serializable properties
component_request.data = orjson.loads(component.get_frontend_context_variables())

return_data = {}
redirect_data = {}

if return_value is not None:
if isinstance(return_value, HttpResponseRedirect):
redirect_data = {
"url": return_value.url,
}
else:
try:
return_data = orjson.loads(dumps(return_value))
except:
pass

if not is_reset_called:
if validate_all_fields:
component.validate()
Expand All @@ -400,6 +416,8 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
"dom": rendered_component,
"data": component_request.data,
"errors": component.errors,
"redirect": redirect_data,
"return": return_data,
}

return JsonResponse(res)
4 changes: 3 additions & 1 deletion example/unicorn/components/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.shortcuts import redirect
from django.utils.functional import cached_property

from coffee.models import Flavor
Expand Down Expand Up @@ -29,7 +30,8 @@ def hydrate(self):

def add_instance_flavor(self):
self.instance_flavor.save()
self.reset()

return redirect(f"/models?createdId={self.instance_flavor.id}")

def add_class_flavor(self):
self.class_flavor.save()
Expand Down
Loading