Milano
Milano

Reputation: 18735

How to avoid dependency injection in Django?

I'm trying to figure out how to avoid dependency injection in my project. There is a file notifications.py in app directory.

File notifications.py contains methods for sending emails to admin and users. To get admins email, I need to check object of SystemData model. But in models, I use notifications.

models

class SystemData(models.Model):
    admin_alerts_email = models.EmailField(verbose_name=u'Emailová adresa admina')
    contact_us_email = models.EmailField(verbose_name=u'Adresa kontaktujte nás')
    waiting_threshold = models.PositiveSmallIntegerField(verbose_name=u'Maximálny počet minút čakania')

class SomeModel(models.Model):
    ....
    def save(...):
        notifications.send_message_to_admin('message')

notifications.py

from django.core.mail import EmailMessage
from models import SystemData

def send_message_to_admin(message):
    mail = EmailMessage(subject, message, to=[SystemData.objects.all().first().admin_email])
    mail.send()

Django returns that it can't import SystemData.

Do you know what to do?

EDIT:

stacktrace

stacktrace

Upvotes: 2

Views: 905

Answers (2)

loleknwn
loleknwn

Reputation: 113

Apart from using circular imports you can either do it like that:

from django.core.mail import EmailMessage
from django.db.models.signals import post_save
from django.dispatch import receiver

from .models import SystemData, SomeModel


@receiver(post_save, sender=SomeModel)
def send_message_to_admin(sender, instance, created, **kwargs):
    message = 'message'
    mail = EmailMessage(
        subject, 
        message, 
        to=[SystemData.objects.all().first().admin_email]
    )
    mail.send()

and at the end of models.py put

from .notifications import *

or use newest approach with AppConfig to register signals (that's what your notifications actually do)

see: https://chriskief.com/2014/02/28/django-1-7-signals-appconfig/

that way it will load when app registry is ready and you'll avoid circular imports, so that line:

from .notifications import *

can be dropped from models.py

AppConfig can be used in a more generic way as well allowing you to import models like that:

from django.apps import apps
Model = apps.get_model('app_name', 'Model')

Upvotes: 2

knbk
knbk

Reputation: 53679

You can solve circular dependencies in functions by using inline imports:

class SomeModel(models.Model):
    ....
    def save(...):
        from .notifications import send_message_to_admin
        send_message_to_admin('message')

This will delay the import statement until the function is actually executed, so the models module has already been loaded. The notifications module can then safely import the models module.

Upvotes: 4

Related Questions