Django - how to enable foreign keys in SqLite3 backend - django

When trying to use SqlLite3 as the DB backend to Django, I'd like to be able to use SqLite's Foreign Key support.
According to http://www.sqlite.org/foreignkeys.html, you can enable foreign key support by running:
PRAGMA foreign_keys = ON;
This support is disabled by default, and would be desirable while running most db dependent tests.
How would one control this feature while using Django test framework? Can you send backend-specific commands in your setUp/tearDown methods? Would you rather specify this option in connection settings ('DATABASE = ' settings) globally?

It's partially answered here: https://stackoverflow.com/a/6843142/552671.
You can just activate it in your app (typically in myapp/apps.py).
from django.db.backends.signals import connection_created
def activate_foreign_keys(sender, connection, **kwargs):
"""Enable integrity constraint with sqlite."""
if connection.vendor == 'sqlite':
cursor = connection.cursor()
cursor.execute('PRAGMA foreign_keys = ON;')
class MyAppConfig(AppConfig):
def ready(self):
connection_created.connect(activate_foreign_keys)
You have to configure MyAppConfig as well, write default_app_config = 'myapp.apps.PanelConfig' to myapp/__init__.py.
Note: This will activate foreign keys not only for tests but also SQLite in general. I think that is what you want, though.

In current django it is not needed anymore. Closed in https://code.djangoproject.com/ticket/14204
And from 2.2 it is also on during unit testing :)

Related

Django applications sharing databases on Heroku: set 'shared' database once for all

I have several Django applications and they need to share one database on Heroku. I may specify the shared database on each statement that need to access it, for example:
from account.models import User
if DEBUG: # Running locally
users = User.objects.all() # 'default' DB
else: # Running on Heroku
users = User.objects.using('shared').all() # 'shared' DB
I have two questions:
1) Specifying the shared database on every statement is really tedious. Is it possible to set the shared database once for all (maybe in setting.py)? For example:
from account.models import User
if not DEBUG: # Running on Heroku
User = User.objects.using('share') # This is hypothetical!!
users = User.objects.all()
2) How do I set the shared DB for a foreign key. For example:
from account.models import User
class Article(models.Model):
author = models.ForeignKey(User) # How to set 'User' to come from 'shared' DB?
Django database routers have what you are looking for
https://docs.djangoproject.com/en/2.0/topics/db/multi-db/#using-routers\
Relations do not work cross database
https://docs.djangoproject.com/en/dev//topics/db/multi-db/#limitations-of-multiple-databases
I recommend trying to keep everything in 1 database initially. Usually when starting out there isn't going to be a need to separate it out.

Run Django tests against an existing database with custom testrunner

I've got an application that is running Django 2 that connects to a Microsoft SQL Server backend.
We're in a big environment where things are pretty tightly controlled - a user account has access to the database that houses all the tables for my django application.
So I have found through various posts that I need to create a testrunner for my application - which is fine. I can override the setup_databases function - but I'm not sure exactly how to do that.
My TestRunner(DiscoverRunner) looks like this:
class ExistingDbTestRunner(DiscoverRunner):
""" A test runner to test without database creation """
def setup_databases(self, **kwargs):
""" Override the database creation defined in parent class """
# force Django to connect to the correct db for tests
connection = self.connections['default']
db_conf = connection.settings_dict
connection.connect()
def teardown_databases(self, old_config, **kwargs):
""" Override the database teardown defined in parent class """
pass
But this fails with AttributeError: 'ExistingDbTestRunner' object has no attribute 'connections'
I'm just trying to get this to use the 'default' database that I have set in the settings for testing purposes.
It's worth noting - the default database specified in settings is a duplicate copy of the production database with a different name.
So I just want my tests to run against this duplicate database. What should I be changing so it connects?
Django tests do not operate on existing database or are meant to be used that way. Django always creates a new database, with the name test_db_name_specified_in_settings for all its tests.
More documentation can be found here: https://docs.djangoproject.com/en/2.0/topics/testing/overview/#the-test-database

How to (intentionally) skip an app with Django syncdb

I have several django applications:
INSTALLED_APPS = (
'geonode.exposure',
'geonode.isc_viewer',
'geonode.geodetic',
'geonode.observations',
'geonode.ged4gem',
I need to manage all of them except one with syncdb.
How can I get syncdb to intentionally skip the geonode.exposure application?
Update:
I did not describe the full configuration, please allow me to go into more detail:
I am using south to manage db migrations and fixtures for all the apps except exposure.
The exposure app is accessing an external database and is using a router to do so (this is why I want it to be skipped by syncdb).
My router settings look like this:
class GedRouter(object):
def db_for_read(self, model, **hints):
"Point all operations on ged models to 'geddb'"
if model._meta.app_label == 'exposure':
return 'geddb'
return 'default'
def allow_syncdb(self, db, model):
if db == 'geddb' or model._meta.app_label == "ged":
return False # we're not using syncdb on our legacy database
else: # but all other models/databases are fine
return True
Is south not respecting the allow_syncdb method? is south running syncbd on the exposure app because I do not have a migration for it?
You can use managed = False in the model's Meta class. This way, syncdb won't create the app's tables. More information on the documentation.
There is a model meta option "managed", for more info check django documentation:
https://docs.djangoproject.com/en/dev/ref/models/options/#managed
Ok, this is not what your asking directly, but please consider using South http://south.aeracode.org
You can decided which apps to include which version of the model to migrate etc. Sounds like you need a solution here.

How do I obtain a cursor for a routed database?

django.db.connection.cursor() turns out to return a connection to default database, not a routed one.
How do I create a cursor to a database selected for my Django application by project configuration?
django.db provides a dictionary of all connections, according to the keys you use in your dictionary within the settings module:
from django.db import connections
cursor = connections['my_db_alias'].cursor()
This is documented here.
Ok, I've examined django.db.models.Model class and found out that routed connection if obtained through router each time:
from django.db import connections, router
using = using or router.db_for_write(self.__class__, instance=self)
connection = connections[using]
Unfortunately, router takes Model subclass as first argument of its db_for_write() method.

Language stored in Useprofile.language field applied on user log in

I need to set language for user in 2 cases:
User logs in (django checks his UserProfile field for language field value and sets the proper lang)
Users chenges lang in "user preferences" page.
I've tried with "user_logged_in" signal receiver
from django.utils import translation
from django.dispatch import receiver
from django.contrib.auth.signals import user_logged_in
#receiver(user_logged_in)
def setlang(sender, **kwargs):
translation.activate(kwargs['user'].get_profile().language)
kwargs['request'].session['django_language'] = translation.get_language()
This works fine until I restart my django instance. Though session is alive (no need to log in again) website is being displayed in language specified in settings.LANGUAGE
I think the same applies to situation #2 (on user preferences form save())
Following up to Timmy's answer you can find an already published middleware that does the job for you!
The translation of a Django app with the preferred language selected by a registered user can be done with the middleware django-user-language-middleware. This allows easy translation of your Django app by looking at the selected language in the user.language field.
Usage:
Add a language field to your user model:
class User(auth_base.AbstractBaseUser, auth.PermissionsMixin):
# ...
language = models.CharField(max_length=10,
choices=settings.LANGUAGES,
default=settings.LANGUAGE_CODE)
Install the middleware from pip:
pip install django-user-language-middleware
Add it to your middleware class list in settings to listen to requests:
MIDDLEWARE = [ # Or MIDDLEWARE_CLASSES on Django < 1.10
...
'user_language_middleware.UserLanguageMiddleware',
...
]
I hope this may help people landing on this question in the future.
I think it's better if you use middleware to achieve this. Django provides a way to add in hooks at various places throughout a request so that you can add custom code.
In your case: check on every request if there is a language variable in the session, if not, fetch the users language preference from the database and save it to the session variable. Check on a save signal whether or not the user has changed their preference; if so, update the session variable (you might have problems getting access to the request from the model level so it might be better to do this in the view).
I've used this snippet, but it conflicts with the Regular LocaleMiddleware, and thus I can't add a multilingual content farm.
http://django-hotclub.googlecode.com/svn-history/r708/trunk/pinax/profiles/middleware.py