Where to instantiate a client for an external service in Django? - python

What is the accepted practice in Django for creating an instance of a client for an external service (e.g. Zookeeper, Cassandra, Redis, etc.)?
I need this client to perform a "set key" operation from inside a view and I don't want to create the client on each request due to high overhead.
Currently I've declared it as a global variable in views.py, but that's not really ok, because it gets instantiated when I do python manage.py makemigrations too.
A really stripped down example of the issue:
urls.py
from django.conf.urls import url
from app.views import MyView
urlpatterns = [
url(r'^', MyView.as_view()),
]
views.py
from django.shortcuts import render
from django.views import View
from django.http import HttpResponse
from kazoo.client import KazooClient
import logging
logging.basicConfig(level=logging.DEBUG)
zk = KazooClient(hosts="127.0.0.1:2181")
zk.start()
# Create your views here.
class MyView(View):
def get(self, request):
value, _ = zk.get("/my_zk_key")
return HttpResponse("value: {}".format(value))
The problem is that the zookeeper client gets created and connects in cases other than runserver, like this:
$ python manage.py makemigrations
INFO:kazoo.client:Connecting to 127.0.0.1:2181
DEBUG:kazoo.client:Sending request(xid=None): Connect(protocol_version=0, last_zxid_seen=0, time_out=10000, session_id=0, passwd=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', read_only=None)
INFO:kazoo.client:Zookeeper connection established, state: CONNECTED
No changes detected
$
I'm looking for an accepted practice in Django for dealing with such cases.

If your problem can be reduced to the last point, you can avoid setting your global variable during the migration with:
import sys
if 'makemigrations' not in sys.argv and 'migrate' not in sys.argv:
# variable setting here

Related

Using Django ORM in plain python script / infinite task

I have application in plain python with some basic libraries. I'm looking to use Django as main framework for this application and maintain frontend.
My question is, can I still use my already written application and switch to Django ORM and maybe controlling the application via some callbacks. This application is running infinitely with asincio library.
Or is there any way how to run background tasks in Django? I found celery but I'm not sure about starting this process. Maybe use of some Django commands and supervisor.
TLDR: I need some way of running background task (command, controller, python script) infinitely long without user interaction.
You can do that with Django Background Tasks.
Define your background-task as a function with the decorator #background.
Since you want your task to run infinitely, call it in your main urls.py and just set
repeat_until = None, then it will repeat infinitely.
Copied Example from the linked tutorial:
from django.shortcuts import render,HttpResponse
from background_task import background
# Create your views here.
#background(schedule=5)
def hello():
print "Hello World!"
def background_view(request):
hello(repeat=10)
return HttpResponse("Hello world !")
And this is your call in the urls.py:
from django.conf.urls import url
from django.contrib import admin
from django.conf.urls import include
from background_app.views import hello
urlpatterns = [
url(r'^admin/', admin.site.urls),
url( r'^', include('background_app.urls',namespace='background')),
]
hello(repeat=10,repeat_until=None) # This is how you call it
Doc:
https://django-background-tasks.readthedocs.io/en/latest/
Tutorial with small example:
https://medium.com/#robinttt333/running-background-tasks-in-django-f4c1d3f6f06e

Using django-stripe's built-in webhook support

I'm using Django 3.0, dj-stripe 2.0, and the Stripe CLI. dj-stripe provides native support for Stripe webhooks, and their documentation says to include the following in my django project's main urls.py file to expose the webhook endpoint:
url(r"^stripe/", include("djstripe.urls", namespace="djstripe")),
I have done this, but how do I now leverage the endpoint?
As a separate experiment, I created a payments app, setup the URL conf, and successfully called the view when triggering the non dj-stripe webhook endpoint via the Stripe CLI. But it doesn't use any dj-stripe functionality:
# project urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('stripe/', include("djstripe.urls", namespace="djstripe")),
path('payments/', include("payments.urls", namespace="payments")),
]
# payments/urls.py
from django.urls import path
from . import views
app_name="payments"
urlpatterns = [
path("", views.my_handler, name="my-handler"),
]
# payments/views.py
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def my_handler(request, **kwargs):
print(request.body)
return HttpResponse(status=200)
Running stripe listen --forward-to localhost:8000/payments/ and, in a separate window, stripe trigger product.created returns a 200 response.
But stripe listen --forward-to localhost:8000/stripe/webhook/ and stripe trigger product.created returns a 500.
Thank you in advance for your help.
[UPDATE]
I have not modified the default DJSTRIPE_WEBHOOK_URL or DJSTRIPE_WEBHOOK_VALIDATION settings. In settings.py I have:
STRIPE_LIVE_PUBLIC_KEY = os.environ.get("STRIPE_LIVE_PUBLIC_KEY")
STRIPE_LIVE_SECRET_KEY = os.environ.get("STRIPE_LIVE_SECRET_KEY")
STRIPE_TEST_PUBLIC_KEY = os.environ.get("STRIPE_TEST_PUBLIC_KEY")
STRIPE_TEST_SECRET_KEY = os.environ.get("STRIPE_TEST_SECRET_KEY")
STRIPE_LIVE_MODE = os.environ.get("STRIPE_LIVE_MODE")
DJSTRIPE_WEBHOOK_SECRET = "*************************************"
The test keys are pulled from env/bin/activate:
export STRIPE_LIVE_PUBLIC_KEY="pk_live_****...."
export STRIPE_LIVE_SECRET_KEY="sk_live_****...."
export STRIPE_TEST_PUBLIC_KEY="pk_test_****...."
export STRIPE_TEST_SECRET_KEY="sk_test_****...."
export STRIPE_LIVE_MODE="False"
When I run stripe listen --forward-to localhost:8000/stripe/webhook/ and trigger stripe trigger customer.created I get the following 500 error:
stripe.error.InvalidRequestError: Request req_NTtzsTFS8uVdfL: No such customer; a similar object exists in test mode, but a live mode key was used to make this request.
But I don't understand how my keys could be getting mixed up, because the webhooks work fine when I trigger the same event and listen via the /payments/ endpoint.
Thank you again for your time.
In summary, you need to decorate a handler function using the #webhooks.handler decorator.
You have some examples in the webhooks section of the dj-stripe documentation.

django - urlpatterns giving 404 error for defined URLs

I have an application running on django. But I am getting error code 404 for some urls even though these are defined.
from .views import check_secret_key # a function in views.py
from .swagger_schema import SwaggerSchemaView # a class inheriting APIView
from .kom.kom_writer import kom_status # a function
from django.conf.urls import url
urlpatterns = [
url(r'^docs/api-docs/', SwaggerSchemaView.as_view()),
url(r'^nag/secret-key/', check_secret_key),
url(r'^nag/kom-status/', kom_status),
]
API curl http://localhost:9999/internal/docs/api-docs/ works fine but curl http://localhost:9999/internal/nag/kom-status/ and nag/secret-key fir 404 errror.
Not FoundThe requested resource was not found on this server.
I am not sure what is it that I am missing.
Note: App was recently updated from DJango 1.8 to 1.11 and djangorestframework 3.2.5 to 3.5.3. Before that it was working fine.
For debugging purpose I am just returning success response right now.
from django.http import HttpResponse
def check_secret_key(request):
r = HttpResponse("I am good", content_type='text/plain', status=200)
return r
well, your definition should be something like
urlpatterns = [
url(r'.*docs/api-docs/', SwaggerSchemaView.as_view()),
url(r'.*nag/secret-key/', check_secret_key),
url(r'.*nag/kom-status/', kom_status),
]
also you need to provide /internal url definition.
I found the issue. Application is using two wsgi.py for running django apps.
Both wsgi.py files were using
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "it.settings.prod")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "nt.settings.prod")
Changing it to
os.environ["DJANGO_SETTINGS_MODULE"] = "it.settings.prod"
os.environ["DJANGO_SETTINGS_MODULE"] = "nt.settings.prod"
resolved the problem.
Took reference from https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/modwsgi/

Testing Django allauth

I am trying to test my app but not sure how to configure the django-allauth in the test environment. I am getting:
ImproperlyConfigured: No Facebook app configured: please add a SocialApp using the Django admin
My approach so far is to instantiate app objects inside tests.py with actual Facebook app parameters, an app which functions correctly locally in the browser:
from allauth.socialaccount.models import SocialApp
apper = SocialApp.objects.create(provider=u'facebook',
name=u'fb1', client_id=u'7874132722290502',
secret=u'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
apper.sites.create(domain='localhost:8000', name='creyu.org')
How can I get these tests running? Thanks
Where inside tests.py do you instantiate this app object? If it's inside the setUpModule() method, there shouldn't be a problem.
Personally, I would create a fixture init_facebook_app.json with the relevant information and then inside tests.py (before the test cases) define:
from django.core.management import call_command
def setUpModule():
call_command('loaddata', 'init_facebook_app.json', verbosity=0)
This ensures that the data in the fixture are loaded before the tests are run, and that they are loaded only once, i.e. not before each test. See this for reference regarding call_command.
Lastly, posting your Facebook app secret key anywhere on the internet is not a good idea - I would reset it if I were you.
I would create a migration so all your environments have the data
eg
import os
from django.db import models, migrations
from django.core.management import call_command
from django.conf import settings
class Migration(migrations.Migration):
def add_initial_providers(apps, schema_editor):
import pdb;pdb.set_trace()
call_command(
'loaddata',
os.path.join(settings.BASE_DIR, 'fixtures/social_auth.json'),
verbosity=0)
dependencies = [
('my_app', '001_auto_20160128_1846'),
]
operations = [
migrations.RunPython(add_initial_providers),
]

Redirect all requests for www. to root domain

I need to redirect all requests coming from www.mysite.com to mysite.com
I have found the solution in rails, but how can I do that in Django/Python?
The only solution I could find, which was posted by a moderator on GoDaddy was the above. Seems like I cannot resolve this kind of problem through the DNS Manager of GoDaddy.
Create your own middleware in [PROJECT_NAME]/middleware.py, like so:
from django.conf import settings
from django.http import HttpResponsePermanentRedirect
from django.utils.deprecation import MiddlewareMixin
class RemoveWWWMiddleware(MiddlewareMixin):
"""
Based on the REMOVE_WWW setting, this middleware removes "www." from the
start of any URLs.
"""
def process_request(self, request):
host = request.get_host()
if settings.REMOVE_WWW and host and host.startswith('www.'):
redirect_url = '%s://%s%s' % (
request.scheme, host[4:], request.get_full_path()
)
return HttpResponsePermanentRedirect(redirect_url)
Then, in your project's settings.py:
Add REMOVE_WWW = True
And add [PROJECT_NAME].middleware.RemoveWWWMiddleware to the MIDDLEWARE list, after Django's SecurityMiddleware and preferably before Django's Common Middleware.
Also, of course, make sure you haven't set PREPEND_WWW = True
This middleware is based on Django's CommonMiddleware.
Solved with this:
from django.http import HttpResponsePermanentRedirect
class WWWRedirectMiddleware(object):
def process_request(self, request):
if request.META['HTTP_HOST'].startswith('www.'):
return HttpResponsePermanentRedirect('http://example.com')

Categories