Django redis cache cannot access redis cache set outside of django - python

I'm setting up redis as a docker service and connect to it through django-cache and python redis library.
First:
from django.http import HttpResponse
from django.core.cache import cache
def test_dj_redis(request):
cache.set('dj_key', 'key_in_dj', 1000)
print(cache.get('dj_key')) # works
print(cache.get('py_key')) # Nope
print(cache.get(':1:py_key')) # No, tried reverse engineer the prefix
return HttpResponse("bla bla bla")
Second:
import redis
r = redis.Redis(
host='redis',
port=6379
)
def cache_it():
r.set('py_key', 'key in py', 1000)
print(r.get('py_key')) # works
print(r.get(':1:dj_key')) # works
print(r.keys()) # show two keys
If I run both of them
first one by refresh the web page related to that django view
second one by python cache_it().
In first, I cannot access 'py_key', it will return None.
But second, I can see cache set in django view. Django cache added a prefix to it and turn 'dj_key' into ':1:key_in_dj', but I can access it nonetheless.
Also in second, the redis_r.keys() return [b'py_key', b':1:key_in_dj'].The value of the key 'py_key' remained the same.
django cache setting
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://redis:6379',
},
}
Question, how do I use django.core.cache to access redis cache set outside of Django.

Related

How can I write a script to monitor changes in database? ImproperlyConfigured: settings.DATABASES Error

I am using Django and have a skeleton website with a single button that increments an IntegerField in my database. I want to have an external script which continually reads this IntegerField and reports when a change has been made.
models.py
class Counter(models.Model):
count = models.PositiveIntegerField(default=0)
views.py
def index(request):
ctr = Counter.objects.get(id=1)
pre = ctr.count
print(f"pre: {pre}")
if request.method == 'POST':
if request.POST.get("increment"):
Counter.objects.filter(id=1).update(count=F('count') + 1)
post = ctr.count
print(f"pre: {pre}")
return render(request, 'increment.html')
I thought I could simply achieve this inside the view function but I've had difficulty detecting a change because the pre & post values above are the same when I print them even though post is clearly created after the field is incremented.
I have a watcher.py file inside my app directory where I am trying to program a script that would run in the background and report changes to the database. Here's what I have so far:
from django.conf import settings
settings.configure(DEBUG=True)
import django
django.setup()
import models
import time
from ahelper import settings
from threading import Thread
def watcher():
ctr = models.Counter.objects.get(id=1)
pre = ctr.count
print(f"{pre}")
print("start loop")
while (1):
temp = time.time()
post = ctr.count
if pre != post:
print("Change detected")
response_time = (time.time() - temp) * 1000
print(f"{response_time}ms")
watcher()
When I try to run this, I get the error: ImproperlyConfigured: settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details. I don't understand why this comes up and I don't know how to resolve it; The engine value is provided in the settings file already.
settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
I am very lost at this point so any help is appreciated. Am I going about this the right way? Is there an easier way to do this?
You must run this script in a django app. This could be done in different manner, but a way more simple one would be to use the before/post save signal of Django, so you know when this field is modified: https://docs.djangoproject.com/en/4.1/ref/signals/#django.db.models.signals.post_save
If you still want to use a dedicated runner, you can create a thread/process that run aside Django. Still you have one issue in your code,
post = ctr.count
This will not reload the object state from the database. You need to reload the object manually to ensure it is uptodate. Other way you would not detect any changes:
ctr.refresh_from_db()
post = ctr.count

How to use one db to run the django service and other to fetch data

i have an existing application data base from which my web site should only fetch the data according to user input. Added database details in the settings.py file and I tried python manage.py integratedb and get the all the 300+ tables came off to my models.py file. I was never able to do python manage.py runserver it threw a million errors. Now i found a work around but i need your opinion on this.
I added the default server into the settings.py and using it i was able to run the server. settings.py looks like this.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'mydatabase',
},
'user': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME' : 'test',
'USER' : 'testuser',
'PASSWORD': 'readonly',
'HOST' : '10.20.30.40',
'PORT' : '5446',
}
}
Now can i access the user db to fetch data from my form? for example
views.py looks like this
from django.shortcuts import render_to_response
from .models import TestCases
from django.shortcuts import HttpResponse
from django.http import JsonResponse
from django.forms.models import model_to_dict
from django.views.decorators.csrf import csrf_exempt
# Create your views here.
#csrf_exempt
def index(request):
posts = TestCases.objects.all()[:10]
return render_to_response('index.html',{'posts':posts})
where TestCases is a class name from the models.py file.
Now when i click the button to retrieve data i get "no such table: test_cases"
models.py looks like
class TestCases(models.Model):
id = models.BigIntegerField(primary_key=True)
clientid = models.ForeignKey(Clients, db_column='clientid')
projectid = models.ForeignKey(Projects, db_column='projectid')
class Meta:
managed = False
db_table = 'test_cases'
what am i doing wrong to get the data from the user input. please help.
Queryset .using() method
I guess Django is going for the default database.
Try this:
#csrf_exempt
def index(request):
posts = TestCases.objects.using('user').all()[:10]
return render_to_response('index.html',{'posts':posts})
When you set using to your queryset, you can specify which database django is going to query.
More information in Django docs
A better approach, so you don't have to manually set it to all your queries.
You can manually add it to all your queries, or you can create a custom manager to your objects and force it to use a specific database for objects.
Ex:
# this will override the default queryset for objects that use this Manager
class UserDatabaseManager(models.Manager):
def get_queryset(self):
return super().get_queryset().using('user')
class MyModel(models.Models):
objects = UserDatabaseManager()
Then, when you use MyModel, you can query as usually and Django will use the user db for default just for models that have objects = UserDatabaseManager().
Keep in mind that this is just a simple use of the Manager, you can have multiple managers and do a lot of cool stuff.
Take a look at the Django Managers docs
first of all you need to do:
python manage.py inspectdb > models.py
to store your models to models.py
By default, inspectdb creates unmanaged models. That is, managed = False in the model’s Meta class tells Django not to manage each table’s creation, modification, and deletion, if you do want to allow Django to manage the table’s lifecycle, you’ll need to change the managed option above to True (or simply remove it because True is its default value).
Next, run the migrate command to install any extra needed database
python manage.py migrate
integrating docs

Django: proper way to reload template library

I am using custom template tags to show site-specific content. Templates for these tags are stored in database and staff is able to edit them. I used that SO answer as reference. Each time after template changed and saved into database, I force reloading of django app (by reloading uwsgi service) or changes doesn't become visible. Looks like Django populates template library for custom tags only on first load of app.
I think reloading Django app isn't best practice to refresh DB templates, and I am searching way to do it programmatically (from model's save() or on save signal)
Template tags code:
from django.template import Template, Library
from project.custom_templates.models import DbTemplate
register = Library()
# template names
SHOW_PART_TABLE_ADMIN = 'custom/tags/show_part_table_admin.html'
SHOW_PART_TABLE_PUBLIC = 'custom/tags/show_part_table_public.html'
t_show_part_table_admin = Template(DbTemplate.objects.get(name=SHOW_PART_TABLE_ADMIN).content)
t_show_part_table_public = Template(DbTemplate.objects.get(name=SHOW_PART_TABLE_PUBLIC).content)
#register.inclusion_tag(t_show_part_table_admin)
def show_part_table_admin(parts, user, perms):
return {'parts': parts, 'user': user, 'perms': perms}
#register.inclusion_tag(t_show_part_table_public)
def show_part_table_public(parts, user, perms):
return {'parts': parts, 'user': user, 'perms': perms}

Dynamic settings.py

I have used django-constance as a library.
Although one thing I notice is that when I tried using ADMIN and MANAGER
CONSTANCE_CONFIG = {
'ADMINS': ([('Errors', 'admin#gmail.com')], 'Admin Emails'),
}
sending of emails is not working.
In MANAGER I have tried this:
MANAGER = CONSTANCE_CONFIG['ADMINS'][0]
still sending emails is not working. Am I missing a wrong implementation?
Or can you suggest any other library which can override ADMIN and MANAGER in settings.py. I am using Django 1.8.5 and Python 3.
also when trying to import inside settings.py it produces error as well.
1# Probably you already know, django-constance does not support tuple. Basically it is really hard to detect a widget for tuple specially
in your case. ADMINS can be added/deleted so how possibly you can make it dynamic through a single widget..!!(think about all django widgets). So here
CONSTANCE_ADDITIONAL_FIELDS will also not work.
2# I think you are misunderstanding the working of django constance.
It does not refresh your django server. So MANAGER = CONSTANCE_CONFIG['ADMINS'][0] is totally wrong(even using CONSTANCE_ADDITIONAL_FIELDS). You accessing constant value here(not dynamic).
You need to access it like
from constance import config
print(config.ADMINS)
3# Default logging config uses AdminEmailHandler class for mail_admins, which uses ADMINS value from django settings, not constance config.
So one possible solution might be to create your own handler class which will use ADMINS value from constance config. So change your setting.py to
CONSTANCE_CONFIG = {
'ADMIN1': ('admin#gmail.com', 'This one will receive error on 500'),
} # you can add as many admins as you want with ADMIN1, ADMIN2 etc(no tuple)
then create your own handler class which will use CONSTANCE_CONFIG.
from django.utils.log import AdminEmailHandler
from constance import config
from django.conf import settings
from django.core.mail.message import EmailMultiAlternatives
class ConstanceEmailHandler(AdminEmailHandler):
def send_mail(self, subject, message, html_message=None, fail_silently=False, *args, **kwargs):
# create a list of ADMIN emails here, if you have more then one ADMIN
mail = EmailMultiAlternatives('%s%s' % (settings.EMAIL_SUBJECT_PREFIX, subject),
message, settings.SERVER_EMAIL, [config.ADMIN1],
connection=self.connection())
if html_message:
mail.attach_alternative(html_message, 'text/html')
mail.send(fail_silently=fail_silently)
And then change your LOGGER config. I would recommend you to copy default logger config from django.utils.log(DEFAULT_LOGGING) if you do not have your custom LOGGING setup. And change mail_admins to
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'], # change it to require_debug_true if you want to test it locally.
'class': '<yourproject>.<yourfile>.ConstanceEmailHandler', # path to newly created handler class
'include_html': True
},

Password protect a whole django app

I am running a simple staging env on heroku and I am now looking to password protect the whole app with some sort of simple authentication
I am wondering if there is a simple app or middleware that already supports this.
Have tried looking around for solutions with Heroku / Cloudflare and django, but nothing seems really straight forward.
Django 1.3.1
I use django-lockdown for exactly this purpose. It allows you to add a simple password over the whole of a dev site, without having to add in any extra auth bits on your views that aren't used outside of a dev environment. It also means you can login as admin, or regular users to test whatever your site does
https://github.com/Dunedan/django-lockdown
I use Heroku and Lockdown with this bit of code in my settings.py file
USE_LOCKDOWN = os.environ.get('USE_LOCKDOWN', 'False') == 'True'
if USE_LOCKDOWN:
INSTALLED_APPS += ('lockdown',)
MIDDLEWARE_CLASSES += ('lockdown.middleware.LockdownMiddleware',)
LOCKDOWN_PASSWORDS = (os.environ.get('LOCKDOWN_PASSWORD', 'False'),)
LOCKDOWN_URL_EXCEPTIONS = (r'^/some/url/not/locked/down/$',)
Then obviously set a config var of USE_LOCKDOWN as True on my dev site, and False on my production site so no need to change the code for either.
Django's authentication framework has built-in utilities #login_required which helps you to password protect your view functions (and its corresponding "url" of course).
Usage like this:-
from django.contrib.auth.decorators import permission_required, login_required
#login_required
def view_organization(request, org_slug):
"""
Only permit a logged in user to view the organization.
"""
org = get_object_or_404(organization, slug=org_slug)
org_users = organizationuser.objects.filter(organization=org,\
organization__is_active=true)
template = 'organizations/view_organization.html'
template_vars = {'org_users': org_users, 'org': org}
return render(request, template, template_vars)
For advanced access control, use #permission_required decorator.

Categories