Live data from Hacker News

Django Styleguide

github.com

81–90 of 141 posts

Re: Django Styleguide

#81
post #17

I think there are too many concepts in this, but rather than being negative, here are some tips: Always keep your models slim. Don't stuff template related stuff in there. You need to look at those models often, so compact is a win. course_has_finished(course) is not much longer than course.has_finished(), and will allow you to expand the functionality as time goes on. Do precomputation if you need the information in…

> Always keep your models slim.

At least never put your business logic in your views, forms or even templates.

Re: Django Styleguide

#82
HN comments are really disparaging, but after reading through I really liked the content and am going to pull out their service / serializer model to use in my project. Nice opinionated way to avoid structuring a program in a bad way.

Re: Django Styleguide

#83

Earlier quoted context omitted.

> Always keep your models slim. As simple as possible, but no simpler. Django models are meant to deal not just with the data, but also with business logic. If `course.has_finished` is a property of the course, why would you want to have a separate function outside of the class? > Do precomputation if you need the information in a template If the precomputation is only needed in a template, you can (should, IMO) use…

At my current company, we've had many teams over the years fail to make business logic in model methods work, and I think many other people have had similar results. The issues usually boil down to some combination of "business logic is too coupled to the data model" and "this method lives at an intersection of these two models and creates weird dependency problems". I now feel that Django puts you down a path for fa…

> I now feel that Django puts you down a path for failure by naming the DB layer "models" and not giving users a decent place to put cross-model domain logic.

I think a lot of 'MVC-inspired' frameworks fail there, not just django. Rails... 'app/helpers' maybe? Laravel 'models' is it, and 'services' or a variation is something I see a lot of folks adding, but it's not an out of the box convention. I can't remember anything specific/explicit in the asp.net world either.

Re: Django Styleguide

#84
post #74

Earlier quoted context omitted.

> Would that mediator be another app? Yes! It is an app that might not even have any model classes. But it will contain business logic. And it will probably speak domain language, which is great. If you're lucky, those two other apps will become pluggable . You will probably never replace them, but separation of concerns is always nice. The downside of course is that you will have 3 apps instead of 1. That's the bala…

So, the way I understand it, job/services.py and load/services.py depend on mediator/mediator.py which depends on job/models.py and load/models.py , instead of job/services.py ultimately using load/services.py , and vice versa. Thanks so much!

This is good enough to break circular dependencies between individual modules, but keep in mind that circular dependencies between the apps remain (e.g. job depends on mediator, mediator depends on job).

I usually prefer to resolve those too. If job is small enough, all the orchestration of jobs should happen through mediator (same for load). If it's not plausible, then job can emit signals which mediator subscribes to.

A good place to start is to give a more descriptive name to the mediator. Sure, it mediates between the two, but it probably does that to implement some business process. Can you name it after that process?

Re: Django Styleguide

#85
post #64

Earlier quoted context omitted.

Same here. If I was starting a non-Python project tomorrow I'd consider using Django to manage the database schema - especially now that we can describe custom indexes and constraints in migrations. Our project has gone thru over 1000 migrations so far tho we squash them down to 50 or so about once a year.

Thats interesting, if I was using TypeScript to access the data, how would I keep the schemas in sync between TS and python?

if using DRF you export openapi to json and use openapi-typescript-codegen

if using graphene or strawberry you export sdl to json and use @graphql-codegen/typescript

Re: Django Styleguide

#86
On testing, I think "one file & class per thing-to-test" is subtly bad advice. It's not harmful in the hands of someone that knows what they are doing, but it tends to point engineers towards a tightly-coupled test suite, which ends up making refactoring more painful and error prone down the road.

If you have one testclass per entity, then if you make any changes to the structure of your services/models/entities, you must restructure your tests too. This means you can't do the "dream refactor" where you don't touch your tests, and restructure your code without changing any behavior. If you rewrite your tests whenever your structure changes, how can you be sure you've not broken your tests?

Instead, I advocate for testing behaviors. In a tightly-integrated framework like Django, most of your tests are going to be integration tests (i.e. you have a database involved). You should bias those tests towards an integration-y approach that uses the public interfaces (Service Layer, if you have one) and asserts behavior. Ideally the tests should not change if the business logic has not changed. (In practice you'll often need to add some mapping/helper/setup code to make this true.)

If you have any fat-model type behavior that is explicitly scoped to a single model, then you can test that in isolation. Many Django projects call these "unit tests" even though they still involve reading and writing your model from the DB. I call them "focused integration tests". All that matters is that you have agreement on terminology inside your project. If you have extremely complex domain logic, it can be worthwhile to construct "true Unit Tests" that use dummy objects to test logic without hitting your DB. I've not found it worthwhile to mock the DB in most Django projects though.

To provide an example of where my "test behaviors not classes" advice differs from the OP's paradigm, let's say you split out a sub-object to provide a pluggable Strategy for a part of your Model's behavior -- you don't necessarily need to have detailed tests for that Strategy class if it's fully covered by the model's tests. Only the edge cases that are awkward to test at the higher level need to be tested at the granular level of the Strategy. Indeed, the first refactor that just creates a Strategy holding your existing behavior need not change any of your existing tests at all! Indeed, if you do need to change existing tests, that suggests your tests were improperly-coupled to the code under test, since a mere structural change like this should not affect the behavior of your application. Even after adding more Strategy logic, most of your old ModelTests are still good; they still test the high-level behavior, and now also test the integration between your model and the new Strategy class. Basically, test at the most-granular level that gives a clear, decoupled test for your behavior; resist testing every entity in isolation, because some entities have rich coreographies with other entites that make them hard to isolate. Sometimes you have to contort and tightly-couple in order to test things at the very-lowest-level possible.

Inspiration/further reading: https://blog.cleancoder.com/uncle-bob/2017/10/03/TestContrav.... (Grit your teeth through the "Socratic dialog" style. The principle being described is extremely valuable.)

Re: Django Styleguide

#87
post #84

Earlier quoted context omitted.

So, the way I understand it, job/services.py and load/services.py depend on mediator/mediator.py which depends on job/models.py and load/models.py , instead of job/services.py ultimately using load/services.py , and vice versa. Thanks so much!

This is good enough to break circular dependencies between individual modules, but keep in mind that circular dependencies between the apps remain (e.g. job depends on mediator, mediator depends on job). I usually prefer to resolve those too. If job is small enough, all the orchestration of jobs should happen through mediator (same for load). If it's not plausible, then job can emit signals which mediator subscribes…

> ...all the orchestration of jobs should happen through mediator (same for load)

So, in a smaller app, when a request comes into job/views.py or load/views.py then we immediately start working with JobLoadMediator to handle business logic between the two? I was just going to focus on specific tasks between job and load. I'll probably look into signals; I haven't used those in several years and as I recall, it felt hacky.

> give a more descriptive name to the mediator

When a Job is deleted, it needs to delete associated Loads. And the states of the Loads will affect the state of the Job. That's the main cycle I'm looking at right now.

Re: Django Styleguide

#88
post #80
post #37

Earlier quoted context omitted.

"Also be wary of the formerly South, now built-in migrations stuff. It's built around a fragile model (perfect history representation), so has lots of foot guns." My experience has been opposite so I'd be interested in hearing your experiences if you are willing. I have been using the built in migrations since day 1 on a medium sized Django project with 350+ migrations and migration issues for our project have been e…

Diamond-dependencies are hard to work with, and so we forbade them. Specifically, you can't revert an individual migration, you just specify your desired "target" to roll back to, and therefore you can't unapply a single branch of a diamond dependency. This means if your second branch of a diamond dependency breaks the DB, but your app depends on the first branch, you're SOL and are now manually running SQL to fix yo…

Great answer thank you!

Re: Django Styleguide

#89
post #17

I think there are too many concepts in this, but rather than being negative, here are some tips: Always keep your models slim. Don't stuff template related stuff in there. You need to look at those models often, so compact is a win. course_has_finished(course) is not much longer than course.has_finished(), and will allow you to expand the functionality as time goes on. Do precomputation if you need the information in…

> Always keep your models slim. As simple as possible, but no simpler. Django models are meant to deal not just with the data, but also with business logic. If `course.has_finished` is a property of the course, why would you want to have a separate function outside of the class? > Do precomputation if you need the information in a template If the precomputation is only needed in a template, you can (should, IMO) use…

> If `course.has_finished` is a property of the course, why would you want to have a separate function outside of the class?

Because one should avoid passing Django models around. It leads to bad design. Have a selector or something that uses the ORM, but exposes some dataclass or pydantic model instead, and put the logic there.

Re: Django Styleguide

#90
post #17

I think there are too many concepts in this, but rather than being negative, here are some tips: Always keep your models slim. Don't stuff template related stuff in there. You need to look at those models often, so compact is a win. course_has_finished(course) is not much longer than course.has_finished(), and will allow you to expand the functionality as time goes on. Do precomputation if you need the information in…

Always keep your models slim. Don't stuff template ...course_has_finished(course) is not much longer than course.has_finished()

I disagree with this. When trouble shooting or expanding code it is super convenient to import a model and have all of your methods on auto complete. Especially when you need the same functionality in a view, a cron job, a celery task, and an DRF end point.

If you want to keep it clean you can put all your methods in a mixin class and import it from another file.

Also be wary of the formerly South, now built-in migrations stuff.

Things are much better than they were in South. But yes be careful, rule of thumb is always move forward.

Don't use class-based views

Please. For the love of God, always use class based views for almost everything. Almost everything you need is a variant of one of the built in class based views, don't make me read your copy/pasted reimplementation of it.

Post reply on HN