Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Hadlock
Nov 9, 2004

Vanilla Django is great for building your own blog

Hugo (written in Go) is a fantastic choice for static site generation

Writing a static website generator seems like a great first programming project

Adbot
ADBOT LOVES YOU

CarForumPoster
Jun 26, 2013

⚡POWER⚡
Why would anyone wanting a blog based website not use Wordpress? It has basically everything you need to get writing, SEO your site, etc. way

The Fool
Oct 16, 2003


The big static site generators do all of that too for a way smaller deployment footprint.

I've done a couple sites with GatsbyJS and deploying them is literally "build; push to s3/blob/pages"

Hadlock
Nov 9, 2004

Yeah you can host a static site on S3 for pennies per month

Wordpress is a nightmare to manage yourself due to all the wacky security vulnerabilities. And they're all specific to wordpress. If you host on S3 there's nothing to hack. Or at least, they have to hack Amazon's S3. Plus managed anything is going to be minimum $5/mo

Data Graham
Dec 28, 2009

📈📊🍪😋



And if you want comments you just plug in Disqus, which if I recall runs on Django :buddy:

IAmKale
Jun 7, 2007

やらないか

Fun Shoe

CarForumPoster posted:

Why would anyone wanting a blog based website not use Wordpress? It has basically everything you need to get writing, SEO your site, etc. way
There are also services like Forestry.io and Stackbit that enable Wordpress-like authoring flows while preserving your blog's use of Hugo/Gatsby/etc... for the benefits of a static site.

KICK BAMA KICK
Mar 2, 2009

Hit a bug with a simple explanation; wondering if this is the right solution. Using django_rq as a task queue, had some code that enqueued a job taking a model instance as its argument, and another job taking the same instance as its argument, conditioned on the successful execution of the first job using the depends_on argument to enqueue. Problem is enqueue pickles the arguments you provide to the enqueued functions, so the second job was receiving its argument in the state it was in before the first job was executed, which doesn't work. The solution is just to have the second job call refresh_from_db() on its argument, but I figured the issue would come up again so I figure I'd write a general solution as a decorator for such functions.

Does this look like it does what it's supposed to, or is there like a library that does this I should use instead? I don't know how to write a unit test for this and the way the relevant code in my project is I only caught the bug by basically running it live.
Python code:
import functools
from typing import Any, Callable

from django.db.models import Model


def _is_in_database(obj: Model) -> bool:
    return getattr(obj, 'pk') is not None


def _is_model_instance(obj: Any) -> bool:
    return isinstance(obj, Model)


def refresh_model_args(func: Callable) -> Callable:
    @functools.wraps(func)
    def inner(*args, **kwargs):
        new_args = []
        for arg in args:
            if _is_model_instance(arg) and _is_in_database(arg):
                arg.refresh_from_db()
            new_args.append(arg)
        new_kwargs = {}
        for key, val in kwargs.items():
            if _is_model_instance(val) and _is_in_database(val):
                val.refresh_from_db()
            new_kwargs[key] = val
        func(*new_args, **new_kwargs)
    return inner

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

I always pass model IDs around to jobs, not model instances, because you'll always need to refresh_from_db anyway.

KICK BAMA KICK
Mar 2, 2009

Thermopyle posted:

I always pass model IDs around to jobs, not model instances, because you'll always need to refresh_from_db anyway.
Ohhhh that makes sense like the same way view functions get arguments from the url routing... somehow did not think of that.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

I use python-rq a lot, but I always forget to mention to people that you should pay attention to the fact that it uses pickle and pickle is not secure.

If an attacker gets enough access to your infrastructure to put jobs on your queue, they can make your workers execute arbitrary code because of the way pickle works.

For most people's deployments this doesn't matter because if they have access to your redis instance they already have enough access to run whatever code they want without messing about sending jobs to your workers. However, if your infrastructure is such that redis is exposed to the internet, you should probably start thinking about using celery or something that uses a more secure serialization scheme.

Data Graham
Dec 28, 2009

📈📊🍪😋



Which means you have to pass object ids instead of objects, to keep it JSON-serializable.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Data Graham posted:

Which means you have to pass object ids instead of objects, to keep it JSON-serializable.

Hah, yes!

That's actually what reminded me to make my post about security

DARPA
Apr 24, 2005
We know what happens to people who stay in the middle of the road. They get run over.

Thermopyle posted:

I always pass model IDs around to jobs, not model instances, because you'll always need to refresh_from_db anyway.

A pitfall is that a job taking a model_id can run in a worker before the view that created the job has committed the transaction, leading to your worker not seeing the object in the DB when it tries to fetch it. So make sure your transaction committed before pushing out a job.

KICK BAMA KICK
Mar 2, 2009

Yeah the idea of arbitrary code execution with the job queue and such is something I've wondered about but not super urgently since there's a lot of work to do before deploying and the exposed part of the project is just a control panel for some code I'm already running in stupider form, and I'm the only person who should ever access it (hypothetically I could generalize this into something I could open-source that might be useful to someone else, but no immediate plans), and no data it touches is at all sensitive except a few API keys it uses that I'm storing/retrieving as Docker secrets so I think they're OK.

But the Redis server, I did notice in its docs it says basically "never expose this to the internet cause even with a password it's not hard to attack"; if I'm running the Redis for the task queue as a service in a docker-compose dot yml file and I'm not explicitly exposing that 6379 port on the host, is that secure enough or what?

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

KICK BAMA KICK posted:


But the Redis server, I did notice in its docs it says basically "never expose this to the internet cause even with a password it's not hard to attack"; if I'm running the Redis for the task queue as a service in a docker-compose dot yml file and I'm not explicitly exposing that 6379 port on the host, is that secure enough or what?

Id be fine with that.

NtotheTC
Dec 31, 2007


Let me tell you about the time I got things in the wrong order in a deployment script and installed Redis before firewalling the ports off. Oh and it was a (now fixed) version of Redis that just so happened to configure itself to run on 0.0.0.0:6379 by default and in the ~5 seconds that the port was open some automated script compromised it. That was a fun afternoon spent wondering why I couldn't restart the Redis service.

Data Graham
Dec 28, 2009

📈📊🍪😋



Is it just me or does the get_FOO_display() method not work properly in Django 3.0's new enum-based choices? Specifically in templates?

I have this:

code:
class Product(models.Model):

    class AccountingType(models.TextChoices):
        INVQ = 1, _('INVQ')
        PREPAID = 2, _('Prepaid')

    accounting_type = models.IntegerField(choices=AccountingType.choices, null=True, blank=True)
And in the shell, the method works as I expect:

code:
In [16]: p = Product.objects.first()                                                                                                                                                              

In [17]: p.accounting_type                                                                                                                                                                    
Out[17]: 2

In [18]: p.get_accounting_type_display()                                                                                                                                                      
Out[18]: 'Prepaid'
But in a template:

code:
{{ product.get_accounting_type_display }}
Produces "2".

Am I doing something stupid here? If I switch back to the old style choices, the template works fine (gives me "Prepaid"); the behavior changes as soon as I switch to TextChoices. Can anyone confirm?



E: YEAH I WAS DOING SOMETHING STUPID. Using TextChoices instead of IntegerChoices. Copy-paste idiocy, don't mind me

Data Graham fucked around with this message at 21:57 on Feb 29, 2020

KICK BAMA KICK
Mar 2, 2009

I've of course Googled like "Django authentication" and read all the tutorials that come up but it's not quite clicking, what's the minimum I need to do to this thing I'm building so I'm the only person who can log in to it? Subclass the User model cause you're always supposed to do that? Define one or more permissions (really doesn't need to be that granular if I'm the only person, but I can imagine at least two levels in theory) on that? Slap @login_required or LoginRequiredMixin and a permission check on every view I've defined (this suggests going entirely class-based and inheriting from a common base), test every view for that, and add self.client.force_login() to all tests that touch views? Expose login/logout urls/views but not register, reset password, etc.? Create a superuser the normal way, and then use the shell to create the actual MyThingUser I'll log in with just for the sake of correctness?

Data Graham
Dec 28, 2009

📈📊🍪😋



You can make a middleware to apply @login_required type requirements to all views (except a couple of exempt ones, like /login). I have one that I based on this:

https://stackoverflow.com/questions/3214589/django-how-can-i-apply-the-login-required-decorator-to-my-entire-site-excludin

Data Graham
Dec 28, 2009

📈📊🍪😋



Has anybody seen a thing happen where an Apache/wsgi/Django setup will just sporadically start returning 504 "Timeout when reading response headers from daemon process" errors after some number of days?

It'll work fine for days, then suddenly with no warning it'll just ... stop. All new requests get the timeout. I bounce Apache and it starts working again.

I don't know what to do about it. I've got a dozen other similar setups on other servers, and this has never happened before. Has anyone dealt with something like it?

minato
Jun 7, 2004

cutty cain't hang, say 7-up.
Taco Defender
I need a page to send an AJAX request to add a task to a queue (Celery, Huey, etc), wait for the task to complete, then grab the results and display it. What's the best Django-friendly way to let the page know that the task is done?

Looks like my options are to:
- periodically poll the server for an update
- use Comet "long polling" (which seems obsolete)
- use WebSockets so that the server can just send a signal to tell me when it's done.

I've never done any of these before. What's best practice, are there any helpful libraries, and any pitfalls to watch out for?

Data Graham
Dec 28, 2009

📈📊🍪😋



I use the first option.

I have an AsyncTask model and create a new instance every time I kick off a celery task, and the task periodically updates the object with its percent done. Then the front-end does Ajax calls every few seconds to check the status and display the progress, and at the end gives the link to the artifact.

Django 3 has that new asgi stuff though, anybody using that yet?

minato
Jun 7, 2004

cutty cain't hang, say 7-up.
Taco Defender
The first option seems like the simplest. I don't want to run into hard-to-debug connection issues with WebSockets and Comet.

Did you roll the AsyncTask model yourself, or is that in some library somewhere? This kind of thing seems like a super common use-case but I can't seem to find much sample code or best practices out there.

Edit: Also how do your tasks communicate with the AsyncTask model to update their progress?

"Channels" seems to be the go-to Django library for doing WebSockets and general ASGI stuff, but I think that might be overkill for my simple use-case.

DARPA
Apr 24, 2005
We know what happens to people who stay in the middle of the road. They get run over.

Data Graham posted:

Django 3 has that new asgi stuff though, anybody using that yet?

Async views did not make it into the Django 3 release. You can run via an asgi server, but vanilla Django won't get you anything new. Views and middleware are up next on the roadmap, and the ORM is still in the theory stages.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

If you're not already running a websocket server, polling is the easiest.

However, once you're running a websocket server it's easy to use websockets for this kind of thing.

CarForumPoster
Jun 26, 2013

⚡POWER⚡
I haven’t used them but zapier will let you do web sockets and it looks like it’s super easy. I was gonna give them a try for a mail tracking API I want to start using where the only way they’ll give you updates is a POST via websockets.

EDIT in my use case I’m just sticking them in an RDS database so I have some data about delivery times.

CarForumPoster fucked around with this message at 02:12 on Mar 28, 2020

Data Graham
Dec 28, 2009

📈📊🍪😋



minato posted:

The first option seems like the simplest. I don't want to run into hard-to-debug connection issues with WebSockets and Comet.

Did you roll the AsyncTask model yourself, or is that in some library somewhere? This kind of thing seems like a super common use-case but I can't seem to find much sample code or best practices out there.

Edit: Also how do your tasks communicate with the AsyncTask model to update their progress?

"Channels" seems to be the go-to Django library for doing WebSockets and general ASGI stuff, but I think that might be overkill for my simple use-case.

I rolled my own, it's pretty vanilla really.

Python code:
class AsyncTask(models.Model):
    # I use a uuid for the id cuz I don't like exposing guessable API endpoints
    id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
    date_created = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey('User', null=True, blank=True, on_delete=models.SET_NULL)
    is_complete = models.BooleanField(default=False)
    has_failed = models.BooleanField(default=False)
    percent_complete = models.FloatField(default=0)
    result_file = models.FileField(max_length=255, upload_to=get_report_path, null=True, blank=True)
    result_content_type = models.CharField(max_length=255, blank=True, default='')
Then I have a DRF serializer exposing that model via an API with appropriate authE/authZ

Before I kick off the task I create the AsyncTask object and store the uuid in the front-end, and start polling. And then the task just updates it like any other bit of ORMery:

Python code:
@shared_task
def long_running_task(async_task_id):

    async_task = AsyncTask.objects.get(pk=async_task_id)

    while doing_stuff:

        ... do stuff, calculate percent_complete etc ...

        async_task.percent_complete = percent_complete
        async_task.save()

Data Graham fucked around with this message at 06:00 on Mar 28, 2020

minato
Jun 7, 2004

cutty cain't hang, say 7-up.
Taco Defender
Oh that's really nice. Generic, simple, doesn't require the worker to do any crazy bus messaging to signal that it's done. And it'd be trivial to write a task monitor page. Thanks for the write up!

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Just be aware that if there's any chance of conflicting tasks you need to have some sort of locking mechanism.

It can be stupid like a in_progress field on the model that you check before starting it again, or you can select_for_update.

KICK BAMA KICK
Mar 2, 2009

If I'm not exposing a view for account registration (and only intend this project for my own use, at the moment), but I did want to define a few permissions and a group or two to carry them just for the sake of doing things the right way, is it appropriate to write a createuser management command that would work more or less like the built-in createsuperuser but take some options about the user's role and assign them to the appropriate groups? Would you get_or_create the Group there or previously define them in a data migration?

I think that's essentially how I want it to work but most of the guides on groups and permissions kinda work on the assumption that you're building something like a CMS that an organization would deploy and management or IT would assign permissions to employees via the admin panel (which I don't really intend to use, I guess).

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

KICK BAMA KICK posted:

If I'm not exposing a view for account registration (and only intend this project for my own use, at the moment), but I did want to define a few permissions and a group or two to carry them just for the sake of doing things the right way, is it appropriate to write a createuser management command that would work more or less like the built-in createsuperuser but take some options about the user's role and assign them to the appropriate groups? Would you get_or_create the Group there or previously define them in a data migration?

I think that's essentially how I want it to work but most of the guides on groups and permissions kinda work on the assumption that you're building something like a CMS that an organization would deploy and management or IT would assign permissions to employees via the admin panel (which I don't really intend to use, I guess).

I think a management command is fine for this.

The right way would be to create Groups in a management command, but if you're just doing this for personal use it doesn't really matter.

The only way it would matter is if someone would migrate back prior to this point at some time in the future if the project ever expands beyond personal usage.

KICK BAMA KICK
Mar 2, 2009

Thermopyle posted:

I think a management command is fine for this.

The right way would be to create Groups in a management command, but if you're just doing this for personal use it doesn't really matter.

The only way it would matter is if someone would migrate back prior to this point at some time in the future if the project ever expands beyond personal usage.
I think I'm hung up on two things. 1) I get that Groups and Permissions are data and they live in the database but I feel like I'm conceiving of them as code, like a fundamental way the application works; 2) The two kinds of users I was gonna distinguish between using Groups were basically just operators who can make the thing do (a few different kinds of) stuff and read-only users who can merely view the stuff the thing did. I don't at the moment see any case where I want to distinguish between users who have can_view permission on ModelA but not ModelB, and the "operator" permissions touch a few different models so I'm not even sure the Django-style app.model.can_whatever model permissions are relevant to what I'm doing?

And... typing this out I think I just got the solution: just create two Groups ("operator" and "viewer"), don't bother with any actual Permissions, assign users to one of those groups on creation and then just check for group membership on the relevant views with @user_passes_test. That sound sane?

Also I hope I've said this before but Thermopyle I really appreciate all the answers you give me and you've been a big part of the progress I've made on this project which is a dumb little thing but it means a lot to me. Thanks!

e: Looking into it a little more there is a reasonable hack to create global Permissions not associated with a particular model, which even if I just implemented a single "operate" permission controlling access to a few different functions that's more Djangonic to check, especially in templates, than group membership. And thinking about it even just a little bit further, some of those actions I didn't think mapped cleanly onto 'app.verb_model' kinda do on second thought... Rubber duck never fails!

KICK BAMA KICK fucked around with this message at 20:58 on Apr 1, 2020

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Reading your quote of me makes me realize I said the opposite of what I meant. Oops!


Thermopyle posted:

The right way would be to create Groups in a management command data migration, but if you're just doing this for personal use it doesn't really matter.



KICK BAMA KICK posted:

That sound sane?

Yeah. People often overcomplicate things with permissions. In fact, for now you may just want a boolean field on the User model. It's easy to use migrations to move that over to a group model or some sort of permission at a later point if you need more capabilities.

KICK BAMA KICK posted:

Also I hope I've said this before but Thermopyle I really appreciate all the answers you give me and you've been a big part of the progress I've made on this project which is a dumb little thing but it means a lot to me. Thanks!

You're welcome!

porksmash
Sep 30, 2008
Fixed a little issue using DRF's BrowsableAPIRenderer causing ridiculous, un-optomizable query explosions just so it can render a form. Apparently, for every instance of an object in the API response, for every related field (FK, M2M, etc) on the object, it generates a query to get a list of possible related objects so it can compile an exhaustive list of <select> input options for the form. My list of 100 objects was taking 2.5k queries to generate. With the plain JSON response it takes 7.

The solution is to override rendering the forms to just return a blank string. The default template already is smart enough to hide them if they evaluate to false, and you still get the raw data form where you can type in JSON data.

code:
from rest_framework.renderers import BrowsableAPIRenderer

class NoFormBrowsableAPIRenderer(BrowsableAPIRenderer):
    def get_rendered_html_form(self, *args, **kwargs):
        return ''

NtotheTC
Dec 31, 2007


I'm not using Django in my day job currently so I've not gotten to use Django 3 - is there any reason I shouldn't do the usual API backend / JS framework frontend for my new personal project?

Data Graham
Dec 28, 2009

📈📊🍪😋



I'd actually like to hear the answer to that more generally, since from an architectural/philosophical standpoint I'm wondering what the arguments are in the modern era for using a template-based site rather than an API-driven SPA.

Off the top of my head the arguments I've used are

1. SPA frameworks continue to evolve and writhe about and it's not clear which horse to back, or whether even backing the right horse will pay any dividends when it inevitably self-immolates and is rebuilt from scratch
2. Client-side distributed performance improvements suggest that the most intelligent thing to do is let everybody's computer/phone/watch do the heavyweight UI rendering / application logic and reduce the load on the centralized API server as much as possible (as opposed to making the poor server render all the templates), but this could be a trick
3. Maybe you're rebuilding a legacy app that was written in ColdFusion or some poo poo and thus is inherently template-based, so the lowest-impact way to do it is to stick to the same architecture
4. If you jealously guard the business logic of your JS code as corporate IP, it's hard to protect it because it's static and thus public
5. Django template tools / templatetags are ... really nice, and a shame to not use
6. ???

Data Graham fucked around with this message at 16:18 on Apr 3, 2020

IAmKale
Jun 7, 2007

やらないか

Fun Shoe

NtotheTC posted:

I'm not using Django in my day job currently so I've not gotten to use Django 3 - is there any reason I shouldn't do the usual API backend / JS framework frontend for my new personal project?
I think the real question is, do you need the complexity of that setup? Instead of just focusing on getting Django up and running, you now have to stand up a deployment pipeline and find hosting for your SPA front end too, set up some kind of user login and authentication mechanism between front end and back end, define two sets of routes (API and client-side routing)...

I do full stack work at my day job and so can move quickly through that list to get to a working API + SPA setup, but nowadays I prefer to use Django when I can because there's so much less complexity to deal with when I lean on "traditional" MVC development - I develop so much quicker when I can let Django work all of its magic to render views, handle logins, and query the database. I know that for me it's always quicker to stand up a fresh Django app, use Django's login functionality to get session-based logins working, and get to handling form requests protected with the built-in CSRF token functionality (odd, I know, but it's a level of protection that I don't know too many people in SPA land bother to wire up because it's not "stateless backend" :rolleyes:)

And to the "scalibility" claims, that it's easier to scale up performance when your API and SPA are separated, I say are you Facebook? Are you Google? If not, what's the point of burdening yourself with all of that complexity out the gate when you don't even need it? Work with time-tested "boring" frameworks like Django to get something up and running quickly, scale up Django when you need to, and when that's no longer enough then re-architect to make things API + SPA when you're so big that you need to (and more importantly you now have the development team needed to take on this complexity and handle the rewrite).

Data Graham posted:

1. SPA frameworks continue to evolve and writhe about and it's not clear which horse to back, or whether even backing the right horse will pay any dividends when it inevitably self-immolates and is rebuilt from scratch
This is a great point - there's a ton of churn in frontend dev! Have you heard of our next lord and savior Svelte?

I also like to bring up the fact that React is a Facebook project and that it's being worked on and gains new features to solve Facebook-level problems. Most of us aren't Facebook and aren't working at that scale of users so why do we force ourselves right out the gate to develop to solve those problems?

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

If I was starting a new personal project right now (depending on exactly what the project needed of course) I'd probably start it with FastAPI instead of Django/DRF.

For a professional project I'd still use Django/DRF because of all the reasons Django is good to begin with. Also, I'm a freelancer now and I always try to think about the future for my clients. They're way more likely to be able to find experienced developers in the future if the codebase is Django than if it's something like FastAPI.

However, FastAPI implements a lot of cutting edge features (it uses type annotations!), has a lot of buzz, and I hope it continues to mature.

As far as the question of templates vs SPA...

I find a template-based site way faster to get started developing on and getting results "out the door". However, 90% of the time I'm going to choose a SPA because (for me) all of the positives of a SPA usually outweigh all the negatives.

There's a lot of positives and negatives of templates and SPAs so it's kinda hard to be comprehensive about a comparison.

Some random thoughts:

1. Nowadays, I'm not too concerned with the churn in frontend frameworks. I always choose React when I'm the one choosing and it's 6 years old and they have a nice deprecation policy. Sure, there's new stuff coming out all the time...but so what?
2. It is a chore to start a new project. You can use create-react-app, but I inevitably end up needing to eject because I want to do something that CRA doesn't support. I just avoid CRA now and set up stuff manually.
3. A good IDE makes a JS frontend much nicer to work in. PyCharm in particular works very well with React.
4. Of course, PyCharm also makes templates much easier.
5. There is something to be said in offloading UI rendering to the client...but I'm kinda iffy on that. On one hand, the client is still rendering the CSS/HTML. On the other hand there's a lot of low-spec devices that groan under a heavy JS load. On the gripping hand, it depends on your audience.

porksmash
Sep 30, 2008
I have started converting parts of my project to a Vue multi-page application where I need just a bit more than Django templates + spaghetti javascript can provide. I started off with this tutorial: https://ilikerobots.github.io/django/vue/software/2019/05/26/django-and-vue-multipage.html

What I like best about this approach is that the Vue applications don't have to be your entire page. You can mount them as deep into the DOM as you like, and still use standard Django templating for the boring or preexisting stuff like a header/footer/etc. This lets you bite off the tiniest chunk and write a little Vue, and progressively take over more and more of the template rendering with Vue components. This was the key for me, because I have a large, fully functional Django app using templates and rewriting it as a SPA all in one go is a pretty monumental task. Everything you write is useful for the future if you do eventually want to go full SPA. You can also pass data directly from Django to Vue by rendering it to a variable in template.

Drawbacks are: It's not a SPA. URLs can actually load new pages, and every page load starts your app from scratch. But part 2 of the tutorial I linked shows way to hack it with persisted storage and rehydrate every page load.

I'm sure this all applies to React as well, but I haven't tried.

Adbot
ADBOT LOVES YOU

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

porksmash posted:

I'm sure this all applies to React as well, but I haven't tried.

Yeah, you can and it works well.

It's a nice middle ground for some sites.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply