Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Unicorn – A full-stack web framework for Django (django-unicorn.com)
133 points by siavash on Aug 4, 2023 | hide | past | favorite | 86 comments


I wondered how Unicorn compared to htmx. They're giving an answer on their website:

htmx and alpine.js are great libraries to provide interactivity to your HTML. Both of those libraries are generalized front-end framework that you could use with any server-side framework (or just regular HTML). They are both well-supported, battle-tested, and answers to how they work are probably Google-able (or on Stackoverflow).

Unicorn isn’t in the same league as either htmx or alpine.js. But, the benefit of Unicorn is that it is tightly integrated with Django and it should “feel” like an extension of the core Django experience. For example:

* redirecting from an action uses the Django redirect shortcut

* validation uses Django forms

* Django Models are tightly integrated

* Django messages “just work” the way you would expect them to

* you won’t have to create extra URLs/views for AJAX calls to send back HTML because Unicorn handles all of that for you


Doesn't HTMX work via HATEOAS so would require a full RESTful backend?


You can just respond back anything and it'll work. You can just respond a flat non-hypermedia JSON and have some JavaScript listen to the event and process it with whatever semantics you want (although it'd be weird for you to do that, but I just tried it and yeah it works).

I still don't understand what they mean when they say "HTMX is HATEOAS": it's not parsing the response to decide things based on relations, it's not following any links, you can still have out-of-band info if you want...it's just substituting a string given by the server on the DOM.

A JSON API can be as hypermedia as HTML (I remember HAL-FORMS and Collection+JSON). A web app that consumes such an API is doing "HATEOAS" even if it's using React.

I really like the HTMX project (that and Unpoly are a godsend) but sometimes it sounds more grandiose than it is: just the regular web doing what it always did, except you program the interactive fetching/swapping of pages/fragments in HTML instead of JS, i.e. AJAX with HTML attributes.


Gotcha. I may have to give HTMX a try again. Though, I worked on a HAL-based, full RESTful service back in 2016 and I'm not sure how eager I am to go back.

REST was ok for the read-side of things, but for writing I find RPC/commands to be better than PUT/PATCH. Combine that with the lack of framework for HATEOAS made full REST APIs cumbersome at the time, though things may be different now.


I wouldn’t consider htmx “battle tested”.


Isn't htmx intercooler.js without jquery, which is (intercooler) a 10 years old library been used extensively in a lot of sites?



funny, the reason i say its not battle tested is because of an issue i discovered in the extension system that was mentioned in that post.


Aside:

htmx is a very small and very predictable library. The underlying pattern is well established among many libraries and probably countless similar implementations.

I have implemented something like htmx several times before htmx came out, and I assume many others have too. I just didn't take the time to extract a clean, generalized and comprehensive library.


I personally found a glaring performance issue in the library related to plugins and created a github issue for it back in December (I don't think it's been fixed to date). I was confused because it was pretty obvious. "Battle tested" things, by definition, have been through battles and don't have as many easy pickings on optimizations.


I guess the biggest problem/downside/flaw with this is that you wait for a response from the backend to update the UI. Which is mostly fine, but I've come to expect either a round trip to the server, or instantaneous updates.

This is the uncanny valley for me, it's neither the old school web or "SPA land" (for a lack of a better term). In the todo example, I write some garbage and press "Add", there is absolutely 0 feedback anything happened. I then press it again. About 100ms later, the first thing I typed appeared, then slightly later it got replaced with nothing.

I have no idea how I'd fix that in this framework, there's too much magic for me to figure out where I'd insert a "fix" for this. I'd probably start writing my own javascript to disable the button while it's submitting (which isn't great, but better than today), at which point the "magic" is lost.


I've only learned about Unicorn from this HN post, so take it with a grain of salt, but this appears to be addressed by the framework itself: https://www.django-unicorn.com/docs/loading-states/


Update state isn't quite the same, though -- take the word counting example. That's not easily solvable as far as I can tell.


I'm the creator of Unicorn, so this is neat to see it on Hacker News again. I've been working on Unicorn nights and weekends since July 2020, slowly adding in new functionality and improving it. I have a conference talk explaining the origins a little bit: https://github.com/adamghill/djangocon-eu-2021-conference-ta....

I never expected Unicorn to completely replace larger SPA frameworks, but I have found it solves for most of the use cases I need for my sites.

Thanks for checking it out -- all PRs are greatly appreciated to add new features or fix bugs!




Stimulus Reflex (Ruby), which predates Hotwire, also deserves a mention, though most of its momentum seemed to stall when Hotwire was announced.

https://docs.stimulusreflex.com/


Yes, StimulusReflex deserves some highlight because they were one of the first to introduce this approach!


Slightly longer list here:

https://github.com/liveviews/liveviews


Unicorn is awesome, and I think most would agree that it's the Django communities answer to Livewire/Liveview/etc. Adam has built a brilliant project and the time he must dedicate to it is amazing!

Last year I had a month free and I had a go at building something for Django in this area, with a bunch of interesting ideas - built on Alpine.js, resumable server side component state, inline component templates. But sadly time is limited and I just can't spend the time needed to push it further. One day I may be able to pivot back to it, but would be very happy for someone to take some of these ideas and run with them - https://www.tetraframework.com/

What I really want to see is a modern asset + front end build solution for Django. It would enable more of these sorts of projects. Thats what needs to be built into Django.


Django was a "batteries included" framework about 10 years ago. But the core team had very strong opinions about javascript, saying that picking a javascript framework should NOT be part of those batteries. Time has passed, and javascript is now part of every single website or app we build. Django still doesn't give any help to developers that wants to build a modern site.

My opinion: They should go the Phoenix Live View-route: adding a small websocket js layer, that permits reactivity while deferring as much logic to the backend as possible.


If they had chosen a JS framework as part of the batteries, that framework probably would've been deprecated by now. For instance, who still builds websites with Angular or Backbone/Marionette anymore?

See also Rails, which did choose a battery quite early on: prototype.js. Well, that was a wrong decision, so they changed it to jQuery (which is now also deprecated; no idea what it supports nowadays) and also dropped their RJS templating language. Lots of breakage and churn on every major release for us poor sods who made use of such features.


More recently in Rails: Sprockets, then Webpack, then Vite.

Migrating a small website from one to the other is trivial.

Migrating a large enough website was for my team a multi-month affair involving hours-long debugging sessions, opening tickets in multiple Github projects and contributing to the projects.

Decoupling different software components is a good thing.


That's why I suggested following the Phoenix LiveView route. It's a very thin and fast JS-layer that defers processing to the backend. Avoids framework churn.


> Angular

A LOT of companies ?


I suspect they mean original Angular (i.e. AngularJS) rather than the reboot just called Angular.

Certainly AngularJS was more contemporary with Backbone and was actually both relatively pleasant to use in its era and long outpaced now.


Oh. I don't know. Just half of the biggest sites in the world from Google...


Tell me you know nothing about OG Angular without telling me.


It was called AngularJS--when someone says "Angular" most people will think of the framework called Angular


The status quo for a Django site right now is that everyone that uses it needs to pick a framework themselves. That includes evaluating the "state of js frameworks" and knowing about all the tradeoffs. That's a lot of mental load for a web framework that claims "batteries included".


As someone who builds Django sites, I know very well that at some point you end up using JavaScript in some fashion. However, to start off with a typical Django project you will be perfectly fine with normal forms. From that point on your can adopt Unicorn, htmx, or even VueJS apps using your API.

It's important to note that most users do not care whether a page refreshes or not. That's mostly the developers - being used to SPA's and frameworks. You can build basic requirements very rapidly with Django as-is.


I have done a lot of Django in my time but it feels like it has stood still for the last 8 years. Barely anything has changed in that time - the fact that you have to use a third party library to get a decent REST API is such an odd standpoint. And the laborious configuration routine that’s still required when you start each project should be a solved problem by now.


> Time has passed, and javascript is now part of every single website or app we build

That’s the problem - it shouldn’t be.


...you can just _do_ that? Channels, model save fires realtime frontend update, frontend handles that update.

Would be an awesome pattern to demonstrate, but doesn't have to be built in..


That's the exact reason I mostly stopped developing using Django and decided to learn Elixir. I hate JS and try to use as little as possible on my projects, and LiveView is a god send. Also, the neat integration with Tailwind CSS is out of the box in Phoenix.


Re "batteries included" -- Often when you know what you want to accomplish, then 80% of the time there's an obvious straightforward way to get it done in the framework. The thing that gives me some anxiety about Django (and any other opinionated framework, I suppose), is that the other 20% of the time you're not sure whether there's an esoteric but idiomatic way to do it, or you really need to implement something custom yourself.


Websockets still don't work in a corporate setting where everything is being filtered through an HTTPS-breaking proxy that tries to detect malware and whose authors believe that, because they can't reliably detect malware in websockets, they would rather just block them all in the name of "security".


Great to see the growing effort to explore all the fields left uncultivated by the breathless SPA/angular/react/vue bandwagons of the past decade. It feels like a ratonal reallocation of tasks to where they can be executed best.

Something that is nagging me though is the (still) rather incomplete take on client-side reactivity. Alpine.js is by-and-large addressing this in a pleasantly minimalistic way, but for this new take to become a defacto standard of lean and mean modern web development I feel there should be uniform semantics and integration that spans the different server-side stacks. This way code and knowledge would be reusable and not tied to a specific language/web platform.


Given gunicorn and uvicorn, I can't help but feel this name can and will cause some confusion. We gotta be able to pick different names lol


They should have called it Djunicorn.


Hahaha


I was definitely instantly confused when I saw this.


Thanks for confirming. I knew it couldn't be just me!


We really need an Uncorn(y) name ...


There's Hypercorn too.


Guinicorn coming next!


Isn't unicorn the ruby web server? Name collisions are kinda unavoidable, but I'd at least try to avoid them in the same space.


Also Gunicorn…


How painful if you had to have 2 friends with the same first name...

Name collision is unavoidable and one of the many wrong battles to fight for, IMO.


Gunicorn is literally one of the 2 most used wsgi servers for Django. This name is objectively confusing.


How is it a "full-stack framework" when it's "for Django" that provides the backend service? Also on their own homepage they mention "Using other frontend frameworks with Django", so I'll conclude it is in fact a frontend framework for Django.


This is subjective and in part marketing but given that it is installed with

  pip install django-unicorn
and used with

  from django_unicorn.components import UnicornView
like any other python module used in a Django app, I'd say that it brings the full stack inside Django, much like its counterparts for Laravel, Phoenix and Rails. What was only backend plus server side generated pages is also Javascript now, written inside the backend without even having to know Javascript.


As in "alternative views for Django"?


They almost surely work together with Django standard views and templates. A Django view prepares the data for the template that renders the HTML page. Then some of that HTML talks with the Unicorn views declared in the attributes of its tags.


React aside… hooks, classes redux fatigue whatever- JSX is beautiful and any other framework that builds this quasi irb+mustache+html-like-markup is taking 2 steps back. I would like to see JSX survive the framework wars of 2024-2025


I abandoned Django purely because JSX is so superior to Django templating + adhoc JS. Even if you pivot to Django Rest Framework, you will immediately feel the creaky 10 year old Java EE class based approach that doesn't make intuitive sense until you wade into the magic variables and methods.


I think main issue is that react is easy to integrate with node, next etc. But python, php (laravel) etc. cant just run react-dom on the server. However would be interesting to see react-dom builders for php & python that would allow you to user react as a templating language in Laravel & Django.


I wish there was something like Streamlit, but for non-data apps. Personal sites, blogs, etc... Something that skipped templating and allowed me to declare compenents in a high level way.

ST was easily the most pleasant experience I've ever had developing front end using a Python backend.


My first thought when seeing their validation examples - when I build a Django app and add validations, I start off with a normal form that does a full submit. This is the easiest to get going. As requirements become a bit more complex, I might add JS validation so that a POST is not necessary. Looking at their validation examples:

https://www.django-unicorn.com/examples/validation

This does a POST each time, albeit automatically on typing. It still means a lag for a kind of validation that could have been done client side.


> This does a POST each time, albeit automatically on typing. It still means a lag for a kind of validation that could have been done client side.

If you can accept that lag, the tradeoff is that you only have to write the validation logic once, in one language. Furthermore, if your validation includes something like "check that this username isn't already registered" that requires server-side logic, you're stuck with that POST request and lag anyway.


Agreed. Rather maybe spend time on your UX and ensure that the form is understandable by the user. And by deploying closer to the edge, the latency should be pretty low.


Does

    class TodoView(UnicornView):
create a model?

    class UnicornView(TemplateView)
and

    from django.views.generic.base import TemplateView
Is this Django magic a Flasker doesn't get?


As far as I understand UnicornView attributes (such as `self.tasks`) simply synchronise client state with the server side. It doesn't seem to automagically create a model instance unless you specifically do it.


This is correct. It looks like there is good integration with existing Django models: https://www.django-unicorn.com/docs/django-models/


Reminds me a lot of https://fresh.deno.dev/


Does anyone know of something similar but targeting FastAPI and SQLModel?


Shame there's no jinja support, I really can't stand Django templates.



I do use Jinja with Django, hence me being disappointed that it's not supported by Unicorn.


Ah I didn't know that and thanks for bringing it up.


isn't Django already a web framework?


yes but unicorn offer a lot more than Django


so is it a new python web framework or is it a new web framework on top of Django web framework? It seems like too many layers of abstractions


Maybe Unicorn is to Django as the Apollo app was to the Reddit app


I've done extensive development with Flask, Bottle, Django, Falcon, Django Rest Framework, Starlette, Sanic and FastAPI over about 13 years or so.

These days I prefer to build my back end applications with nodejs, TypeScript and plain SQL (no ORM) talking to Postgres.

I like Django but it pissed me off no end that it's called "batteries included", except that the very first thing you need to do with any Django project is going and find some batteries for the user signup auth flow. That's not batteries included, and its not something you should need to figure out for yourself in a batteries included framework. It's time consuming, complex and error prone and should be built in.

Anyhow as I say, I've prefer nodejs though I still do use Python for lots of stuff.


> I like Django but it pissed me off no end that it's called "batteries included"

The fact that the Django project and you have different preferences and consider different things essential is not a failure on their end. The fact that this "pisses you off" seems to indicate that you could channel your passion into more productive paths.


That doesn't make sense. Django-allauth is the standard for non-rest Auth flow, django-rest-auth and djoser for rest API Auth flow. Each implementation is tailored for a slightly different case.

There's a reason why only admin views are kind of built in and if you want you can expose admin to the end user and it supports the entire Auth flow out of the box.


I can't seem to be able to edit from mobile, I'm actually not sure if you can create a user using /admin out of the box. Anyway, it's a MTVC framework. Views and templates are coupled, and Django provides a user registration form if you want. Of course no one uses that since big projects are using Django for its orm and only need the rest API. The 2 libs I mentioned above are easy to plug in, but they rely on drf, so in order to battery-include the feature you want, Django would have to merge with drf first.


Batteries included means the stuff you need for most uses is already included.

The fact that there are many possible add ins to solve this doesn't address the fact that Django does not include core functionality for web applications.


You mentioned running nodejs + typescript over extensive frameworks like django now. Curious if you chose to write your own framework or are you using something from nodejs space to do "batteries included" thing for you?

Asking because I've had some experience with nest.js and even though it's fun it often feels hackish and a bit messy even when solving standard issues like authorization that's not a simple app wide RBAC.


I don't really know what is different.

I can say that nodejs and TypeScript and pure SQL is so simple and straightforward that I seem to need almost nothing in terms of "framework". I just write endpoints with a query behind them.

On reflection I spent alot of development time hacking around in Django models/forms and the ORM and really almost none of that is needed if you just write straight SQL with Postgres. It's just unnecessary complexity - once you cut all that guff out then things start to become REALLY simple. Request/auth/query/response.

I put auth into a separate web application which gets called by Caddy (or you coudl use Nginx) as an auth subrequest. This makes auth extremely easy, and gives a very powerful decoupling and separation of concerns.

For many years I have thought, finally, that's the last time I need to write a user signup/forgot password/signin flow, but every project still seems to need it written, so I do that myself.

This approach of super simple, just talking to Postgres also would work well with Python but I have come to really like TypeScript more than Python. The latest Python projects I wrote were pretty much Starlette with asynpg talking to Postgres and no ORM or other fluff - that's a pretty nice combination, and asyncpg is the fastest Postgres driver for Python by a country mile which is also nice.

If I wanted RBAC then I would intercept all my SQL queries and wrap them in Postgres RBAC environment variables that implement the Postgres constraints. I've done this before with Django, it worked pretty well, but I think it would be nicer and cleaner in a system without ORM or database abstraction of any form.


I don't understand how what you're describing is batteries included where Django isn't?

It's typically not practical to solve "auth flows" in a centralized way -- needs are so different for different projects. There are tons of third party modules you can just plug in...


My batteries included comment related to Django, nothing else.


Personally, Next.js with NextAuth and Prisma for ORM is a much better full stack dev experience for me. Django templates are their own DSL, adding JS is more annoying and less powerful to do than React/JSX, Django Auth is cumbersome, Django Rest Framework will immediately feel like an old Java EE OOP way of doing REST or JSON API...


The built-in auth system is one of the things I dislike the most about Django.

It's 2023, people log in with e-mail addresses, and even if they log in with a username, make it case insensitive ffs

The very first thing I have to do with each project is completely rebuild the user system, and if you wait until later then it's too late and it's way more difficult to do (according to the docs themselves)


Yep.

The moment you install Django, you have to add all the auth stuff to your todo list. If you're not a Django expert then its easily hours or days pointless work.

Pretty frustrating to instantly be given the large and for some people intimidating problem of implement signup/signin/forgot password/reset password and do it all properly, reliably and securely, even whilst Django smugly asserts that it's "batteries included".

You have to head off and find which auth plug in system to use, work out what is still maintained, which provides the features you need and then the task of integrating it with whatever front end you need to present to the user. All this you need to do if you are new to Django.

It's a huge gap in the Django offering, and makes Django look pretty out of date.

Django should come with a built in user flow that allows signup/signin/forgot password/reset password as well as related emails, and it should come with support for json web token and cookie session, plus example UI code for React, VueJS, plain HTML and maybe some of the other common JavaScript front ends too, as well as APIs for other front ends to use.

Why they don't provide this is hard to understand. It's just instant makework for developers.

Either this or drop the "batteries included" thing.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: