Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Large(ish) django application architecture

Tags:

python

django

How does one properly structure a larger django website such as to retain testability and maintainability?

In the best django spirit (I hope) we started out by not caring too much about decoupling between different parts of our website. We did separate it into different apps, but those depend rather directly upon each other, through common use of model classes and direct method calls.

This is getting quite entangled. For example, one of our actions/services looks like this:

def do_apply_for_flat(user, flat, bid_amount):
    assert can_apply(user, flat)
    application = Application.objects.create(
        user=user, flat=flat, amount=bid_amount,
        status=Application.STATUS_ACTIVE)

    events.logger.application_added(application)
    mails.send_applicant_application_added(application)
    mails.send_lessor_application_received(application)
    return application

The function does not only perform the actual business process, no, it also handles event logging and sending mails to the involved users. I don't think there's something inherently wrong with this approach. Yet, it's getting more and more difficult to properly reason about the code and even test the application, as it's getting harder to separate parts intellectually and programmatically.

So, my question is, how do the big boys structure their applications such that:

  1. Different parts of the application can be tested in isolation
  2. Testing stays fast by only enabling parts that you really need for a specific test
  3. Code coupling is reduced

My take on the problem would be to introduce a centralized signal hub (just a bunch of django signals in a single python file) which the single django apps may publish or subscribe to. The above example function would publish an application_added event, which the mails and events apps would listen to. Then, for efficient testing, I would disconnect the parts I don't need. This also increases decoupling considerably, as services don't need to know about sending mails at all.

But, I'm unsure, and thus very interested in what's the accepted practice for these kind of problems.

like image 407
Nuschk Avatar asked Mar 30 '26 20:03

Nuschk


1 Answers

For testing, you should mock your dependencies. The logging and mailing component, for example, should be mocked during unit testing of the views. I would usually use python-mock, this allows your views to be tested independently of the logging and mailing component, and vice versa. Just assert that your views are calling the right service calls and mock the return value/side effect of the service call.

You should also avoid touching the database when doing tests. Instead try to use as much in memory objects as possible, instead of Application.objects.create(), defer the save() to the caller, so that you can test the services without having to actually have the Application in the database. Alternatively, patch out the save() method, so it won't actually save, but that's much more tedious.

like image 52
Lie Ryan Avatar answered Apr 02 '26 09:04

Lie Ryan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!