django with multiple databases and foreignkeys for User - python

Suppose I have a django app on my server, but I wish to do authentication using django.contrib.auth.models where the User and Group models/data are on another server in another database. In Django, my DATABASES setting would be something like this:
DATABASES = {
'default': {},
'auth_db': {
'NAME' : 'my_auth_db',
'ENGINE' : 'django.db.backends.mysql',
'USER' : 'someuser',
'PASSWORD' : 'somepassword',
'HOST' : 'some.host.com',
'PORT' : '3306',
},
'myapp': {
'NAME': 'myapp_db',
'ENGINE': 'django.db.backends.mysql',
'USER': 'localuser',
'PASSWORD': 'localpass',
}
}
DATABASE_ROUTERS = ['pathto.dbrouters.AuthRouter', 'pathto.dbrouters.MyAppRouter']
First question: will this work, ie will it allow me to login to my Django app using users that are stored in the remote DB 'my_auth_db'?
Assuming the answer to the above is yes, what happens if in my local DB (app 'myapp') I have models that have a ForeignKey to User? In other words, my model SomeModel is defined in myapp and should exist in the myapp_db, but it have a ForeignKey to a User in my_auth_db:
class SomeModel(models.model):
user = models.ForeignKey(User, unique=False, null=False)
description = models.CharField(max_length=255, null=True)
dummy = models.CharField(max_length=32, null=True)
etc.
Second question: Is this possible or is it simply not possible for one DB table to have a ForeignKey to a table in another DB?
If I really wanted to make this work, could I replace the ForeignKey field 'user' with an IntegerField 'user_id' and then if I needed somemodel.user I would instead get somemodel.user_id and use models.User.objects.get(pk=somemodel.user_id), where the router knows to query auth_db for the User? Is this a viable approach?

The answer to question 1 is: Yes.
What you will need in any case is a database router (The example in the Django docs is exactly about the auth app, so there's no need to copy this code here).
The answer to question 2 is: Maybe. Not officially. It depends on how you have set up MySQL:
https://docs.djangoproject.com/en/dev/topics/db/multi-db/#limitations-of-multiple-databases
Django doesn’t currently provide any support for foreign key or many-to-many relationships spanning multiple databases.
This is because of referential integrity.
However, if you’re using SQLite or MySQL with MyISAM tables, there is no enforced referential integrity; as a result, you may be able to ‘fake’ cross database foreign keys. However, this configuration is not officially supported by Django.
I have a setup with several legacy MySQL DBs (readonly). This answer shows How to use django models with foreign keys in different DBs?
I later ran into troubles with Django ManyToMany through with multiple databases and the solution (as stated in the accepted answer there) is to set the table name with quotes:
class Meta:
db_table = '`%s`.`table2`' % db2_name
Related questions that might provide some additional information:
How to work around lack of support for foreign keys across databases in Django
How to use django models with foreign keys in different DBs?
It would be nice if somebody would take all this information and put in into the official Django doc :-)

Related

I can connect an application to 2 databases in Django?

I have a web application in Python django. I need to import users and display data about them from another database, from another existing application. All I need is the user to be able to login and display information about them. What solutions are?
You can set 2 DATABASES in settings.py.
DATABASES = {
'default': {
...
},
'user_data': {
...
}
}
Then in one database store User models with authentication and stuff, in another rest information. You can connect information about specific User with a field that is storing id of User from another database.
If you have multiple databases and create a model, you should declare on which db it is going to be stored. If you didn't, it will be in default one (if you have it declared).
class UserModel(models.Model):
class Meta:
db_table = 'default'
class UserDataModel(models.Model):
class Meta:
db_table = 'user_data'
the answer from #NixonSparrow was wrong.
_meta.db_table defined only table_name in database and not the database self.
for switch database you can use manager.using('database_name'), for every model, it is good declared here: https://docs.djangoproject.com/en/4.0/topics/db/multi-db/#topics-db-multi-db-routing
in my project i use multiple router.
https://docs.djangoproject.com/en/4.0/topics/db/multi-db/#topics-db-multi-db-routing
it help don't override every manager with using. But in your case:
DATABASES = {
'default': {
...
},
'other_users_data': {
...
}
}
and somethere in views:
other_users = otherUserModel.objects.using('other_users_data')
Probably, otherUserModel should define in meta, which table you want to use db_table = 'other_users_table_name' and also probably it should have managed=False, to hide this model from migration manager.

How to connect to existing oracle db table in Django

Actually, i am developing UI for my team for daily Data visualization using Django, but from last two week i have searched a lot in google, i didn't get solution, even i have followed Django Documentation for connecting Oracle DB.
Already i have date in my Metrics table, i don't want create a table again. can anyone help me to connect existing Oracle DB OtherUser table in Django and how to write in model.py.
i have installed cx_Oracle library also.
i have tried inspectin DB its show as below.
from django.db import models
Unable to inspect table 'METRICS'
The error was: ORA-00942: table or view does not exist
# setting.py
'default': {
'ENGINE': 'django.db.backends.oracle',
'NAME': 'servicename',
'USER': 'mysuer',
'PASSWORD': 'mypassword',
'SCHEMAS': 'SCHEMANAME',
}
# models.py
class Metrics(models.Model):
class Meta:
db_table = '"SCHEMANAME"."METRICS"'
verbose_name = 'Metrics'
verbose_name_plural = 'Metrics'
i have test in single test.py file i am able to get the data using cursor in cx_oracle, but how i can do it in django?

Refresh mongodb collection structure through python mongoengine

I'm writing a simple Flask app, with the sole purpose to learn Python and MongoDB.
I've managed to reach to the point where all the collections are defined, and CRUD operations work in general. Now, one thing that I really want to understand, is how to refresh the collection, after updating its structure. For example, say that I have the following model:
user.py
class User(db.Document, UserMixin):
email = db.StringField(required=True, unique=True)
password = db.StringField(required=True)
active = db.BooleanField()
first_name = db.StringField(max_length=64, required=True)
last_name = db.StringField(max_length=64, required=True)
registered_at = db.DateTimeField(default=datetime.datetime.utcnow())
confirmed = db.BooleanField()
confirmed_at = db.DateTimeField()
last_login_at = db.DateTimeField()
current_login_at = db.DateTimeField()
last_login_ip = db.StringField(max_length=45)
current_login_ip = db.StringField(max_length=45)
login_count = db.IntField()
companies = db.ListField(db.ReferenceField('Company'), default=[])
roles = db.ListField(db.ReferenceField(Role), default=[])
meta = {
'indexes': [
{'fields': ['email'], 'unique': True}
]
}
Now, I already have entries in my user collection, but I want to change companies to:
company = db.ReferenceField('Company')
How can I refresh the collection's structure, without having to bring the whole database down?
I do have a manage.py script that helps me and also provides a shell:
#!/usr/bin/python
from flask.ext.script import Manager
from flask.ext.script.commands import Shell
from app import factory
app = factory.create_app()
manager = Manager(app)
manager.add_command("shell", Shell(use_ipython=True))
# manager.add_command('run_tests', RunTests())
if __name__ == "__main__":
manager.run()
and I have tried a couple of commands, from information that I could recompile and out of my basic knowledge:
>>> from app.models import db, User
>>> import mongoengine
>>> mongoengine.Document(User)
field = iter(self._fields_ordered)
AttributeError: 'Document' object has no attribute '_fields_ordered'
>>> mongoengine.Document(User).modify() # well, same result as above
Any pointers on how to achieve this?
Update
I am asking all of this, because I have updated my user.py to match my new requests, but anytime I interact with the db its self, since the table's structure was not refreshed, I get the following error:
FieldDoesNotExist: The field 'companies' does not exist on the
document 'User', referer: http://local.faqcolab.com/company
Solution is easier then I expected:
db.getCollection('user').update(
// query
{},
// update
{
$rename: {
'companies': 'company'
}
},
// options
{
"multi" : true, // update all documents
"upsert" : false // insert a new document, if no existing document match the query
}
);
Explanation for each of the {}:
First is empty because I want to update all documents in user collection.
Second contains $rename which is the invoking action to rename the fields I want.
Last contains aditional settings for the query to be executed.
I have updated my user.py to match my new requests, but anytime I interact with the db its self, since the table's structure was not refreshed, I get the following error
MongoDB does not have a "table structure" like relational databases do. After a document has been inserted, you can't change it's schema by changing the document model.
I don't want to sound like I'm telling you that the answer is to use different tools, but seeing things like db.ListField(db.ReferenceField('Company')) makes me think you'd be much better off with a relational database (Postgres is well supported in the Flask ecosystem).
Mongo works best for storing schema-less documents (you don't know before hand how your data is structured, or it varies significantly between documents). Unless you have data like that, it's worth looking at other options. Especially since you're just getting started with Python and Flask, there's no point in making things harder than they are.

Running Django unittests causes South migrations to duplicate tables

How do you prevent Django unittests from running South migrations?
I have a custom Django app, myapp, that I'm trying to test with manage.py test myapp but when I run it I get the error:
django.db.utils.OperationalError: table "myapp_mymodel" already exists
and sure enough, the traceback shows that South is being executed:
File "/usr/local/myproject/.env/local/lib/python2.7/site-packages/south/management/commands/test.py", line 8, in handle
super(Command, self).handle(*args, **kwargs)
However, in my settings, I've specified:
SOUTH_TESTS_MIGRATE = 0
SKIP_SOUTH_TESTS = 1
which I believe should prevent Django's test framework from executing any South components.
What am I doing wrong?
Edit: I worked around this by simply removing south with:
if 'test' in sys.argv:
INSTALLED_APPS.remove('south')
However, then I got:
ImproperlyConfigured: settings.DATABASES is improperly configured. Please supply the NAME value.
For my test database settings, I was using:
DATABASES = {
'default':{
'ENGINE': 'django.db.backends.sqlite3'
}
}
which worked fine in Django 1.4. Now I'm using Django 1.5, and I guess that's not kosher. However, no NAME value I see it to fixes it. They all report none of my tables exist. I've tried:
DATABASES = {
'default':{
'ENGINE': 'django.db.backends.sqlite3',
'NAME': '/dev/shm/test.db',
'TEST_NAME': '/dev/shm/test.db',
}
}
DATABASES = {
'default':{
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
'TEST_NAME': ':memory:',
}
}
DATABASES = {
'default':{
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(os.path.dirname(__file__), 'test.db'),
'TEST_NAME': os.path.join(os.path.dirname(__file__), 'test.db'),
}
}
each seems to create a physical test.db file, which I don't understand, because unittests should be run in-memory. It should never save anything to disk. Presumably, it's failing to run syncdb after creating the file but before it executes the actual unittest. How do I fix this?
Edit: I discovered that, in one of my forms, I was populating field choices by directly querying a model (whereas I should have been doing that inside the form's init), so when Django's test framework imported my model, it was trying to read the table before the sqlite3 database had been created. I fixed that, but now I'm getting the error:
DatabaseError: table "myapp_mythroughmodel" already exists
so I'm back to square one, even though it's throwing a different exception type than initially.
Edit: I had a duplicate through model defined, causing Django to attempt to create it twice, resulting in the error.
This also happened to me with a legacy code but for another reason.
I had two models with db_table referencing the same db table.
I know that is stupid, but it's not my fault )
And I never found anything on the internet that could help me.
I was saved by verbosity set to 3 (manage.py test -v 3)
Hope this helps anyone.
class Bla1(Model):
some_column = ...
class Meta:
db_table = 'some_table'
class Bla2(Model):
some_column = ...
class Meta:
db_table = 'some_table'
This error was the result of several problems. I'll summarize them here to help others who may have stumbled across this.
Ensure your settings.DATABASES is set correctly. Django's docs mention using TEST_NAME, but for clarity, I find it easier to check for the test command and override everything. e.g. at the bottom of my settings.py, I have:
if 'test' in sys.argv:
DATABASES = {
'default':{
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
},
}
Unless you have a good reason, always use :memory: to ensure it runs in memory and doesn't create a physical file that will be bogged down on disk. For some odd reason, a lot of other answers on SO recommend specifying a literal path to a test.db file for testing. This is horrible advice.
Unless you want to test South and/or your South migrations, disable South, because it'll only complicate things:
SOUTH_TESTS_MIGRATE = False
SKIP_SOUTH_TESTS = True
Don't be dumb like me and try to access your models before they're created. This mostly means don't directly refer to models from the fields of other models or forms. e.g.
class MyForm(forms.Form):
somefield = forms.ChoiceField(
required=True,
choices=[(_.id, _.name) for _ in OtherModel.objects.filter(criteria=blah)],
)
This might work in code where your database already exists, but it'll break Django's unittest framework when it tries to load your tests, which load your models.py and forms.py, causing it to read a table that doesn't exist. Instead, set the choices value in the form's __init__().

Django_tagging (v0.3/pre): Configuration issue

I am trying to use the django-tagging in one of my project and run into some errors.
I can play with tags in the shell but couldn't assign them from admin interface.
What I want to do is add "tag" functionality to a model and add/remove tags from Admin interface.
Why is it the "tags" are seen by shell and not by "admin" interface? What is going on?
Model.py:
import tagging
class Department(models.Model):
tags = TagField()
Admin.py:
class DepartmentAdmin(admin.ModelAdmin):
list_display = ('name', 'tags') --> works
....
fields = ['name', 'tags'] --> throws error
Error
OperationalError at /admin/department/1/
(1054, "Unknown column 'schools_department.tags' in 'field list'")
I looked at the docs and couldn't find further information
Useful Tips
Overview Txt
The TagField requires an actual database column on your model; it uses this to cache the tags as entered. If you add a TagField to a model that already has a database table, you will need to add the column to the database table, just as with adding any other type of field. Either use a schema migration tool (like South or django-evolution) or run the appropriate SQL ALTER TABLE command manually.

Categories