How to avoid dependency injection in Django? - python

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

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.

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')

Related

Django Signals - creating folder on model create

I got a model where I would like to create a folder when a model is created:
The model:
class Drive(models.Model):
user = models.ManyToManyField(User)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
path = models.CharField(max_length=150, editable=False,
default='C:/Users/User/Desktop/Python/RavNet/media/storage/drives/{}'.format(str(id)))
def save(self):
super().save()
I am trying to use signals, but I must admit this is my first ever attempt at making a signal not following a tutorial on point, and even after reading the documentation I am having a tough time.
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Drive
import os
#receiver(post_save, sender=Drive)
def create_drive(sender, instance, created, **kwargs):
if created:
os.mkdir(Drive.path)
Nothing happens when I create a new drive model via the django admin. I have tested out my code in the shell and using a placeholder path (C:/Users/User/Desktop/Python/RavNet/media/storage/drives/test) that I know works in the signal in a try to debug, and have gotten as far as realising I am having two issues.
The first: When calling the Drive.path in the shell, I am getting the path:
'C:/Users/User/Desktop/Python/RavNet/media/storage/drives/<django.db.models.fields.UUIDField>'
Instead of a path with the actual id as I hoped for. How do I solve this?
Secondly, my signal isn't working. It's like it isn't getting called. What am I doing wrong?
You must import your signals in app.py. Please check your app.py. It must be like this:
from django.apps import AppConfig
class YourAppConfig(AppConfig):
name = '...'
def ready(self):
import your_project.your_app.signals
Also your app/__init__.py must include this code:
default_app_config = 'your_project.your_app.apps.YourAppConfig'
In case you have many signals and don't want the app.py defined in your app.
you could do so:
signals are in folder app/signals/__init__.py signal1.py signal2.py
import all files in __init__.py
from .signal1 import *
from .signal2 import *
from app.signals import * inside of app/views/__init__.py

Using Django signals to identify a present user session

i want to use django signals to identify if a user is logged-in twice and if so, revoke his first session so that only one user session can be present at once.
i used the following example but it seems that my signals.py does not even gets reconcnized and i dont know why.
Example:
How to prevent multiple login in Django
accounts/signals.py
from django.contrib.auth import user_logged_in
from django.dispatch.dispatcher import receiver
from django.contrib.sessions.models import Session
from .models import UserSession
#receiver(user_logged_in)
def remove_other_sessions(sender, user, request, **kwargs):
# remove other sessions
Session.objects.filter(usersession__user=user).delete()
# save current session
request.session.save()
# create a link from the user to the current session (for later removal)
UserSession.objects.get_or_create(
user=user,
session=Session.objects.get(pk=request.session.session_key)
)
accounts/models.py
# Model to store the list of logged in users
class UserSession(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
session = models.OneToOneField(Session, on_delete=models.CASCADE)
accounts/apps.py
from django.apps import AppConfig
class AccountsConfig(AppConfig):
name = 'Accounts'
def ready(self):
import Accounts.signals
but for some reason nothing gets written onto the database for that table.
do i maybe miss something here, this is the very first time i get in touch with signals so i might missed something at the configuration.
Try this,
default_app_config = 'Accounts.apps.AccountsConfig'
Add this line in __init__.py file of your apps.py file directory.

Django signals duplicate with unique dispatch id

I have a problem with duplicate signals. I have looked-up the relevant part of Django docs and a similar question on Stackoverflow but I still can't get it working right - i.e. the action that I have planned (creation on an ActivityLog entry) is actually happening 4 times :/
I have added the dispatch_uid and the problem still persists, so I guess I'm doing something wrong. Can you please hint me to the error?
Here's my code:
signals.py
from patient.models import Patient
from .models import ActivityLog
#receiver(pre_save, sender=Patient)
def create_new_patient(sender, instance, **kwargs):
ActivityLog.objects.create(
user=instance.created_by_user,
event='created a new patient',
patient_id=instance.patient_id
)
and this is it's usage in the patient.apps module:
from django.apps import AppConfig
from django.db.models.signals import pre_save
app_name = "patient"
class PatientConfig(AppConfig):
name = 'patient'
verbose_name = "Patients"
def ready(self):
from activity_logger.signals import create_new_patient
print('Patient APP is ready!')
pre_save.connect(create_new_patient, sender='patient.Patient', dispatch_uid='patient')
The print Patient APP is ready! does appear twice, and the object gets created 4 times, despite setting the dispatch_uid. What have I misunderstood?
The #receiver(Signal,...) decorator is a shortcut for Signal.connect(...), so you indeed register your create_new_patient handler twice (once thru #receiver when importing your signals module, the second time with pre_save.connect().
Solution : in your App.ready() method, you should just import your app's signal.py module. This will trigger the registration of the handlers decorated with #receiver.

How to add the last_login_ip, when the user login if using `rest-auth`?

How to add the last_login_ip, when the user login if using rest-auth?
Befor ask the post I searched one post bellow:
How can I do my logic in `http://127.0.0.1:8000/rest-auth/login/` of `django-rest-auth`?
The answer is perfect for custom verify the login params.
But, how about after login, then add attribute to the User?
I mean, the answer is just for verify the data, I want to after user login success, I want to add the last_login_ip to the user instance.
EDIT
Please be attention, I know how to get the remote IP who visit my site. I mean I use the rest-auth to login, and customized LoginSerializer like the link. The link is for verify the login data, I can not to change the last_login_ip data in the LoginSerializer, I want when the user login success, then change the user's last_login_ip. but where to write my change last_login_ip code?
EDIT-2
I follow the #at14's advice, in the init.py (as the same level as apps.py):
default_app_config = '管理员后台.用户管理.qiyun_admin_usermanage.apps.QiyunAdminUsermanageConfig'
and in the apps.py:
from django.apps import AppConfig
class QiyunAdminUsermanageConfig(AppConfig):
name = 'qiyun_admin_usermanage'
def ready(self):
import 管理员后台.用户管理.qiyun_admin_usermanage.api.signals
There will get bellow error:
django.core.exceptions.ImproperlyConfigured: Cannot import 'qiyun_admin_usermanage'. Check that '管理员后台.用户管理.qiyun_admin_usermanage.apps.QiyunAdminUsermanageConfig.name' is correct.
and I also tried to comment the default_app_config = '管理员后台.用户管理.qiyun_admin_usermanage.apps.QiyunAdminUsermanageConfig' in the init.py, but still not work.
Use django's user logged in signal to save this information into the database.
In signals.py,
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver
#receiver(user_logged_in)
def user_logged_in_callback(sender, user, request, **kwargs):
// Get the IP Address and save it
Read more about signals here.
Do pay attention on how to import signals in your app ready method
Make an apps.py (it might already exist) in your app directory,
from django.apps import AppConfig
class YourAppConfig(AppConfig):
name = '管理员后台.用户管理.qiyun_admin_usermanage'
def ready(self):
import 管理员后台.用户管理.qiyun_admin_usermanage.api.signals # noqa
In your apps init.py file,
default_app_config = '管理员后台.用户管理.qiyun_admin_usermanage.apps.QiyunAdminUsermanageConfig'

Django 1.9 how to import in __init__.py

I've updated from Django 1.8 to 1.9.
apps/comment/__init__.py (in 1.8)
from .models import Mixin
In Django 1.9 this no longer works but I still want to import Mixin in the same way.
So I tried this:
apps/comment/__init__.py
default_app_config = 'comment.apps.CommentConfig'
apps/comment/apps.py
# Django imports.
from django.apps import AppConfig
class CommentConfig(AppConfig):
name = 'comments'
def ready(self):
"""
Perform initialization tasks.
"""
from .models import CommentMixin
This however, does not appear to work, i.e. I cannot do from comment import Mixin, why?
Adding from .models import CommentMixin imports CommentMixin so that you can use it inside the ready() method. It does not magically add it to the comment module so that you can access it as comments.CommentMixin
You could assign it to the comments module in the ready() method.
# Django imports.
from django.apps import AppConfig
import comments
class CommentConfig(AppConfig):
name = 'comments'
def ready(self):
"""
Perform initialization tasks.
"""
from .models import CommentMixin
comments.CommentMixin = CommentsMixin
However I would discourage you from doing this, you might end up with hard-to-debug import errors later on. I would just change your imports to from comment.models import CommentMixin.

Categories

Resources