Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: how to fully decouple apps when it seems they are coupled? [closed]

Tags:

Note: I am not a proper python programmer... but I use python extensively. I do things like write classes with inheritance, use iterators and comprehension, etc. My point is that I do not have a full grasp of the language, e.g. what exactly constitutes an python object, why __init__.py is needed other than to specify a module, etc. In relation to Django, I have written multi-app sites (with the help of S.O.) and have really enjoyed Django's templating system, blocks, and how they can be nested. Now are my apps fully decoupled and reusable? That this is subject of this post.

I state this disclaimer because a lot of the Django resources seem to assume that one knows these things. This makes understanding some of the documentation and S.O. questions difficult for a person who is just an (subpower)-user. So please answer this question with that in mind.

Question

These questions are inspired by both the question When to create a new app with startapp in django? by @håkan and the answer given by @antti rasinen which links to James Bennett's 2008 PyCon presentation

A few key points from Bennett's presentation are:

  1. sites are a collection of apps
  2. an app does one thing and one thing well

Which directs me to his section "Project coupling kills re-use" that mentions:

  • Single module directly on Python path (registration, tagging, etc.)
  • Related modules under a package (ellington.events, ellington.podcasts, etc.)

Question 0

A "module" in this case is just an app made of other apps?

Question 1

(Apps with related functionality and shared models )

What should I do when apps share models?

In Barrett's slides he implies that user registration and user profiles are distinct and should be distinct apps. (He certainly states that profiles have nothing to do with user registration).

So if I wanted both, would my project have two apps like:

  • user-registration
  • user-profile

even though the app user-profile will need the user model from user-registration? Or do I make a single app (module):

  • user-app
    • registration
    • profile

which contains both?

Question 2

(Apps with distinct functions but shared models)

Extending the example from question 1, lets say that my main app (or some other app that is used by the main app) utilizes some aspect of the user model (e.g. recently active members if it was a chat site).

Clearly my main app gets this information from the user model. Does my main app now get bundled under the user-app module?

This may not be the best example, but the point is as follows:

I have two apps app-dependency and app-needs-dependency, where each app does its one thing and one thing well... It is just that app-needs-dependency needs information from app-dependency. What do I do in this case, if everything else about app-needs-dependency is completely decoupled from app-dependency (so it could be used in other projects)?

Question 3

(writing apps for flexibility)

Now I have my site with its couple of apps. Each app does its one thing and does it well. The main app serves as the landing page/ overview in this case.

I want all my other apps to use / inherit the static and template files of the main app.

Where do I store all the static files and templates? In the main app and set that as the default for the other apps? Or where should these static files / templates (e.g. base.css, base.html) go? Do I make a copy of these files for each other app, so they can be run even though this is redundant?

Which makes my app more flexible?

like image 959
SumNeuron Avatar asked Feb 16 '18 17:02

SumNeuron


2 Answers

Question 0

A "module" in the Python context is simply a file that contains definitions and statements. So "related modules under a package" really just means "split your code into separate files based on what the code is doing".

Describing it as "an app made of other apps" is to start confusing Django's concept of an app with Python's concept of a module (which, as stated above is just a file that houses some code).

Question 1

What should I do when apps share models?

You should still try and stick to the "apps do one thing and do it well" maxim. In this case separate profile and registration apps seems like a good idea - because they have quite different functions. A registration app is going to contain the logic for allowing users to register on your site. A profile app is all about what information you will store about a user.

There is nothing wrong with these two apps having a relationship to each other - see below.

Question 2

Let's say that my main app (or some other app that is used by the main app) utilizes some aspect of the user model (e.g. recently active members if it was a chat site). Clearly my main app gets this information from the user model. Does my main app now get bundled under the user-app?

No. It should still be a separate app, with links to the other app.

The user model is actually a good example. Django allows you to specify a custom user model that lets you store whatever additional data you want about a user.

Now, there are loads of third party apps out there that do things like registration, authentication, etc for users. They are designed to work with any user model, not just Django's default one. The way they do that is to use get_user_model() wherever they need to reference the User model, instead of directly importing django.contrib.auth.models.User.

This means that you can use those third party apps with whatever user model you have defined for your own project.

Django's get_user_model() utility is there to serve a very common use case. However the same principle can be extended to your own apps. If there is a dependency between your apps that you think should be swappable, then you can provide a way to swap it out - e.g., a setting/configuration that allows any other project using your app to specify an alternative.

There are hundreds of examples of this kind of configurability in the Django ecosystem. For example, Django itself ships with its own django.contrib.auth authentication app. However if you want to implement your own authentication logic, you don't have to reimplement the entire auth app again yourself (that would be a huge pain). Instead you specify an an authentication backend that it's auth app will use to authenticate. The auth app is designed to allow any project to swap out a core piece of its functionality with minimal effort.

So in your main app, you might define a setting that controls which profile model to use. This means that if someone else wants to use a different profile model, they simply change this setting and they're all set. They are no longer tied to your profile app.

For example - let's say you have a main app that has a view that displays some user data, but also provides a link to a registration view that is provided by a different app. You want anyone else to be able to use that app regardless of what registration app they are using. So you can make this view resuable like so:

In main/views.py:

from django.contrib.auth import get_user_model
from django.conf import settings
from django.urls import reverse

class UserDetailView(DetailView):

    # First of all, we're using get_user_model so that a project
    # can specify whatever user model it wants, and still use this
    # view.
    model = get_user_model()

    def get_context_data(self, *args, *kwargs):
        ctx = super().get_context_data(*args, **kwargs)
        # We want to add a link to a registration view into this template context.
        # But we want this to be configurable.
        # Your REGISTRATION_URL would be something like 'profile:registration'
        ctx['registration_link'] = reverse(settings.REGISTRATION_URL)
        return ctx

Question 3

The main app serves as the landing page/ overview in this case. I want all my other apps to use / inherit the static and template files of the main app. Where do I store all the static files and templates?

You should store the templates in each respective app. If your main app is providing the base templates, then those should reside in the main app.

If your profile app is then providing a registration view, then the template for that should live in the profile app. There is nothing wrong with it extending the base template from the main app - this can easily be overridden by a project that wants to.

It's fine to make assumptions about how two apps are related to each other - as long as you're careful to allow overriding of those assumptions.

like image 131
solarissmoke Avatar answered Jan 03 '23 18:01

solarissmoke


I have to admit your question is not a technical one but rather a conceptual and dogmatic one. No answer is absolute and universally valid and every detail about how you project is structured and should behave can change the perspective. As you wrote, each Django app does one thing and it does it well.

I would extend that to the point that each app should contain no more than one Model and at most, it's closets dependents.

Ex: the Product with it's Category, Color, Image

"What Changes together, stay together"

You will have plenty of logic to cover inside that app with only these ones.

Try to look at Django framework as a tool to create your project..this is the final goal...but if you want also to create reusable apps try to create them as independent as possible, or at least dependent to some Django features:

ex: a reusable app and totally independent would be an app that only requires User Model, Sessions, Groups included in Django. You get the idea of dependent but still autonomous app.

An app is part of a project after all...either here or in other part after you build it. Look at it as if it would be a simple function...can run alone or can depend on other functions result...at what point you keep everything inside one function and when you decide to split them in 2 separate ones. So:

  • Question 0:

An app is the smallest piece that can run by it's own...having models, views, templates, urls, static files.

It can depend also on other apps...so answer is YES

  • Question 1:

Always keep things separate by functionality... User Auth is dealing with user creation and their authentication

User Profile is dealing with personal data of the User

  • Question 2:

Nothing gets bundled. Everything stays at the same level as 2 different but dependents apps

  • Question 3:

You can do as you wish.

You can do static as a central place and templates specific for each app or everything central. No right answer here but only what scales well for your project.

like image 32
Catalin Avatar answered Jan 03 '23 17:01

Catalin