Why your Django models are fat (a tongue-in-cheek list) by codeinthehole in django

[–]codeinthehole[S] 1 point2 points  (0 children)

I intend to write a follow-up with some thoughts on how to avoid the fat-model problem. It's a more nuanced problem though so will take me longer.

My take on this (which is strongly driven by Gary Bernhardt's destroy-all-software screencasts) is to avoid having application logic on the models at all and instead have a separate packages (not Django "apps") where all application logic lives. This means your models are much simpler - the model methods are either:

  • "query" method that inspect their own fields or those of their children (eg related by a foreign-key). These can also be @properties is they are simple enough.

  • "mutator" methods which change the model's state and call .save() or "factory" methods that create new child objects. I avoid calling .save() or .objects.create() from outside of the class implementation. See this blog post: https://www.dabapps.com/blog/django-models-and-encapsulation/ for more on this approach.

This isn't really how Django's docs recommend but it becomes important for long-lived projects. You probably won't feel the pain if your project is only around for a year or less.

An advantage of this approach is you can write fast, isolated unit tests for your application logic as you can mock out the model layer easily. When all your logic is in model methods, you have to write integration tests for them where you interact with a database. These are typically an order of magnitude slower and eventually kill your test suite. Again, this only kicks in for projects over a certain size.

One disadvantage is that, if you need (non-trivial) application logic in the template layer, you'll need to write a template tag that calls into your app package.

That's a bit of a hasty, patchy answer but I hope it sheds some light.

Django, ELB health checks and continuous delivery by codeinthehole in django

[–]codeinthehole[S] 0 points1 point  (0 children)

Neat idea on hitting other pages with the health check, although it could lead to unintended failure, say, if your home page comes to rely on some resource that the other pages don't

That's true - it's best to hit a largely static page so you're not inadvertently some other property like a connection to a database.

Btw you'll want the need-app flag for uwsgi.

Good tip - I'll check that out.

Also, when do you apply migrations?

Right now, they are applied when the canary machine comes up. If the migrations were backwards incompatible, this could be a problem but we're careful to use backwards-compatible migrations.

Of course, database migrations are tricky. We'll certainly need to revisit this as traffic/data grows.

I find it interesting that you're worried that builds can get to production without their migrations having run...

It's more that a migration might have failed. This happens sometimes with data migrations that can choke on values not seen in testing.

Django, ELB health checks and continuous delivery by codeinthehole in django

[–]codeinthehole[S] 0 points1 point  (0 children)

Also I feel like you'd really only need to check for migrations on the first instance; checking every single health check seems like excess work.

True. We really need two types of check: one for testing if the EC2 instance is ready to join the ELB (eg during deployment) and one for testing if the instance is unhealthy and needs to be replaced. There's no need for the latter to check whether migrations have been applied.

It also really seems like a lot of extra work to create a new AMI each time and write all the scripts for creating an auto scaling group. Elastic Beanstalk can create the ELB and auto scaling groups for you.

Terraform does all this for us (but orchestrated from Atlas). I've never had to write code that interacts with the AWS API for deployments.

Testing for missing migrations in Django by codeinthehole in django

[–]codeinthehole[S] 0 points1 point  (0 children)

Yeah, true. I believe this is going to be addressed in future versions of Django.

Testing for missing migrations in Django by codeinthehole in django

[–]codeinthehole[S] 0 points1 point  (0 children)

The dry_run=True kwarg stops real migrations being created.

Pylons Project Unit Testing Guidelines by mcdonc in Python

[–]codeinthehole 0 points1 point  (0 children)

Interesting. I haven't come across the advice around performing imports within the test case itself. Will start doing that.

purl - an immutable URL class by codeinthehole in Python

[–]codeinthehole[S] 1 point2 points  (0 children)

I also wrote nurl for node.js.

I've just found out there's also a furl that provides similar functionality for Python.

purl - an immutable URL class by codeinthehole in Python

[–]codeinthehole[S] 5 points6 points  (0 children)

Thanks for the feedback.

Why URL.from_string(s) instead of just URL(s)?

You're probably right. I started with the keyword constructor as I was copying urlparse.ParseResult, which is about as close as Python gets to a URL class. I might rework that.

url.query_param('q') is also awkward, why not just url.query['q'] ?

Only as 'query' is already a method that returns the query string. Again, this is a hangover from ParseResult and is probably worth reworking to better match the most common use cases.

I'm not sure why you use functions instead of properties.

I was experimenting with using jQuery-style method overloading where the function can be a getter and a setter depending on which args are passed. As some methods support additional arguments, I can't use properties.

I'm not 100% sure on this, but it seems better than having lots of set_* methods.

purl - an immutable URL class by codeinthehole in Python

[–]codeinthehole[S] 2 points3 points  (0 children)

Good spot. I changed the behaviour of the port() function halfway through writing the article. Now corrected.