Skip to content

Commit d45edea

Browse files
committed
django_q2 no longer needs to be installed even if not used
1 parent 020b6d4 commit d45edea

File tree

8 files changed

+57
-53
lines changed

8 files changed

+57
-53
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ see their [release notes](https://github.com/adamspd/django-appointment/tree/mai
6868
```bash
6969
pip install django-appointment
7070
```
71+
Optionally installing django_q2 if you need email reminders:
72+
73+
```bash
74+
pip install django_q2
75+
```
7176

7277
2. Add "appointment" (& "django_q" if you want to enable email reminders) to your `INSTALLED_APPS` setting like so:
7378

appointment/email_sender/email_sender.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,21 @@
33

44
from django.core.mail import mail_admins, send_mail
55
from django.template import loader
6-
from django_q.tasks import async_task
76

87
from appointment.logger_config import get_logger
98
from appointment.settings import APP_DEFAULT_FROM_EMAIL, check_q_cluster
109

1110
logger = get_logger(__name__)
1211

12+
try:
13+
from django_q.tasks import async_task
14+
15+
DJANGO_Q_AVAILABLE = True
16+
except ImportError:
17+
async_task = None
18+
DJANGO_Q_AVAILABLE = False
19+
logger.warning("django-q is not installed. Email will be send synchronously.")
20+
1321

1422
def has_required_email_settings():
1523
"""Check if all required email settings are configured and warn if any are missing."""
@@ -45,7 +53,7 @@ def send_email(recipient_list, subject: str, template_url: str = None, context:
4553
from_email = from_email or APP_DEFAULT_FROM_EMAIL
4654
html_message = render_email_template(template_url, context)
4755

48-
if get_use_django_q_for_emails() and check_q_cluster():
56+
if get_use_django_q_for_emails() and check_q_cluster() and DJANGO_Q_AVAILABLE:
4957
# Asynchronously send the email using Django-Q
5058
async_task(
5159
"appointment.tasks.send_email_task", recipient_list=recipient_list, subject=subject,
@@ -69,7 +77,7 @@ def notify_admin(subject: str, template_url: str = None, context: dict = None, m
6977

7078
html_message = render_email_template(template_url, context)
7179

72-
if get_use_django_q_for_emails() and check_q_cluster():
80+
if get_use_django_q_for_emails() and check_q_cluster() and DJANGO_Q_AVAILABLE:
7381
# Enqueue the task to send admin email asynchronously
7482
async_task('appointment.tasks.notify_admin_task', subject=subject, message=message, html_message=html_message)
7583
else:

appointment/settings.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,29 +37,15 @@ def check_q_cluster():
3737
logger.info("Checking missing configuration for django q cluster")
3838
# Check if Django Q is installed
3939
if 'django_q' not in settings.INSTALLED_APPS:
40-
missing_conf.append("Django Q is not in settings.INSTALLED_APPS. Please add it to the list.\n"
41-
"Example: \n\n"
42-
"INSTALLED_APPS = [\n"
43-
" ...\n"
44-
" 'appointment',\n"
45-
" 'django_q',\n"
46-
"]\n")
40+
missing_conf.append("Django Q is not in settings.INSTALLED_APPS. Please add it to the list. "
41+
"See https://django-appt-doc.adamspierredavid.com/getting-started/#installation "
42+
"for more information")
4743

4844
# Check if Q_CLUSTER configuration is defined
4945
if not hasattr(settings, 'Q_CLUSTER'):
50-
missing_conf.append("Q_CLUSTER is not defined in settings. Please define it.\n"
51-
"Example: \n\n"
52-
"Q_CLUSTER = {\n"
53-
" 'name': 'DjangORM',\n"
54-
" 'workers': 4,\n"
55-
" 'timeout': 90,\n"
56-
" 'retry': 120,\n"
57-
" 'queue_limit': 50,\n"
58-
" 'bulk': 10,\n"
59-
" 'orm': 'default',\n"
60-
"}\n"
61-
"Then run 'python manage.py qcluster' to start the worker.\n"
62-
"See https://django-q.readthedocs.io/en/latest/configure.html for more information.")
46+
missing_conf.append("Q_CLUSTER is not defined in settings. Please define it. "
47+
"See https://django-appt-doc.adamspierredavid.com/project-structure/#configuration "
48+
"for more information.")
6349

6450
# Log warnings if any configurations are missing
6551
if missing_conf:

appointment/tests/test_settings.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,9 @@ def test_check_q_cluster_with_django_q_missing(self, mock_logger, mock_settings)
2222
self.assertFalse(result)
2323
# Verify logger was called with the expected warning about 'django_q' not being installed
2424
mock_logger.warning.assert_called_with(
25-
"Django Q is not in settings.INSTALLED_APPS. Please add it to the list.\n"
26-
"Example: \n\n"
27-
"INSTALLED_APPS = [\n"
28-
" ...\n"
29-
" 'appointment',\n"
30-
" 'django_q',\n"
31-
"]\n")
25+
"Django Q is not in settings.INSTALLED_APPS. Please add it to the list. "
26+
"See https://django-appt-doc.adamspierredavid.com/getting-started/#installation "
27+
"for more information")
3228

3329
@patch('appointment.settings.settings')
3430
@patch('appointment.settings.logger')

appointment/tests/utils/test_db_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ def test_create_and_save_appointment_without_django_q(self):
241241
self.assertIsNotNone(appointment)
242242
self.assertEqual(appointment.client.email, client_data['email'])
243243
mock_logger_warning.assert_called_with(
244-
f"Email reminder requested for appointment {appointment.id}, but django-q is not available.")
244+
f"Email reminder requested for appointment {appointment.id}, but django-q is not available.")
245245

246246

247247
def get_mock_reverse(url_name, **kwargs):

appointment/utils/db_helpers.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@
1111
from urllib.parse import urlparse
1212

1313
from django.apps import apps
14+
from django.conf import settings
1415
from django.contrib.auth import get_user_model
1516
from django.core.cache import cache
1617
from django.core.exceptions import FieldDoesNotExist
1718
from django.urls import reverse
1819
from django.utils import timezone
19-
from django_q.models import Schedule
20-
from django_q.tasks import schedule
2120

2221
from appointment.logger_config import get_logger
2322
from appointment.settings import (
@@ -26,6 +25,23 @@
2625
)
2726
from appointment.utils.date_time import combine_date_and_time, get_weekday_num
2827

28+
logger = get_logger(__name__)
29+
30+
# Check if django-q is installed in settings
31+
DJANGO_Q_AVAILABLE = 'django_q' in settings.INSTALLED_APPS
32+
33+
# Check if django-q is installed as a dependency
34+
try:
35+
from django_q.models import Schedule
36+
from django_q.tasks import schedule
37+
38+
DJANGO_Q_AVAILABLE = True
39+
except ImportError:
40+
DJANGO_Q_AVAILABLE = False
41+
Schedule = None
42+
schedule = None
43+
logger.warning("django-q is not installed. Email reminders will not be scheduled.")
44+
2945
Appointment = apps.get_model('appointment', 'Appointment')
3046
AppointmentRequest = apps.get_model('appointment', 'AppointmentRequest')
3147
WorkingHours = apps.get_model('appointment', 'WorkingHours')
@@ -37,8 +53,6 @@
3753
EmailVerificationCode = apps.get_model('appointment', 'EmailVerificationCode')
3854
AppointmentRescheduleHistory = apps.get_model('appointment', 'AppointmentRescheduleHistory')
3955

40-
logger = get_logger(__name__)
41-
4256

4357
def calculate_slots(start_time, end_time, buffer_time, slot_duration):
4458
"""Calculate the available slots between the given start and end times using the given buffer time and slot duration
@@ -112,16 +126,17 @@ def create_and_save_appointment(ar, client_data: dict, appointment_data: dict, r
112126
logger.info(f"New appointment created: {appointment.to_dict()}")
113127
if appointment.want_reminder:
114128
logger.info(f"User wants a reminder for appointment {appointment.id}, scheduling it...")
115-
schedule_email_reminder(appointment, request)
129+
if DJANGO_Q_AVAILABLE:
130+
schedule_email_reminder(appointment, request)
131+
else:
132+
logger.warning(f"Email reminder requested for appointment {appointment.id}, but django-q is not available.")
116133
return appointment
117134

118135

119136
def schedule_email_reminder(appointment, request, appointment_datetime=None):
120137
"""Schedule an email reminder for the given appointment."""
121-
# Check if the Django-Q cluster is running
122-
from appointment.settings import check_q_cluster
123-
if not check_q_cluster():
124-
logger.warning("Django-Q cluster is not running. Email reminder will not be scheduled.")
138+
if not DJANGO_Q_AVAILABLE:
139+
logger.warning("Django-Q is not available. Email reminder will not be scheduled.")
125140
return
126141

127142
# Calculate reminder datetime if not provided
@@ -155,6 +170,9 @@ def update_appointment_reminder(appointment, new_date, new_start_time, request,
155170
Updates or cancels the appointment reminder based on changes to the start time or date,
156171
and the user's preference for receiving a reminder.
157172
"""
173+
if not DJANGO_Q_AVAILABLE:
174+
logger.warning("Django-Q is not available. Appointment reminder cannot be updated.")
175+
return
158176
# Convert new date and time strings to datetime objects for comparison
159177
new_datetime = combine_date_and_time(new_date, new_start_time)
160178

@@ -179,7 +197,7 @@ def update_appointment_reminder(appointment, new_date, new_start_time, request,
179197
cancel_existing_reminder(appointment.id_request)
180198

181199
# If a reminder is still desired and the appointment is in the future, schedule a new one
182-
if want_reminder and new_datetime > timezone.now() and DJANGO_Q_AVAILABLE:
200+
if want_reminder and new_datetime > timezone.now():
183201
schedule_email_reminder(appointment, request, new_datetime)
184202
else:
185203
logger.info(
@@ -195,6 +213,9 @@ def cancel_existing_reminder(appointment_id_request):
195213
"""
196214
Cancels any existing reminder for the appointment.
197215
"""
216+
if not DJANGO_Q_AVAILABLE:
217+
logger.warning("Django-Q is not available. Appointment reminder cannot be updated.")
218+
return
198219
task_name = f"reminder_{appointment_id_request}"
199220
Schedule.objects.filter(name=task_name).delete()
200221

appointments/settings.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
"django.contrib.messages",
4343
"django.contrib.staticfiles",
4444
'appointment.apps.AppointmentConfig',
45-
'django_q',
4645
]
4746

4847
MIDDLEWARE = [
@@ -150,13 +149,3 @@
150149
ADMINS = [
151150
(os.getenv('ADMIN_NAME'), os.getenv('ADMIN_EMAIL')),
152151
]
153-
154-
Q_CLUSTER = {
155-
'name': 'DjangORM',
156-
'workers': 4,
157-
'timeout': 90,
158-
'retry': 120,
159-
'queue_limit': 50,
160-
'bulk': 10,
161-
'orm': 'default',
162-
}

setup.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,4 @@ install_requires =
2727
phonenumbers==8.13.42
2828
django-phonenumber-field==8.0.0
2929
babel==2.15.0
30-
django-q2==1.6.2
3130
colorama~=0.4.6

0 commit comments

Comments
 (0)