4 ms·
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 bias
by wbc 5y ago
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 https://github.com/django-extensions/django-extensions
- piyh 5y agoI'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.
- Daishiman 5y agoI personally keep display functions away from business objects. You could do with a mixin, or a helper class.
- rajin444 5y agoIf 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/ 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.
- lastofus 5y agoSounds 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.
- blackrobot 5y agoAre 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.
- olau 5y agoIt 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.
- rajin444 5y agoIsn'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".
- rajin444 5y agoThat 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".
- mtoddsmith 5y agoKeeping 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).
- bebop 5y agoThis is hugely important when you need to do a migration of your data store.
- autorun 5y agoin fact, for urls you can also use resolve() helper