Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is great stuff! after working w/ various Django apps for years (anywhere from 3 dev teams to 200 dev teams), it's great to read stuff that confirms my biases :D

Regarding services, I'll go as far as to say adding ANY method on models instead of handling logic in services is a recipe for disaster. How many times have you seen:

  class GodModel:

    @property

    def status(self):

      # 1 million lines of logic and who knows how many queries
I've actually seen this pattern in every Django project :(

Regarding urls, instead of enforcing a flat file, I'd highly recommend always using django_extensions[0]. You'll get `shell_plus` that auto imports model and `show_urls` that you can grep for endpoint and gives you the handler.

[0] https://github.com/django-extensions/django-extensions



Are there any good articles or examples you can share that elaborate on why using services is best? Writing a custom model manager method for these sorts of operations seems to work best. For instance, the create_account service could easily be part of the User.objects manager:

  class UserManager(models.Manager):
      def create_account(self, sanitized_username: str, ...):
          # the rest of the code in this method is the same as the example.
          ...
          return user_model, auth_token
  
  class User(models.Model):
      ...
      objects = UserManager()
  
  >>> User.objects.create_account(sanitized_username="blackrobot", ...)
  (<User: blackrobot>, 'fake-auth-token:12345')
The benefit here is that other parts of your code only need to import the User model to access the manager methods. It also allows for the User.objects.create_account(...) method to be used by related models, without risking a circular import, by using the fk model's Model._meta.get_field(...) method.

I'm not opposed to services, I just don't see when they'd be particularly useful.


I like your approach and I think what you’re proposing can also be fine in many situations. Managers are not the same as models and using them here is not drastically different than using a separate service class/function. Managers can be accessed through the model and they have “enough” exposure to table wide operation (querysets). I usually start with managers in a separate file (managers.py) for my business logic and when the project grows, I extract the logic into services in a way that only queryset definitions remain in the manager. You can mock manager’s methods for tests (get_queryset) and the business logic code in them can be written in a relatively portable manner.


It might be a little bit more convenient, but really, models are central to everything else. You're spamming your most central code with arbitrary crap that you are only interested in perhaps 0.1% of the time.

Once you get out of the OOP mentality, it's much easier to shuffle code around, and keeping things that logically belong together close to each other in separate files. Move the crap out of the way and enjoy the cleanliness. Less mental overhead helps you make better decisions faster.

And yes, sometimes you have to deal with a circular import, but it's not the end of the world, just decide which file is the most basic, and don't let that import other less basic files at the top level, but only inside functions. Or try to decouple the logic.


Isn't a mix of fat models and services best? Say for a user model you have first name, middle, and last name. You add a property "full_name" that joins those 3. Putting that logic in a service feels confusing and unintuitive.

On the other end, if you have a complex auth mechanism that needs to talk to several external APIs, putting that in a service feels natural. You're making remote API calls, possibly pulling in other models, and it's a clearly defined "business area".


In my opinion and experience, treating the model as anything but a way to talk to the database behind a service interface is a very slippery slope.

My service methods receive and return pure objects (pydantic or attrs) that I serialize from the models. No other part of the app gets to pass around that service’s model, updating it willy nilly, maybe saving the updates, maybe not.

The service completely hides the model and all corresponding persistence logic behind its interface.

The decoupling you achieve is worth the extra boilerplate. It’s the only way I have ever seen Django apps not become giant balls of mud.


Reading your example code and explanation already makes me hope I never have to open my debugger on this code. :)

A simple service that I explicitly import and call methods on is so much easier to understand. Hell, even if all services were global, singleton, objects with static methods that'd even be preferable.


That logic has to go somewhere right? I'm not sure what the issue is.

There's the "heuristic" of expecting a property to not be expensive to access (which django already kind of throws out the window depending on how you fetch the model), but otherwise I don't see how services fixes this. Is copy and pasting that code over to a services.py file better?

Services are very nice for dealing with python's import issue. Accessing other models from a method / property on a model is ugly. But it's very hard to structure and name services in a logical way, especially when the lines start to cross. You end up with a "shared_services.py".


Keeping the business logic in a service (aka Anemic Model in Domain Driven Design terms) allows you to change the business logic using different versions of the same service. You can then inject the appropriate implementation for a given context using IoC (Inversion of Control).


This is hugely important when you need to do a migration of your data store.


I'm at a crossroads for a hobby project I'm working on. I have a model for youtube video metadata. I have functions to do things like transform the metadata into display values to be rendered to template.

Should that logic be a function that takes a youtube metadata model object, or a class method on the model to return display values?

There are more functions of increasing complexity after this display function.


If you're not able to quickly answer it, I'd put it where you feel like you'd first look for it (where it feels natural) and move on - don't try to optimize too early. The correct answer is going to depend heavily on what your app is doing, how it's structured, etc, so providing a good answer from an HN comment is going to be really hard. It's ok to be "wrong", just make sure you're consistently wrong so when you need to refactor it's not so bad. Eventually the answer will become clear...and if it never does, you probably made the right choice and saved a lot of time :)

FWIW, https://www.django-rest-framework.org/api-guide/serializers/ might be helpful - but DRF is also a lot to take in if you're not already comfortable with Django.


I personally keep display functions away from business objects. You could do with a mixin, or a helper class.


Thanks for the things to google, I've faked it till I made it and still have some knowledge gaps to fill in.


You didn't fake it, you're making it. Whether you learned along the way or had book knowledge or experience from before isn't something anyone will care about.


Sounds like you might have a View Model.

One option would be to define this View Model that takes your Model in the constructor, then make this view model object available in the template context.


in fact, for urls you can also use resolve() helper




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

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

Search: